Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
3e0e3da
updates for optional arguments
schreiberx Jul 18, 2025
9200c57
minor updates
schreiberx Jul 18, 2025
fdd3e80
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
schreiberx Jul 18, 2025
a2a69a1
updated documentation strings
schreiberx Jul 18, 2025
6dbf457
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Jul 21, 2025
41d8a1f
#3067 revert check_unresolved_symbols changes to SymbolTable
arporter Jul 22, 2025
2035144
#3067 fix syntax
arporter Jul 22, 2025
b68bac3
#3067 switch to keyword args and fix all tests [skip ci]
arporter Jul 23, 2025
64ff51c
#3067 rename _get_argument_routine_match to get_argument_map() and ti…
arporter Jul 23, 2025
27e03a7
#3067 revert all changes to SymbolTable [skip ci]
arporter Jul 23, 2025
6eba1c9
#3067 put back setting of DefaultModuleInterface (but no tests were a…
arporter Jul 23, 2025
4b2714f
#3067 correct type hint [skip ci]
arporter Jul 23, 2025
1e6bcf5
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Jul 25, 2025
77ae188
#3027 rationalise args and fix formatting
arporter Jul 25, 2025
8689b80
#3067 fix linting
arporter Jul 25, 2025
cde738a
#3067 tidying and move tests to correct files
arporter Jul 28, 2025
c7e8328
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Jul 28, 2025
28b6970
#3067 fix linting
arporter Jul 28, 2025
42f739a
#3067 tidying of InlineTrans and tests
arporter Jul 30, 2025
512d9de
#3067 add extra validation in IfBlock.condition getter
arporter Jul 30, 2025
e7a34a7
#3067 rationalise InlineTrans tests and get full cov
arporter Jul 30, 2025
0ca8e83
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Aug 4, 2025
5f11c5d
#3067 rm unused options from get_callees()
arporter Aug 4, 2025
f4092c5
#3067 further tidying
arporter Aug 4, 2025
dd96787
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Aug 11, 2025
ab4d864
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Aug 12, 2025
157584b
#3067 update to use SymbolicMaths to simply condition expression of i…
arporter Aug 13, 2025
c41d04f
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Aug 20, 2025
41c4e88
#3067 update docstrings
arporter Aug 20, 2025
cecfb45
#3067 WIP improving testing
arporter Aug 20, 2025
e4422d6
#3067 extend test with 2nd optional arg [skip ci]
arporter Aug 20, 2025
a06582f
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Aug 20, 2025
1f2c2d8
#3067 fix inlining test
arporter Aug 20, 2025
788e79c
#3067 extend testing of Call
arporter Aug 20, 2025
e873644
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Aug 29, 2025
932a70f
#3067 fix args to InlineTrans.validate() in tests
arporter Aug 29, 2025
07c6a0d
#3067 improve testing of Call
arporter Sep 1, 2025
a2454d3
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Sep 11, 2025
efa5dca
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Sep 15, 2025
9d69873
#3067 update to check on types of array arguments
arporter Sep 15, 2025
899bc69
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Oct 3, 2025
469faa6
#3067 fix error in merge
arporter Oct 3, 2025
9dcef94
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Oct 3, 2025
dc94bd2
#3067 fix linting
arporter Oct 3, 2025
bd4dc4d
#3067 mv check on IfBlock conditional to create() method
arporter Oct 3, 2025
de92e31
#3067 rm type hint from test for now
arporter Oct 3, 2025
93c9b4b
#3067 revert IfBlock changes
arporter Oct 3, 2025
ce85922
#3607 updates for review
arporter Oct 3, 2025
69c2e3b
#3067 updates to InlineTrans and test for review
arporter Oct 3, 2025
cbe01e7
Merge branch 'master' into martin_2_issue_inline_optional_args_2525_new
arporter Oct 3, 2025
7404293
#3607 fix syntax error in docstring
arporter Oct 3, 2025
254f000
#3607 update to get coverage
arporter Oct 3, 2025
5f90b16
#3067 Updated changelog.
hiker Oct 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ cov.xml
.coverage.*
*.psycache
__pycache__
build
3 changes: 3 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
3) PR #3067 for #2525. Updates to InlineTrans to support optional
arguments

