Repository: numba/numba

Issue:
Numba's `np.broadcast_to` doesn't change the size of the array
<!--

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).

-->

## Reporting a bug

<!--

Before submitting a bug report please ensure that you can check off these boxes:

-->

- [x] I have tried using the latest released version of Numba (most recent is
 visible in the change log (https://github.com/numba/numba/blob/main/CHANGE_LOG).
- [x] I have included a self contained code sample to reproduce the problem.
  i.e. it's possible to run as 'python bug.py'.

<!--

Please include details of the bug here, including, if applicable, what you
expected to happen!

-->

As titled, Numba's `np.broadcast_to` doesn't update the size of an array after broadcasting it. 

MWE as follows:

```python
import numpy as np
import numba

@numba.njit     
def bdcast(array):
    bdcast_array = np.broadcast_to(array, (2, 3))
    return bdcast_array.size

array = np.array([1,2,0])

expected = bdcast.py_func(array)
got = bdcast(array)

# Initial size of array
print(array.size) # 3

# Expected size after broadcast
print(expected) # 6
# Returned size after broadcast
print(got) # 3

np.testing.assert_equal(got, expected)

```

Numba's `np.broadcast_to` doesn't change the size of the array
<!--

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).

-->

## Reporting a bug

<!--

Before submitting a bug report please ensure that you can check off these boxes:

-->

- [x] I have tried using the latest released version of Numba (most recent is
 visible in the change log (https://github.com/numba/numba/blob/main/CHANGE_LOG).
- [x] I have included a self contained code sample to reproduce the problem.
  i.e. it's possible to run as 'python bug.py'.

<!--

Please include details of the bug here, including, if applicable, what you
expected to happen!

-->

As titled, Numba's `np.broadcast_to` doesn't update the size of an array after broadcasting it. 

MWE as follows:

```python
import numpy as np
import numba

@numba.njit     
def bdcast(array):
    bdcast_array = np.broadcast_to(array, (2, 3))
    return bdcast_array.size

array = np.array([1,2,0])

expected = bdcast.py_func(array)
got = bdcast(array)

# Initial size of array
print(array.size) # 3

# Expected size after broadcast
print(expected) # 6
# Returned size after broadcast
print(got) # 3

np.testing.assert_equal(got, expected)

```



Discussion:
Thanks for the report. Seems like `size` attr is incorrect in the compiled code:
```python
from numba import njit
import numpy as np

@njit
def foo(arr):
    ret = np.broadcast_to(arr, (2, 3))
    return ret, ret.size, ret.shape

arr = np.arange(3)

def dump(where, x, sz, shp):
    print(where)
    print(x)
    print(x.size)
    print(x.shape)
    print(sz)
    print(shp)
    print("")

dump("pyfunc", *foo.py_func(arr))
dump("numba ", *foo(arr))
```
gives:
```
pyfunc
[[0 1 2]
 [0 1 2]]
6
(2, 3)
6
(2, 3)

numba 
[[0 1 2]
 [0 1 2]]
6
(2, 3)
3
(2, 3)
```
the returned array seems correct, it's the `.size` attr in the compiled region that's incorrect.
Thanks for the report. Seems like `size` attr is incorrect in the compiled code:
```python
from numba import njit
import numpy as np

@njit
def foo(arr):
    ret = np.broadcast_to(arr, (2, 3))
    return ret, ret.size, ret.shape

arr = np.arange(3)

def dump(where, x, sz, shp):
    print(where)
    print(x)
    print(x.size)
    print(x.shape)
    print(sz)
    print(shp)
    print("")

dump("pyfunc", *foo.py_func(arr))
dump("numba ", *foo(arr))
```
gives:
```
pyfunc
[[0 1 2]
 [0 1 2]]
6
(2, 3)
6
(2, 3)

numba 
[[0 1 2]
 [0 1 2]]
6
(2, 3)
3
(2, 3)
```
the returned array seems correct, it's the `.size` attr in the compiled region that's incorrect.

Resolving patch:
diff --git a/numba/np/arrayobj.py b/numba/np/arrayobj.py
--- a/numba/np/arrayobj.py
+++ b/numba/np/arrayobj.py
@@ -1342,9 +1342,13 @@ def _broadcast_to_shape(context, builder, arrtype, arr, target_shape):
     new_arrtype = arrtype.copy(ndim=len(target_shape), layout='A')
     # Create new view
     new_arr = make_array(new_arrtype)(context, builder)
-    repl = dict(shape=cgutils.pack_array(builder, shapes),
-                strides=cgutils.pack_array(builder, strides))
-    cgutils.copy_struct(new_arr, arr, repl)
+    populate_array(new_arr,
+                   data=arr.data,
+                   shape=cgutils.pack_array(builder, shapes),
+                   strides=cgutils.pack_array(builder, strides),
+                   itemsize=arr.itemsize,
+                   meminfo=arr.meminfo,
+                   parent=arr.parent)
     return new_arrtype, new_arr
 
 


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
@@ -921,6 +921,20 @@ def test_broadcast_to_indexing(self):
             got = cfunc(input_array, shape, idx)
             self.assertPreciseEqual(got, expected)
 
+    def test_broadcast_to_array_attrs(self):
+        # See issue #8534. This tests that broadcast array attributes have the
+        # correct value when accessed.
+        @njit
+        def foo(arr):
+            ret = np.broadcast_to(arr, (2, 3))
+            return ret, ret.size, ret.shape, ret.strides
+
+        arr = np.arange(3)
+
+        expected = foo.py_func(arr)
+        got = foo(arr)
+        self.assertPreciseEqual(expected, got)
+
     @unittest.skipIf(numpy_version < (1, 20), "requires NumPy 1.20 or newer")
     def test_broadcast_shapes(self):
         pyfunc = numpy_broadcast_shapes