Skip to content

Commit 920b96d

Browse files
authored
Fix renamed function lookup (pyccel#1914)
`FunctionDef` objects are annotated during the first `FunctionCall` where they are used. At this point they have not been renamed if there are name conflicts. Both the original and the collisionless name should therefore be used to locate the `FunctionDef`. The original name will retrieve the syntactic object while the collisionless name will retrieve the renamed function. Fixes pyccel#1913
1 parent 14ac94d commit 920b96d

File tree

6 files changed

+54
-13
lines changed

6 files changed

+54
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ All notable changes to this project will be documented in this file.
4040
- Link and mention `devel` branch, not `master`.
4141
- #1047 : Print the value of an unrecognised constant.
4242
- #1903 : Fix memory leak when using type annotations on local variables.
43+
- #1913 : Fix function calls to renamed functions.
4344

4445
### Changed
4546

pyccel/errors/messages.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"#$ header function FUNC_NAME(ARG_TYPES) results(RESULT_TYPES)\n")
2222

2323
INCOMPATIBLE_TYPES = 'Incompatible types'
24-
INCOMPATIBLE_TYPES_IN_ASSIGNMENT = "Variable has already been defined with type '{}'. Type '{}' in assignment is incompatible"
24+
INCOMPATIBLE_TYPES_IN_ASSIGNMENT = "Object has already been defined with type '{}'. Type '{}' in assignment is incompatible"
2525
INCOMPATIBLE_REDEFINITION = 'Incompatible redefinition'
2626
INCOMPATIBLE_REDEFINITION_STACK_ARRAY = 'Cannot change shape of stack array, because it does not support memory reallocation. Avoid redefinition, or use standard heap array.'
2727
STACK_ARRAY_DEFINITION_IN_LOOP = 'Cannot create stack array in loop, because if does not support memory reallocation. Create array before loop, or use standard heap array.'

pyccel/parser/base.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,26 @@ def blocking(self):
295295
return self._blocking
296296

297297
def insert_function(self, func):
298-
"""."""
298+
"""
299+
Insert a function into the current scope.
300+
301+
Insert a function into the current scope under the final name by which it
302+
will be known in the generated code.
303+
304+
Parameters
305+
----------
306+
func : FunctionDef | SympyFunction | Interface | FunctionAddress
307+
The function to be inserted into the scope.
308+
"""
299309

300310
if isinstance(func, SympyFunction):
301311
self.insert_symbolic_function(func)
302312
elif isinstance(func, (FunctionDef, Interface, FunctionAddress)):
303313
container = self.scope.functions
304-
container[func.name] = func
314+
if func.pyccel_staging == 'syntactic':
315+
container[self.scope.get_expected_name(func.name)] = func
316+
else:
317+
container[func.name] = func
305318
else:
306319
raise TypeError('Expected a Function definition')
307320

pyccel/parser/semantic.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,8 +1214,9 @@ def _annotate_the_called_function_def(self, old_func, function_call_args=None):
12141214
# Set the Scope to the FunctionDef's parent Scope and annotate the old_func
12151215
self._scope = sc
12161216
self._visit_FunctionDef(old_func, function_call_args=function_call_args)
1217+
new_name = self.scope.get_expected_name(old_func.name)
12171218
# Retreive the annotated function
1218-
func = self.scope.find(old_func.name, 'functions')
1219+
func = self.scope.find(new_name, 'functions')
12191220
# Add the Module of the imported function to the new function
12201221
if old_func.is_imported:
12211222
mod = old_func.get_direct_user_nodes(lambda x: isinstance(x, Module))[0]
@@ -1227,12 +1228,12 @@ def _annotate_the_called_function_def(self, old_func, function_call_args=None):
12271228
# Remove the old_func from the imports dict and Assign the new annotated one
12281229
if old_func.is_imported:
12291230
scope = self.scope
1230-
while old_func.name not in scope.imports['functions']:
1231+
while new_name not in scope.imports['functions']:
12311232
scope = scope.parent_scope
1232-
assert old_func is scope.imports['functions'].get(old_func.name)
1233-
func = func.clone(old_func.name, is_imported=True)
1233+
assert old_func is scope.imports['functions'].get(new_name)
1234+
func = func.clone(new_name, is_imported=True)
12341235
func.set_current_user_node(mod)
1235-
scope.imports['functions'][old_func.name] = func
1236+
scope.imports['functions'][new_name] = func
12361237
return func
12371238

12381239
def _create_variable(self, name, class_type, rhs, d_lhs, arr_in_multirets=False):
@@ -1592,7 +1593,14 @@ def _ensure_inferred_type_matches_existing(self, class_type, d_var, var, is_auga
15921593
# TODO improve check type compatibility
15931594
if not isinstance(var, Variable):
15941595
name = var.name
1595-
errors.report(INCOMPATIBLE_TYPES_IN_ASSIGNMENT.format(type(var), class_type),
1596+
message = INCOMPATIBLE_TYPES_IN_ASSIGNMENT.format(type(var), class_type)
1597+
if var.pyccel_staging == "syntactic":
1598+
new_name = self.scope.get_expected_name(name)
1599+
if new_name != name:
1600+
message += '\nThis error may be due to object renaming to avoid name clashes (language-specific or otherwise).'
1601+
message += f'The conflict is with "{name}".'
1602+
name = new_name
1603+
errors.report(message,
15961604
symbol=f'{name}={class_type}',
15971605
bounding_box=(self.current_ast_node.lineno, self.current_ast_node.col_offset),
15981606
severity='fatal')
@@ -2053,8 +2061,7 @@ def _visit_Module(self, expr):
20532061

20542062
self.scope = mod_scope
20552063

2056-
for f in self.scope.functions.copy():
2057-
f = self.scope.functions[f]
2064+
for f in self.scope.functions.copy().values():
20582065
if not f.is_semantic and not isinstance(f, InlineFunctionDef):
20592066
assert isinstance(f, FunctionDef)
20602067
self._visit(f)
@@ -3731,7 +3738,7 @@ def _visit_FunctionDef(self, expr, function_call_args=None):
37313738

37323739
existing_semantic_funcs = []
37333740
if not expr.is_semantic:
3734-
self.scope.functions.pop(expr.name, None)
3741+
self.scope.functions.pop(self.scope.get_expected_name(expr.name), None)
37353742
elif isinstance(expr, Interface):
37363743
existing_semantic_funcs = [*expr.functions]
37373744
expr = expr.syntactic_node
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
# pylint: disable=missing-function-docstring, missing-module-docstring
22
def f():
3-
return g()
3+
return g() + do()
44

55
def g():
66
return 2
77

8+
def do():
9+
return 3
10+
11+
def do_0001():
12+
return 4
13+
14+
815
a = f()
916

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# pylint: disable=missing-function-docstring, missing-module-docstring
2+
3+
def f():
4+
do_0001 = 5
5+
return g() + do() + do_0001
6+
7+
def g():
8+
return 2
9+
10+
def do():
11+
return 4
12+
13+
a = f()

0 commit comments

Comments
 (0)