2) PR #3131 for #2049. Refactor and rename DriverCreator classes.

1) PR #3118 towards #3049. Adds the --fixed-form and --free-form flags
Expand Down
3 changes: 2 additions & 1 deletion src/psyclone/psyir/nodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
from psyclone.psyir.nodes.statement import Statement
from psyclone.psyir.nodes.structure_reference import StructureReference
from psyclone.psyir.nodes.structure_member import StructureMember
from psyclone.psyir.nodes.call import Call
from psyclone.psyir.nodes.call import Call, CallMatchingArgumentsNotFound
from psyclone.psyir.nodes.file_container import FileContainer
from psyclone.psyir.nodes.directive import (
Directive, StandaloneDirective, RegionDirective)
Expand Down Expand Up @@ -119,6 +119,7 @@
'AtomicDirectiveMixin',
'BinaryOperation',
'Call',
'CallMatchingArgumentsNotFound',
'Clause',
'CodeBlock',
'Container',
Expand Down
183 changes: 116 additions & 67 deletions src/psyclone/psyir/nodes/call.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@

''' This module contains the Call node implementation.'''

# Support for *postponed* type annotations.
from __future__ import annotations

from collections.abc import Iterable
from typing import List, Tuple
from typing import List, Tuple, Union

from psyclone.configuration import Config
from psyclone.core import AccessType, VariablesAccessMap
Expand All @@ -49,13 +52,16 @@
from psyclone.psyir.nodes.reference import Reference
from psyclone.psyir.nodes.routine import Routine
from psyclone.psyir.symbols import (
DataSymbol,
DataType,
DataTypeSymbol,
GenericInterfaceSymbol,
RoutineSymbol,
Symbol,
SymbolError,
UnsupportedFortranType,
DataSymbol,
)
from psyclone.psyir.symbols.datatypes import ArrayType


