Repository: pyccel/pyccel
Issue: Shape of pointer is not updated when pointer is reassigned

**Describe the bug**
A pointer can point at objects with different shapes as long as they have the same rank. However as we set the shape in the constructor and never modify it, if a pointer is reallocated then it has the wrong shape

**To Reproduce**
Provide code to reproduce the behavior:
```python
from numpy import array, shape
a = array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
b = array([1, 2, 3, 4, 5, 6, 7, 8, 9])
c = a
c = a[1:]
d = c*b
print(d)
```

raises the following error when translated with pyccel:
```bash
ERROR at annotation (semantic) stage
pyccel:
 |fatal [semantic]: tmp.py [6,4]| operands could not be broadcast together with shapes (<pyccel.ast.literals.LiteralInteger object at 0x7f72c92c7b80>,) (<pyccel.ast.literals.LiteralInteger object at 0x7f72c8f4b950>,) (c * b)
```
but runs correctly in python

**Expected behavior**
The shape should be set to a tuple of `PyccelArraySize`s when the `Variable` is a pointer

Changed files:
pyccel/ast/operators.py, tests/epyccel/modules/pointers.py, pyccel/ast/literals.py, pyccel/ast/variable.py

diff --git a/pyccel/ast/literals.py b/pyccel/ast/literals.py
index 7482d11e..e2a97c26 100644
--- a/pyccel/ast/literals.py
+++ b/pyccel/ast/literals.py
@@ -49,6 +49,9 @@ class Literal(PyccelAstNode):
     def python_value(self):
         """ Get python literal represented by this instance """
 
+    def __repr__(self):
+        return "Literal({})".format(repr(self.python_value))
+
     def __str__(self):
         return str(self.python_value)
 
diff --git a/pyccel/ast/operators.py b/pyccel/ast/operators.py
index 8d9ba5c7..7bc6e277 100644
--- a/pyccel/ast/operators.py
+++ b/pyccel/ast/operators.py
@@ -88,8 +88,10 @@ def broadcast(shape_1, shape_2):
                 and not (sy_e1 - sy_e2).is_constant():
             new_shape.append(e1)
         else:
+            shape1_code = '({})'.format(' '.join([str(s)+',' for s in shape_1]))
+            shape2_code = '({})'.format(' '.join([str(s)+',' for s in shape_2]))
             msg = 'operands could not be broadcast together with shapes {} {}'
-            msg = msg.format(shape_1, shape_2)
+            msg = msg.format(shape1_code, shape2_code)
             raise PyccelSemanticError(msg)
     return tuple(new_shape)
 
diff --git a/pyccel/ast/variable.py b/pyccel/ast/variable.py
index 6fdf1afe..f72e99da 100644
--- a/pyccel/ast/variable.py
+++ b/pyccel/ast/variable.py
@@ -130,40 +130,6 @@ class Variable(PyccelAstNode):
         ):
         super().__init__()
 
-        # ------------ PyccelAstNode Properties ---------------
-        if isinstance(dtype, str) or str(dtype) == '*':
-
-            dtype = datatype(str(dtype))
-        elif not isinstance(dtype, DataType):
-            raise TypeError('datatype must be an instance of DataType.')
-
-        if not isinstance(rank, int):
-            raise TypeError('rank must be an instance of int.')
-
-        if rank == 0:
-            shape = ()
-
-        if shape is None:
-            shape = tuple(None for i in range(rank))
-
-        if not precision:
-            if isinstance(dtype, NativeInteger):
-                precision = default_precision['int']
-            elif isinstance(dtype, NativeReal):
-                precision = default_precision['real']
-            elif isinstance(dtype, NativeComplex):
-                precision = default_precision['complex']
-            elif isinstance(dtype, NativeBool):
-                precision = default_precision['bool']
-        if not isinstance(precision,int) and precision is not None:
-            raise TypeError('precision must be an integer or None.')
-
-        self._alloc_shape = shape
-        self._dtype = dtype
-        self._rank  = rank
-        self._shape = self.process_shape(shape)
-        self._precision = precision
-
         # ------------ Variable Properties ---------------
         # if class attribute
         if isinstance(name, str):
@@ -214,6 +180,40 @@ class Variable(PyccelAstNode):
         self._is_kwonly      = is_kwonly
         self._is_temp        = is_temp
 
+        # ------------ PyccelAstNode Properties ---------------
+        if isinstance(dtype, str) or str(dtype) == '*':
+
+            dtype = datatype(str(dtype))
+        elif not isinstance(dtype, DataType):
+            raise TypeError('datatype must be an instance of DataType.')
+
+        if not isinstance(rank, int):
+            raise TypeError('rank must be an instance of int.')
+
+        if rank == 0:
+            shape = ()
+
+        if shape is None:
+            shape = tuple(None for i in range(rank))
+
+        if not precision:
+            if isinstance(dtype, NativeInteger):
+                precision = default_precision['int']
+            elif isinstance(dtype, NativeReal):
+                precision = default_precision['real']
+            elif isinstance(dtype, NativeComplex):
+                precision = default_precision['complex']
+            elif isinstance(dtype, NativeBool):
+                precision = default_precision['bool']
+        if not isinstance(precision,int) and precision is not None:
+            raise TypeError('precision must be an integer or None.')
+
+        self._alloc_shape = shape
+        self._dtype = dtype
+        self._rank  = rank
+        self._shape = self.process_shape(shape)
+        self._precision = precision
+
     def process_shape(self, shape):
         """ Simplify the provided shape and ensure it
         has the expected format
@@ -230,7 +230,10 @@ class Variable(PyccelAstNode):
 
         new_shape = []
         for i,s in enumerate(shape):
-            if isinstance(s,(LiteralInteger, PyccelArraySize)):
+            if self.is_pointer:
+                # Shape of a pointer can change
+                new_shape.append(PyccelArraySize(self, LiteralInteger(i)))
+            elif isinstance(s,(LiteralInteger, PyccelArraySize)):
                 new_shape.append(s)
             elif isinstance(s, int):
                 new_shape.append(LiteralInteger(s))
diff --git a/tests/epyccel/modules/pointers.py b/tests/epyccel/modules/pointers.py
index 2afd811e..4fd868d5 100644
--- a/tests/epyccel/modules/pointers.py
+++ b/tests/epyccel/modules/pointers.py
@@ -3,7 +3,8 @@ __all__ = [
         'slice_is_pointer_idx_0',
         'array_copy_is_pointer',
         'pointer_to_pointer_is_pointer',
-        'reassigned_pointer'
+        'reassigned_pointer',
+        'reassigned_pointer_shape'
         ]
 
 def slice_is_pointer_idx_0():
@@ -36,3 +37,12 @@ def reassigned_pointer():
     c[1] = 0
     c = a
     return c[0], c[1], c[2], shape(c)[0]
+
+def reassigned_pointer_shape():
+    from numpy import array, shape
+    a = array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
+    b = array([1, 2, 3, 4, 5, 6, 7, 8, 9])
+    c = a
+    c = a[1:]
+    d = c*b
+    return shape(d)[0], d[0], d[5]