Repository: numba/numba

Issue:
typed.List's empty_list function cannot accept allocated parameter in nopython mode 
<!--

Thanks for opening an issue! To help the Numba team handle your information
efficiently, please first ensure that there is no other issue present that
already describes the issue you have
(search at https://github.com/numba/numba/issues?&q=is%3Aissue).

For more general "how do I do X?" type questions, please speak to us in real
time on https://gitter.im/numba/numba or post to the Numba mailing list
https://groups.google.com/a/continuum.io/forum/#!forum/numba-users.

-->

## Reporting a bug

<!--

Before submitting a bug report please ensure that you can check off these boxes:

-->

- [x] I am using the latest released version of Numba (most recent is visible in
 the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).
- [x] I have included below a minimal working reproducer (if you are unsure how
 to write one see http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports).

Hi, 

It seems the overload for 'empty_list' function has no allocated parameter, hence it's not possible to use pre-allocated typed.Lists in nopython mode, i.e. with the following reproducer compilation fails in nopython mode and passes in obj mode:

```
    def test_numba_typed_list_allocated(self):
        import numba
        from numba import types
        from numba.typed import Dict, List
        list_type1 = types.ListType(types.int64)

        def test_impl():
            # allocated doesn't compile in nopython mode, but works in objmode
            # problem in overload of empty_list in typedlist.py
            alloc_list = List.empty_list(types.int64, 10)
            return len(alloc_list)
        jitted = numba.njit(test_impl)

        print(jitted())
```

The error:
```
======================================================================
ERROR: test_numba_typed_list_allocated (sdc.tests.test_series.TestSeries)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\akozlov\AppData\Local\Continuum\anaconda3\hpat\sdc\tests\test_series.py
", line 6817, in test_numba_typed_list_allocated
    print(jitted())
  File "c:\users\akozlov\appdata\local\continuum\anaconda3\numba_master\numba\numba\disp
atcher.py", line 401, in _compile_for_args
    error_rewrite(e, 'typing')
  File "c:\users\akozlov\appdata\local\continuum\anaconda3\numba_master\numba\numba\disp
atcher.py", line 344, in error_rewrite
    reraise(type(e), e, None)
  File "c:\users\akozlov\appdata\local\continuum\anaconda3\numba_master\numba\numba\six.
py", line 668, in reraise
    raise value.with_traceback(tb)
numba.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Invalid use of Function(<function typedlist_empty at 0x0000015A9C481550>) with argument(
s) of type(s): (typeref[<class 'numba.types.containers.ListType'>], class(int64), int64)

 * parameterized
In definition 0:
    TypeError: typedlist_empty() takes 2 positional arguments but 3 were given
    raised from c:\users\akozlov\appdata\local\continuum\anaconda3\numba_master\numba\nu
mba\typing\templates.py:539
In definition 1:
    TypeError: typedlist_empty() takes 2 positional arguments but 3 were given
    raised from c:\users\akozlov\appdata\local\continuum\anaconda3\numba_master\numba\nu
mba\typing\templates.py:539
This error is usually caused by passing an argument of a type that is unsupported by the
 named function.
[1] During: resolving callee type: BoundFunction((<class 'numba.types.abstract.TypeRef'>
, 'empty_list') for typeref[<class 'numba.types.containers.ListType'>])
[2] During: typing of call at C:\Users\akozlov\AppData\Local\Continuum\anaconda3\hpat\sd
c\tests\test_series.py (6813)


File "sdc\tests\test_series.py", line 6813:
        def test_impl():
            <source elided>
            # problem in overload of empty_list in typedlist.py
            alloc_list = List.empty_list(types.int64, 10)
            ^


----------------------------------------------------------------------
Ran 1 test in 0.075s

FAILED (errors=1)
```

Numba version:
# Name                    Version                   Build  Channel
numba                     0.48.0                    dev_0    <develop>

Best Regards,
Alexey.


Discussion:
@esc any chance you could take a look at this please as you're likely most familiar with it. Thanks.
Here is the patch for this:

```
diff --git i/numba/typed/typedlist.py w/numba/typed/typedlist.py
index 0b0f73d6d4..896e060dc9 100644
--- i/numba/typed/typedlist.py
+++ w/numba/typed/typedlist.py
@@ -364,12 +364,12 @@ class List(MutableSequence):

 # XXX: should we have a better way to classmethod
 @overload_method(TypeRef, 'empty_list')
-def typedlist_empty(cls, item_type):
+def typedlist_empty(cls, item_type, allocated=0):
     if cls.instance_type is not ListType:
         return

-    def impl(cls, item_type):
-        return listobject.new_list(item_type)
+    def impl(cls, item_type, allocated=0):
+        return listobject.new_list(item_type, allocated=allocated)

     return impl
```

I'll prep a PR with appropriate tests a little later on.
Thanks @esc 

Resolving patch:
diff --git a/numba/typed/listobject.py b/numba/typed/listobject.py
--- a/numba/typed/listobject.py
+++ b/numba/typed/listobject.py
@@ -50,6 +50,8 @@
 
 index_types = types.integer_domain
 
+DEFAULT_ALLOCATED = 0
+
 
 @register_model(ListType)
 class ListModel(models.StructModel):
@@ -258,7 +260,7 @@ def _imp_dtor(context, module):
     return fn
 
 
-def new_list(item, allocated=0):
+def new_list(item, allocated=DEFAULT_ALLOCATED):
     """Construct a new list. (Not implemented in the interpreter yet)
 
     Parameters
@@ -369,7 +371,7 @@ def codegen(context, builder, sig, args):
 
 
 @overload(new_list)
-def impl_new_list(item, allocated=0):
+def impl_new_list(item, allocated=DEFAULT_ALLOCATED):
     """Creates a new list.
 
     Parameters
@@ -385,7 +387,7 @@ def impl_new_list(item, allocated=0):
 
     itemty = item
 
-    def imp(item, allocated=0):
+    def imp(item, allocated=DEFAULT_ALLOCATED):
         if allocated < 0:
             raise RuntimeError("expecting *allocated* to be >= 0")
         lp = _list_new(itemty, allocated)
diff --git a/numba/typed/typedlist.py b/numba/typed/typedlist.py
--- a/numba/typed/typedlist.py
+++ b/numba/typed/typedlist.py
@@ -25,9 +25,11 @@
 )
 from numba.typed import listobject
 
