Repository: numba/numba

Issue:
The `np.size()` Function Fails
## The Bug

It seems Numba support the `ndarray` property of an array (See [`numpy.ndarray.size`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.size.html)) yet fails with the function [`np.size()`](https://numpy.org/doc/stable/reference/generated/numpy.ma.size.html).

## Code for Producing the Bug

The following works:


```python
import numpy as np
from numba import njit

@njit
def SumArrayWorks( vX : np.ndarray ) -> float:
  arrSum = 0.0
  for ii in range(vX.size):
    arrSum += vX[ii]

  return arrSum

vX = np.array([1.0, 2.0, 3.0, 4.0])
SumArrayWorks(vX) #<! Works!

```

While this will fail:

```python
import numpy as np
from numba import njit

@njit
def SumArrayFails( vX : np.ndarray ) -> float:
  arrSum = 0.0
  for ii in range(np.size(vX)):
    arrSum += vX[ii]

  return arrSum

vX = np.array([1.0, 2.0, 3.0, 4.0])
SumArrayFails(vX) #<! Fails!
```

The error is:

```python
TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Use of unsupported NumPy function 'numpy.size' or unsupported use of the function.
```

## System Information

1. Numba: `0.59.0`.
2. Python: `3.12`.
3. NumPy: `1.26.4`.
4. OS: Windows 11 Pro 23H2.



Discussion:
`np.size()` is so rarely used that we completely missed it for all these years.
Hi, would a simple solution be to just call np.size() outside of the njit function? I am new to this, but here is my attempt to use np.size() over .size: 

```python
import numpy as np
from numba import njit

@njit
def SumArrayWorks_NpSize( vX : np.ndarray ) -> float:
  arrSum = 0.0
  for ii in range(vX_size):
    arrSum += vX[ii]

  return arrSum

vX = np.array([1.0, 2.0, 3.0, 4.0])
vX_size = np.size(vX)

SumArrayWorks_NpSize(vX) #<! Works!```
@SDanielDev , The concept here is about having a native Numba support, not bypassing it.
Specifically, it is better to avoid using global variables. A simpler and better solution would be using `vX.size` instead of `np.size()` which works as shown above.

Resolving patch:
diff --git a/numba/np/arrayobj.py b/numba/np/arrayobj.py
--- a/numba/np/arrayobj.py
+++ b/numba/np/arrayobj.py
@@ -2555,6 +2555,16 @@ def impl(a):
         return np.asarray(a).shape
     return impl
 
+
+@overload(np.size)
+def np_size(a):
+    if not type_can_asarray(a):
+        raise errors.TypingError("The argument to np.size must be array-like")
+
+    def impl(a):
+        return np.asarray(a).size
+    return impl
+
 # ------------------------------------------------------------------------------
 
 


Test patch:
diff --git a/numba/tests/test_array_manipulation.py b/numba/tests/test_array_manipulation.py
--- a/numba/tests/test_array_manipulation.py
+++ b/numba/tests/test_array_manipulation.py
@@ -174,6 +174,9 @@ def numpy_fill_diagonal(arr, val, wrap=False):
 def numpy_shape(arr):
     return np.shape(arr)
 
+def numpy_size(arr):
+    return np.size(arr)
+
 
 def numpy_flatnonzero(a):
     return np.flatnonzero(a)
@@ -1219,6 +1222,30 @@ def check(x):
         self.assertIn("The argument to np.shape must be array-like",
                       str(raises.exception))
 
+    def test_size(self):
+        pyfunc = numpy_size
+        cfunc = jit(nopython=True)(pyfunc)
+
+        def check(x):
+            expected = pyfunc(x)
+            got = cfunc(x)
+            self.assertPreciseEqual(got, expected)
+
+        # check arrays
+        for t in [(), (1,), (2, 3,), (4, 5, 6)]:
+            arr = np.empty(t)
+            check(arr)
+
+        # check scalar values
+        for t in [1, False, 3.14, np.int8(4), np.float32(2.718)]:
+            check(t)
+
+        with self.assertRaises(TypingError) as raises:
+            cfunc('a')
+
+        self.assertIn("The argument to np.size must be array-like",
+                      str(raises.exception))
+
     def test_flatnonzero_basic(self):
         pyfunc = numpy_flatnonzero
         cfunc = jit(nopython=True)(pyfunc)
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
@@ -332,6 +332,10 @@ def get_shape(rec):
     return np.shape(rec.j)
 
 
+def get_size(rec):
+    return np.size(rec.j)
+
+
 def get_charseq(ary, i):
     return ary[i].n
 
@@ -1616,6 +1620,20 @@ def test_shape(self):
         arr_res = cfunc(arg)
         np.testing.assert_equal(arr_res, arr_expected)
 
+    def test_size(self):
+        # test getting the size of a nestedarray inside a record
+        nbarr = np.recarray(2, dtype=recordwith2darray)
+        nbarr[0] = np.array([(1, ((1, 2), (4, 5), (2, 3)))],
+                            dtype=recordwith2darray)[0]
+
+        arg = nbarr[0]
+        pyfunc = get_size
+        ty = typeof(arg)
+        arr_expected = pyfunc(arg)
+        cfunc = self.get_cfunc(pyfunc, (ty,))
+        arr_res = cfunc(arg)
+        np.testing.assert_equal(arr_res, arr_expected)
+
     def test_corner_slice(self):
         # testing corner cases while slicing nested arrays
         nbarr = np.recarray((1, 2, 3, 5, 7, 13, 17), dtype=recordwith4darray,