class CallMatchingArgumentsNotFound(PSycloneError):
Expand Down Expand Up @@ -273,7 +279,8 @@ def replace_named_arg(self, existing_name: str, arg: DataNode):
raise ValueError(
f"The value of the existing_name argument ({existing_name}) "
f"in 'replace_named_arg' in the 'Call' node was not found "
f"in the existing arguments.")
f"in the existing arguments."
)
# The n'th argument is placed at the n'th+1 children position
# because the 1st child is the routine reference
self.children[index + 1] = arg
Expand Down Expand Up @@ -483,8 +490,8 @@ def copy(self):
def get_callees(self) -> List[Routine]:
'''
Searches for the implementation(s) of all potential target routines
for this Call without resolving static polymorphism by checking the
argument types.
for this Call. It does *not* attempt to resolve static polymorphism
by checking the argument types.

:returns: the Routine(s) that this call targets.

Expand Down Expand Up @@ -647,69 +654,111 @@ def get_callees(self) -> List[Routine]:

def _check_argument_type_matches(
self,
call_arg: DataSymbol,
routine_arg: DataSymbol,
call_arg: DataNode,
routine_arg: DataSymbol
) -> None:
"""Return information whether argument types are matching.
This also supports 'optional' arguments by using
"""Checks whether the supplied call and routine arguments are
compatible. This also supports 'optional' arguments by using
partial types.

:param call_arg: One argument of the call
:param routine_arg: One argument of the routine

:raises CallMatchingArgumentsNotFound: Raised if no matching argument
was found.
:raises CallMatchingArgumentsNotFound: if the supplied arguments
do not match.

"""
if isinstance(
routine_arg.datatype, UnsupportedFortranType
):
# This could be an 'optional' argument.
# This has at least a partial data type
if (
call_arg.datatype
!= routine_arg.datatype.partial_datatype
):
def type_symbols_match(type1: Union[DataTypeSymbol, DataType],
type2: Union[DataTypeSymbol, DataType]) -> bool:
'''
:returns: True if the two types correspond to DataTypeSymbols with
the same name (case insensitive), False otherwise.
'''
return (isinstance(type1, DataTypeSymbol) and
isinstance(type2, DataTypeSymbol) and
(type1.name.lower() == type2.name.lower()))

actual_type = call_arg.datatype
dummy_type = routine_arg.datatype
if isinstance(actual_type, ArrayType) and isinstance(dummy_type,
ArrayType):
# Arguments must have the same shape.
if len(actual_type.shape) != len(dummy_type.shape):
call_arg_str = call_arg.debug_string().strip()
routine_arg_str = routine_arg.name
raise CallMatchingArgumentsNotFound(
f"Rank mismatch of call argument '{call_arg_str}' "
f"(rank {len(actual_type.shape)}) and routine argument "
f"'{routine_arg_str}' (rank {len(dummy_type.shape)})")
# Arguments must have the same intrinsic type.
if actual_type.intrinsic != dummy_type.intrinsic:
if type_symbols_match(actual_type.intrinsic,
dummy_type.intrinsic):
return
call_arg_str = call_arg.debug_string().strip()
routine_arg_str = routine_arg.name
raise CallMatchingArgumentsNotFound(
f"Array argument type mismatch of call argument "
f"'{call_arg_str}' ({actual_type.intrinsic}) and routine "
f"argument '{routine_arg_str}' ({dummy_type.intrinsic})")
return

if isinstance(dummy_type, UnsupportedFortranType):
# This could be an 'optional' argument. If so, it will have at
# least a partial datatype which we can check.
if actual_type != dummy_type.partial_datatype:
call_arg_str = call_arg.debug_string().strip()
routine_arg_str = routine_arg.name
raise CallMatchingArgumentsNotFound(
f"Argument partial type mismatch of call "
f"argument '{call_arg}' and routine argument "
f"'{routine_arg}'"
f"Argument partial type mismatch of call argument "
f"'{call_arg_str}' ({actual_type}) and routine "
f"argument '{routine_arg_str}' ("
f"{dummy_type.partial_datatype})"
)
else:
if call_arg.datatype != routine_arg.datatype:
if actual_type != dummy_type:
if type_symbols_match(actual_type, dummy_type):
return
call_arg_str = call_arg.debug_string().strip()
routine_arg_str = routine_arg.name
raise CallMatchingArgumentsNotFound(
f"Argument type mismatch of call argument "
f"'{call_arg}' and routine argument "
f"'{routine_arg}'"
f"Argument type mismatch of call argument '{call_arg_str}'"
f" ({actual_type}) and routine argument "
f"'{routine_arg_str}' ({dummy_type})"
)

def _get_argument_routine_match(self, routine: Routine):
'''Return a list of integers giving for each argument of the call
the index of the corresponding entry in the argument list of the
def get_argument_map(self, routine: Routine) -> List[int]:
'''Return a list of indices mapping from each argument of this
call to the corresponding entry in the argument list of the
supplied routine.

:return: None if no match was found, otherwise list of integers
referring to matching arguments.
:rtype: None|List[int]
'''
:param routine: the target of this Call.

:return: list of integers referring to matching arguments of the
supplied routine.

:raises CallMatchingArgumentsNotFound: If there was some problem in
finding matching arguments.

