Repository: numba/numba

Issue:
Crash when returning a named tuple of lists
This crashes with a segmentation fault:

```
Point = collections.namedtuple('Point', ('x', 'y'))

@jit(nopython=True)
def list_tup(n):
    return Point(list(range(n)), [42])

print(list_tup(3))
```

gdb shows it crashing in `wrapper..main..list_tup$1.int64`. There's probably a NRT incref missing somewhere.



Discussion:

Resolving patch:
diff --git a/numba/targets/tupleobj.py b/numba/targets/tupleobj.py
--- a/numba/targets/tupleobj.py
+++ b/numba/targets/tupleobj.py
@@ -31,7 +31,9 @@ def generic_compare(context, builder, key, argtypes, args):
 @implement(types.NamedTupleClass, types.VarArg(types.Any))
 def namedtuple_constructor(context, builder, sig, args):
     # A namedtuple has the same representation as a regular tuple
-    return context.make_tuple(builder, sig.return_type, args)
+    res = context.make_tuple(builder, sig.return_type, args)
+    # The tuple's contents are borrowed
+    return impl_ret_borrowed(context, builder, sig.return_type, res)
 
 @builtin
 @implement(types.len_type, types.Kind(types.BaseTuple))
@@ -125,7 +127,8 @@ def namedtuple_getattr(context, builder, typ, value, attr):
     Fetch a namedtuple's field.
     """
     index = typ.fields.index(attr)
-    return builder.extract_value(value, index)
+    res = builder.extract_value(value, index)
+    return impl_ret_borrowed(context, builder, typ[index], res)
 
 
 #------------------------------------------------------------------------------


Test patch:
diff --git a/numba/tests/test_tuples.py b/numba/tests/test_tuples.py
--- a/numba/tests/test_tuples.py
+++ b/numba/tests/test_tuples.py
@@ -3,12 +3,12 @@
 import collections
 import itertools
 
-import numpy
+import numpy as np
 
 from numba import unittest_support as unittest
 from numba.compiler import compile_isolated
 from numba import jit, types
-from .support import TestCase
+from .support import TestCase, MemoryLeakMixin
 
 
 Rect = collections.namedtuple('Rect', ('width', 'height'))
@@ -64,13 +64,19 @@ def make_point(a, b, c):
 def make_point_kws(a, b, c):
     return Point(z=c, y=b, x=a)
 
+def make_point_nrt(n):
+    r = Rect(list(range(n)), np.arange(n))
+    # This also exercises attribute access
+    p = Point(r, len(r.width), len(r.height))
+    return p
+
 
 class TestTupleReturn(TestCase):
 
     def test_array_tuple(self):
         aryty = types.Array(types.float64, 1, 'C')
         cres = compile_isolated(tuple_return_usecase, (aryty, aryty))
-        a = b = numpy.arange(5, dtype='float64')
+        a = b = np.arange(5, dtype='float64')
         ra, rb = cres.entry_point(a, b)
         self.assertTrue((ra == a).all())
         self.assertTrue((rb == b).all())
@@ -199,7 +205,7 @@ def test_le(self):
         self._test_compare(le_usecase)
 
 
-class TestNamedTuple(TestCase):
+class TestNamedTuple(TestCase, MemoryLeakMixin):
 
     def test_unpack(self):
         def check(p):
@@ -304,5 +310,19 @@ def check(pyfunc):
         check(make_point_kws)
 
 
+class TestNamedTupleNRT(TestCase, MemoryLeakMixin):
+
+    def test_return(self):
+        # Check returning a namedtuple with a list inside it
+        pyfunc = make_point_nrt
+        cfunc = jit(nopython=True)(pyfunc)
+
+        for arg in (3, 0):
+            expected = pyfunc(arg)
+            got = cfunc(arg)
+            self.assertIs(type(got), type(expected))
+            self.assertPreciseEqual(got, expected)
+
+
 if __name__ == '__main__':
     unittest.main()