Repository: numba/numba

Issue:
Unable to pass single record/row of a numpy recarray into a jit'd function
Passing a single row/record of a numpy recarray into a jit'd function causes an error (Numba 0.11.1):

``` python
# test.py
import numba
import numpy as np
dt = [('a', numba.double), ('b', numba.i8)]

struct_type = numba.struct(dt)

@numba.jit(numba.double(struct_type))
def test(b):
    return b.a

a = np.recarray(3, struct_type.get_dtype())
a.a = 10.0
a.b = 1

d = test(a[0])
print d
```

results in the error:

``` bash
$ python test.py
Assertion failed: (getOperand(0)->getType() == cast<PointerType>(getOperand(1)->getType())->getElementType() && "Ptr must be a pointer to Val type!"), function AssertOK, file Instructions.cpp, line 1084.
Illegal instruction: 4
```



Discussion:

Resolving patch:
diff --git a/numba/dispatcher.py b/numba/dispatcher.py
--- a/numba/dispatcher.py
+++ b/numba/dispatcher.py
@@ -169,11 +169,14 @@ def typeof_pyval(cls, val):
             ndim = val.ndim
             if ndim == 0:
                 # is array scalar
-                return numpy_support.from_dtype(val.dtype)
+                return dtype
             layout = numpy_support.map_layout(val)
             aryty = types.Array(dtype, ndim, layout)
             return aryty
 
+        elif isinstance(val, numpy.record):
+            return numpy_support.from_dtype(val.dtype)
+
         # The following are handled in the C version for exact type match
         # So test these later
         elif isinstance(val, INT_TYPES):


Test patch:
diff --git a/numba/tests/test_dispatcher.py b/numba/tests/test_dispatcher.py
--- a/numba/tests/test_dispatcher.py
+++ b/numba/tests/test_dispatcher.py
@@ -10,13 +10,13 @@ def dummy(x):
 
 
 class TestDispatcher(unittest.TestCase):
+
     def test_typeof(self):
         self.assertEqual(typeof(numpy.int8(1)), types.int8)
         self.assertEqual(typeof(numpy.uint16(1)), types.uint16)
         self.assertEqual(typeof(numpy.float64(1)), types.float64)
         self.assertEqual(typeof(numpy.complex128(1)), types.complex128)
 
-
     def test_numba_interface(self):
         """
         Check that vectorize can accept a decorated object.
diff --git a/numba/tests/test_record_dtype.py b/numba/tests/test_record_dtype.py
--- a/numba/tests/test_record_dtype.py
+++ b/numba/tests/test_record_dtype.py
@@ -3,7 +3,7 @@
 import sys
 
 import numpy as np
-from numba import numpy_support, types
+from numba import jit, numpy_support, types
 from numba import unittest_support as unittest
 from numba.compiler import compile_isolated
 from numba.utils import IS_PY3
@@ -66,6 +66,7 @@ def record_return(ary, i):
 
 
 class TestRecordDtype(unittest.TestCase):
+
     def setUp(self):
         ary = np.recarray(3, dtype=recordtype)
         self.sample1d = ary
@@ -77,6 +78,12 @@ def setUp(self):
             ary[i].c = x * 1j
             ary[i].d = "%d" % x
 
+    def get_cfunc(self, pyfunc, argspec):
+        # Need to keep a reference to the compile result for the
+        # wrapper function object to remain valid (!)
+        self.__cres = compile_isolated(pyfunc, argspec)
+        return self.__cres.entry_point
+
     def test_from_dtype(self):
         rec = numpy_support.from_dtype(recordtype)
         self.assertEqual(rec.typeof('a'), types.float64)
@@ -94,8 +101,7 @@ def test_from_dtype(self):
 
     def _test_get_equal(self, pyfunc):
         rec = numpy_support.from_dtype(recordtype)
-        cres = compile_isolated(pyfunc, (rec[:], types.intp))
-        cfunc = cres.entry_point
+        cfunc = self.get_cfunc(pyfunc, (rec[:], types.intp))
         for i in range(self.sample1d.size):
             self.assertEqual(pyfunc(self.sample1d, i), cfunc(self.sample1d, i))
 
@@ -110,8 +116,7 @@ def test_get_c(self):
 
     def _test_set_equal(self, pyfunc, value, valuetype):
         rec = numpy_support.from_dtype(recordtype)
-        cres = compile_isolated(pyfunc, (rec[:], types.intp, valuetype))
-        cfunc = cres.entry_point
+        cfunc = self.get_cfunc(pyfunc, (rec[:], types.intp, valuetype))
 
         for i in range(self.sample1d.size):
             expect = self.sample1d.copy()
@@ -135,8 +140,7 @@ def test_set_c(self):
     def test_set_record(self):
         pyfunc = set_record
         rec = numpy_support.from_dtype(recordtype)
-        cres = compile_isolated(pyfunc, (rec[:], types.intp, types.intp))
-        cfunc = cres.entry_point
+        cfunc = self.get_cfunc(pyfunc, (rec[:], types.intp, types.intp))
 
         test_indices = [(0, 1), (1, 2), (0, 2)]
         for i, j in test_indices:
@@ -166,8 +170,7 @@ def test_record_args(self):
 
             pyfunc = globals()['get_record_' + attr]
             nbrecord = numpy_support.from_dtype(recordtype)
-            cres = compile_isolated(pyfunc, [nbrecord, valtyp])
-            cfunc = cres.entry_point
+            cfunc = self.get_cfunc(pyfunc, (nbrecord, valtyp))
 
             got = cfunc(recval, val)
             self.assertEqual(expected, got)
@@ -184,8 +187,7 @@ def test_record_return(self):
         """
         pyfunc = record_return
         recty = numpy_support.from_dtype(recordtype)
-        cres = compile_isolated(pyfunc, [recty[:], types.intp])
-        cfunc = cres.entry_point
+        cfunc = self.get_cfunc(pyfunc, (recty[:], types.intp))
 
         attrs = 'abc'
         indices = [0, 1, 2]
@@ -202,6 +204,15 @@ def test_record_return(self):
             self.assertEqual(sys.getrefcount(ary), old_refcnt)
 
 
+class TestRecordDtypeWithDispatcher(TestRecordDtype):
+    """
+    Same as TestRecordDtype, but stressing the Dispatcher's type dispatch
+    mechanism (issue #384).
+    """
+
+    def get_cfunc(self, pyfunc, argspec):
+        return jit(argspec, nopython=True)(pyfunc)
+
 
 if __name__ == '__main__':
     unittest.main()