'''
# Create a copy of the list of actual arguments to the routine.
# Once an argument has been successfully matched, set it to 'None'
routine_argument_list: List[DataSymbol] = (
routine.symbol_table.argument_list[:]
)

if len(self.arguments) > len(routine.symbol_table.argument_list):
call_str = self.debug_string().strip()
raise CallMatchingArgumentsNotFound(
f"More arguments in call ('{self.debug_string()}')"
f"More arguments in call ('{call_str}')"
f" than callee (routine '{routine.name}')"
)

# Iterate over all arguments to the call
ret_arg_idx_list = []
# Iterate over all arguments to the call
for call_arg_idx, call_arg in enumerate(self.arguments):
call_arg_idx: int
call_arg: DataSymbol
call_arg: DataNode

# If the associated name is None, it's a positional argument
# => Just return the index if the types match
Expand Down Expand Up @@ -738,8 +787,11 @@ def _get_argument_routine_match(self, routine: Routine):
if routine_arg is None:
continue

if arg_name == routine_arg.name:
self._check_argument_type_matches(call_arg, routine_arg)
if arg_name.lower() == routine_arg.name.lower():
self._check_argument_type_matches(
call_arg,
routine_arg,
)
ret_arg_idx_list.append(routine_arg_idx)
break

Expand All @@ -764,70 +816,67 @@ def _get_argument_routine_match(self, routine: Routine):
# TODO #759: Optional keyword is not yet supported in psyir.
# Hence, we use a simple string match.
if ", OPTIONAL" not in str(routine_arg.datatype):
call_name = self.debug_string().replace("\n", "")
raise CallMatchingArgumentsNotFound(
f"Argument '{routine_arg.name}' in subroutine"
f" '{routine.name}' does not match any in the call"
f" '{self.debug_string()}' and is not OPTIONAL."
f"Argument '{routine_arg.name}' in subroutine "
f"'{routine.name}' does not match any in the call "
f"'{call_name}' and is not OPTIONAL."
)

return ret_arg_idx_list

def get_callee(
self,
check_matching_arguments: bool = True,
):
self,
use_first_callee_and_no_arg_check: bool = False
) -> Tuple[Routine, List[int]]:
'''
Searches for the implementation(s) of the target routine for this Call
including argument checks.

If `check_matching_arguments` is set to `False`, the very first
implementation of the matching routine will be returned in case no
match was found. Then, the arguments of the call and routine
might not match each other.
.. warning::
If `use_first_callee_and_no_arg_check` is set to True, the very
first implementation of a Routine with a matching name will be
returned. In this case, the arguments of the Call and the Routine
might not match.

:param check_matching_arguments: Also check argument types to match.
If set to `False` and in case it doesn't find matching arguments,
the very first implementation of the matching routine will be
returned (even if the argument type check failed). The argument
types and number of arguments might therefore mismatch!
:type ret_arg_match_list: bool
:param use_first_callee_and_no_arg_check: whether or not (the default)
Comment thread
hiker marked this conversation as resolved.
to just find the first potential callee without checking its
arguments.

:returns: A tuple of two elements. The first element is the routine
that this call targets. The second one a list of arguments
providing the information on matching argument indices.
:rtype: Set[psyclone.psyir.nodes.Routine, List[int]]

:raises NotImplementedError: if the routine is not local and not found
in any containers in scope at the call site.
'''

'''
routine_list = self.get_callees()

if use_first_callee_and_no_arg_check:
arg_match_list = list(i for i in range(
len(routine_list[0].symbol_table.argument_list)))
return (routine_list[0], arg_match_list)

err_info_list = []

# Search for the routine matching the right arguments
for routine in routine_list:
routine: Routine

try:
arg_match_list = self._get_argument_routine_match(routine)
arg_match_list = self.get_argument_map(routine)

except CallMatchingArgumentsNotFound as err:
err_info_list.append(err.value)
continue

return (routine, arg_match_list)

# If we didn't find any routine, return some routine if no matching
# arguments have been found.
# This is handy for the transition phase until optional argument
# matching is supported.
if not check_matching_arguments:
# Also return a list of dummy argument indices
return list(range(len(self.arguments)))

error_msg = "\n".join(err_info_list)

call_str = self.debug_string().replace("\n", "")
raise CallMatchingArgumentsNotFound(
f"No matching routine found for '{self.debug_string()}':"
f"No matching routine found for '{call_str}':"
"\n" + error_msg
)
1 change: 1 addition & 0 deletions src/psyclone/psyir/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
'''

from psyclone.psyir.tools.call_tree_utils import CallTreeUtils
from psyclone.psyir.tools.definition_use_chains import DefinitionUseChain
Comment thread
hiker marked this conversation as resolved.
from psyclone.psyir.tools.dependency_tools import DTCode, DependencyTools
from psyclone.psyir.tools.read_write_info import ReadWriteInfo
from psyclone.psyir.tools.definition_use_chains import DefinitionUseChain
Expand Down
Loading
Loading