Repository: pyccel/pyccel (python)

Issue:
Variable already exists when using type annotation and result annotation
### Describe the bug
When we use a type annotation for both the result type and the variable returned as the result, Pyccel thinks that the type has been declared twice. This leads to an error.

### To Reproduce
Provide code to reproduce the behaviour:
```python
def f() -> 'int':
    result_0001 : 'int'
    return result_0001
```
The following code also causes the error but reports it more cleanly:
```python
def f() -> 'int':
    result_0001 : 'int' = 1
    return result_0001
```

### Error details
Provide the generated code, or the error message:
```
  File "/home/emily/Code/pyccel/pyccel/parser/semantic.py", line 4551, in _visit_FunctionDef
    body = self._visit(expr.body)
  File "/home/emily/Code/pyccel/pyccel/parser/semantic.py", line 2373, in _visit
    raise err
  File "/home/emily/Code/pyccel/pyccel/parser/semantic.py", line 2361, in _visit
    obj = getattr(self, annotation_method)(expr)
  File "/home/emily/Code/pyccel/pyccel/parser/semantic.py", line 2712, in _visit_CodeBlock
    self.scope.insert_variable(line[0])
  File "/home/emily/Code/pyccel/pyccel/parser/scope.py", line 355, in insert_variable
    raise RuntimeError(f'New variable {name} already exists in scope')
RuntimeError: New variable result_0001 already exists in scope
```
with `result_0001 : 'int' = 1`:
```
ERROR at annotation (semantic) stage
pyccel:
   File "/home/emily/Code/pyccel/pyccel/parser/semantic.py", line 2361, in _visit
    obj = getattr(self, annotation_method)(expr)
  File "/home/emily/Code/pyccel/pyccel/parser/semantic.py", line 2705, in _visit_CodeBlock
    line = self._visit(b)
  File "/home/emily/Code/pyccel/pyccel/parser/semantic.py", line 2361, in _visit
    obj = getattr(self, annotation_method)(expr)
  File "/home/emily/Code/pyccel/pyccel/parser/semantic.py", line 3442, in _visit_Assign
    errors.report(e, symbol=expr, severity='error')
  File "/home/emily/Code/pyccel/pyccel/errors/errors.py", line 345, in report
    traceback = ''.join(tb.format_stack(limit=5))
|error [semantic]: tmp.py [3,4]| New variable result_0001 already exists in scope (<pyccel.ast.variable.AnnotatedPyccelSymbol object at 0x7416a6fb90d0> := Literal(0))
```

### Expected behavior
This should be allowed

### Language
All languages

### Additional context
Blocking #2217 


Pull request description:
Fix annotation of variables that are returned in a function whose result type is annotated
Fix annotation of variables that are returned in a function whose result type is annotated. Fixes #2229 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 950cf7ec..487ea44b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -129,6 +129,7 @@ All notable changes to this project will be documented in this file.
 -   #2220 : Fix premature `stc/cspan` import.
 -   #2214 : Fix returning a local variable from an inline function.
 -   #1321 : Fix use of tuples returned from functions in a non-assign statement.
+-   #2229 : Fix annotation of variables that are returned in a function whose result type is annotated.
 
 ### Changed
 
diff --git a/pyccel/ast/type_annotations.py b/pyccel/ast/type_annotations.py
index 4b1c22e5..d6a97401 100644
--- a/pyccel/ast/type_annotations.py
+++ b/pyccel/ast/type_annotations.py
@@ -313,6 +313,10 @@ class SyntacticTypeAnnotation(PyccelAstNode):
         else:
             return False
 
+    def __str__(self):
+        order_str = f'(order={self.order})' if self.order else ''
+        return f'{self.dtype}{order_str}'
+
 #==============================================================================
 
 typenames_to_dtypes = { 'float'   : PythonNativeFloat(),
diff --git a/pyccel/parser/semantic.py b/pyccel/parser/semantic.py
index 0e373a4f..671c2a29 100644
--- a/pyccel/parser/semantic.py
+++ b/pyccel/parser/semantic.py
@@ -2896,8 +2896,8 @@ class SemanticParser(BasicParser):
 
     def _visit_AnnotatedPyccelSymbol(self, expr):
         # Check if the variable already exists
-        var = self.scope.find(expr, 'variables', local_only = True)
-        if var is not None:
+        var = self.scope.find(expr.name, 'variables', local_only = True)
+        if var is not None and not any(isinstance(n, FunctionDefResult) for n in var.get_all_user_nodes()):
             errors.report("Variable has been declared multiple times",
                     symbol=expr, severity='error')
 
@@ -2987,6 +2987,16 @@ class SemanticParser(BasicParser):
         # An annotated variable must have a type
         assert len(possible_args) != 0
 
+        # If var was declared in results
+        if var is not None:
+            new_var = possible_args[0]
+            if len(possible_args) != 1 or new_var.class_type != var.class_type:
+                errors.report(f"Variable was declared as the result of the function {self.current_function} but is now declared with a different type",
+                        symbol=expr, severity='error')
+            # Remove variable from scope as AnnotatedPyccelSymbol is always inserted into scope
+            self.scope.remove_variable(var)
+            return [var]
+
         return possible_args
 
     def _visit_SyntacticTypeAnnotation(self, expr):


Test patch:
diff --git a/tests/epyccel/test_epyccel_functions.py b/tests/epyccel/test_epyccel_functions.py
index 1860bc2b..b3a2081c 100644
--- a/tests/epyccel/test_epyccel_functions.py
+++ b/tests/epyccel/test_epyccel_functions.py
@@ -358,6 +358,14 @@ def test_union_type(language):
     assert np.isclose(f(y), square(y), rtol=RTOL, atol=ATOL)
     assert isinstance(f(y), type(square(y)))
 
+def test_return_annotation(language):
+    def get_2() -> int:
+        my_var : int = 2
+        return my_var
+
+    f = epyccel(get_2, language=language)
+    assert f() == get_2()
+
 ##==============================================================================
 ## CLEAN UP GENERATED FILES AFTER RUNNING TESTS
 ##==============================================================================