+DEFAULT_ALLOCATED = listobject.DEFAULT_ALLOCATED
+
 
 @njit
-def _make_list(itemty, allocated=0):
+def _make_list(itemty, allocated=DEFAULT_ALLOCATED):
     return listobject._as_meminfo(listobject.new_list(itemty,
                                                       allocated=allocated))
 
@@ -172,14 +174,14 @@ class List(MutableSequence):
     Implements the MutableSequence interface.
     """
 
-    def __new__(cls, lsttype=None, meminfo=None, allocated=None):
+    def __new__(cls, lsttype=None, meminfo=None, allocated=DEFAULT_ALLOCATED):
         if config.DISABLE_JIT:
             return list.__new__(list)
         else:
             return object.__new__(cls)
 
     @classmethod
-    def empty_list(cls, item_type, allocated=0):
+    def empty_list(cls, item_type, allocated=DEFAULT_ALLOCATED):
         """Create a new empty List.
 
         Parameters
@@ -213,7 +215,7 @@ def __init__(self, **kwargs):
         else:
             self._list_type = None
 
-    def _parse_arg(self, lsttype, meminfo=None, allocated=0):
+    def _parse_arg(self, lsttype, meminfo=None, allocated=DEFAULT_ALLOCATED):
         if not isinstance(lsttype, ListType):
             raise TypeError('*lsttype* must be a ListType')
 
@@ -247,7 +249,7 @@ def __len__(self):
 
     def _allocated(self):
         if not self._typed:
-            return 0
+            return DEFAULT_ALLOCATED
         else:
             return _allocated(self)
 
@@ -364,12 +366,12 @@ def __repr__(self):
 
 # XXX: should we have a better way to classmethod
 @overload_method(TypeRef, 'empty_list')
-def typedlist_empty(cls, item_type):
+def typedlist_empty(cls, item_type, allocated=DEFAULT_ALLOCATED):
     if cls.instance_type is not ListType:
         return
 
-    def impl(cls, item_type):
-        return listobject.new_list(item_type)
+    def impl(cls, item_type, allocated=DEFAULT_ALLOCATED):
+        return listobject.new_list(item_type, allocated=allocated)
 
     return impl
 


Test patch:
diff --git a/numba/tests/test_typedlist.py b/numba/tests/test_typedlist.py
--- a/numba/tests/test_typedlist.py
+++ b/numba/tests/test_typedlist.py
@@ -616,6 +616,25 @@ def test_allocation(self):
             tl = List.empty_list(types.int32, i)
             self.assertEqual(tl._allocated(), i)
 
+    def test_allocation_njit(self):
+        # kwarg version
+        @njit
+        def foo(i):
+            tl = List.empty_list(types.int32, allocated=i)
+            return tl._allocated()
+
+        for j in range(16):
+            self.assertEqual(foo(j), j)
+
+        # posarg version
+        @njit
+        def foo(i):
+            tl = List.empty_list(types.int32, i)
+            return tl._allocated()
+
+        for j in range(16):
+            self.assertEqual(foo(j), j)
+
     def test_growth_and_shrinkage(self):
         tl = List.empty_list(types.int32)
         growth_before = {0: 0, 4:4, 8:8, 16:16}