Repository: numba/numba

Issue:
Segmentation Fault while trying to do in place np.sin

The following code produces a segmentation fault:

```In [1]: import numpy as np                                                                                                                                                                    
In [2]: import numba                                                                                                                                                                          
In [3]: out = np.ones(1024)                                                                                                                                                                   
In [4]: @numba.jit(nopython=True, fastmath=True)  
   ...: def test():  
   ...:     np.sin(np.ones(1024), out)  
   ...:                                                                                                                                                                                       
In [5]: test()                                                                                                                                                                                
[1]    20055 segmentation fault (core dumped)  ipython
```

Using numba 0.41.0 (latest)
chaging the function to:
```
@numba.jit(nopython=False, fastmath=True)  
def test():  
    np.sin(np.ones(1024), out)  
```

eliminates the issue. (but fails to be nopython!)


Discussion:
Thanks for the report. This is a bug, the global `out` should be frozen as a readonly array at compile time and writing to it should have been caught as invalid behaviour, as happens in this example:
```python
import numpy as np
import numba

out = np.zeros(1024)

@numba.jit(nopython=True, fastmath=True)
def test():
    out[0] = 1

test()
```
which shows:
```
numba.errors.InternalError: Cannot modify value of type readonly array(float64, 1d, C)
[1] During: typing of staticsetitem at issue3603.py (8)
--%<----------------------------------------------------------------------------


File "issue3603.py", line 8:
def test():
    <source elided>
    out[0] = 1
```

Resolving patch:
diff --git a/numba/typing/npydecl.py b/numba/typing/npydecl.py
--- a/numba/typing/npydecl.py
+++ b/numba/typing/npydecl.py
@@ -73,6 +73,10 @@ def _handle_inputs(cls, ufunc, args, kws):
             msg = "ufunc '{0}' called with an explicit output that is not an array"
             raise TypingError(msg=msg.format(ufunc.__name__))
 
+        if not all(output.mutable for output in explicit_outputs):
+            msg = "ufunc '{0}' called with an explicit output that is read-only"
+            raise TypingError(msg=msg.format(ufunc.__name__))
+
         # find the kernel to use, based only in the input types (as does NumPy)
         base_types = [x.dtype if isinstance(x, types.ArrayCompatible) else x
                       for x in args]


Test patch:
diff --git a/numba/tests/npyufunc/test_ufunc.py b/numba/tests/npyufunc/test_ufunc.py
--- a/numba/tests/npyufunc/test_ufunc.py
+++ b/numba/tests/npyufunc/test_ufunc.py
@@ -3,8 +3,9 @@
 import numpy as np
 
 from numba import unittest_support as unittest
-from numba import float32
+from numba import float32, jit
 from numba.npyufunc import Vectorize
+from numba.errors import TypingError
 from ..support import tag, TestCase
 
 
@@ -100,6 +101,26 @@ def test_implicit_broadcasting(self):
             self.assertPreciseEqual(ufunc(a, broadcasting_b),
                                     a + broadcasting_b)
 
+    def test_ufunc_exception_on_write_to_readonly(self):
+        z = np.ones(10)
+        z.flags.writeable = False # flip write bit
+
+        tests = []
+        expect = "ufunc 'sin' called with an explicit output that is read-only"
+        tests.append((jit(nopython=True), TypingError, expect))
+        tests.append((jit(nopython=False), ValueError,
+                      "output array is read-only"))
+
+        for dec, exc, msg in tests:
+            def test(x):
+                a = np.ones(x.shape, x.dtype) # do not copy RO attribute from x
+                np.sin(a, x)
+
+            with self.assertRaises(exc) as raises:
+                dec(test)(z)
+
+            self.assertIn(msg, str(raises.exception))
+
 
 if __name__ == '__main__':
     unittest.main()