From b2a4ea0b160dbd3e6c9774b0256b5e35383c0573 Mon Sep 17 00:00:00 2001 From: Matthew Naylor Date: Tue, 26 May 2026 13:33:54 +0100 Subject: [PATCH 1/4] Permit implicit reshaping in `get_callee()` --- src/psyclone/psyir/nodes/call.py | 14 +++- src/psyclone/tests/psyir/nodes/call_test.py | 64 ++++++++++++++++++- .../transformations/inline_trans_test.py | 8 +-- 3 files changed, 78 insertions(+), 8 deletions(-) diff --git a/src/psyclone/psyir/nodes/call.py b/src/psyclone/psyir/nodes/call.py index 6ed3653f30..906fd3f521 100644 --- a/src/psyclone/psyir/nodes/call.py +++ b/src/psyclone/psyir/nodes/call.py @@ -726,8 +726,18 @@ def type_symbols_match(type1: Union[DataTypeSymbol, 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): + # Is the dummy argument an explicit-shape array? + has_explicit_shape = all([ + isinstance(dim, ArrayType.ArrayBounds) and + dim.lower is not ArrayType.Extent.ATTRIBUTE and + dim.upper is not ArrayType.Extent.ATTRIBUTE + for dim in dummy_type.shape]) + # Do arguments need to have the same rank? + match_rank = (isinstance(self.routine.symbol, + GenericInterfaceSymbol) or + not has_explicit_shape) + # Check that ranks of arguments match, if necessary + if match_rank and len(actual_type.shape) != len(dummy_type.shape): call_arg_str = call_arg.debug_string().strip() routine_arg_str = routine_arg.name raise CallMatchingArgumentsNotFound( diff --git a/src/psyclone/tests/psyir/nodes/call_test.py b/src/psyclone/tests/psyir/nodes/call_test.py index 747b52f7d2..0358682b9a 100644 --- a/src/psyclone/tests/psyir/nodes/call_test.py +++ b/src/psyclone/tests/psyir/nodes/call_test.py @@ -1431,6 +1431,62 @@ def test_call_get_callee_8_arguments_not_handled(fortran_reader): assert "No matching routine found for 'call foo(e, f)" in str(err.value) +def test_call_get_callee_9_implicit_reshaping(fortran_reader): + ''' + Check that implicit reshaping of array arguments is permitted + when the dummy argument is an explicit-shape array, provided + that the call is not a call to an interface. + ''' + code = ''' +module implicit_reshaping_test + implicit none + + interface ifc + module procedure sub1 + end interface +contains + subroutine top(n) + integer, intent(in) :: n + real :: mat(n, n) + call ifc(mat) + call sub1(mat) + call sub2(mat) + end subroutine + + subroutine sub1(arr) + real, intent(in) :: arr(100) + print *, "Called sub1" + end subroutine + + subroutine sub2(arr) + real, intent(in) :: arr(1:) + print *, "Called sub2" + end subroutine +end module +''' + psyir: Node = fortran_reader.psyir_from_source(code) + routine_top: Routine = psyir.walk(Routine)[0] + assert routine_top.name == "top" + + # Call to 'ifc' should not permit implicit reshaping + call_ifc: Call = routine_top.walk(Call)[0] + with pytest.raises(CallMatchingArgumentsNotFound) as err: + call_ifc.get_callee() + assert "No matching routine found for" in str(err.value) + + # Call to 'sub1' should permit implicit reshaping + call_sub1: Call = routine_top.walk(Call)[1] + (result, _) = call_sub1.get_callee() + sub1_match: Routine = psyir.walk(Routine)[1] + assert result is sub1_match + + # Call to 'sub2' should not permit implicit reshaping + call_sub2: Call = routine_top.walk(Call)[2] + with pytest.raises(CallMatchingArgumentsNotFound) as err: + call_sub2.get_callee() + assert "No matching routine found for" in str(err.value) + + @pytest.mark.usefixtures("clear_module_manager_instance") def test_call_get_callees_unresolved(fortran_reader, tmpdir, monkeypatch, config_instance): @@ -1913,7 +1969,10 @@ def test_check_argument_type_matches(fortran_reader): with pytest.raises(CallMatchingArgumentsNotFound) as err: call2._check_argument_type_matches( call2.arguments[0], - DataSymbol("dummy1", ArrayType(REAL_TYPE, shape=[10, 10]))) + DataSymbol("dummy1", + ArrayType(REAL_TYPE, + shape=[ArrayType.Extent.ATTRIBUTE, + ArrayType.Extent.ATTRIBUTE]))) assert ("Rank mismatch of call argument 'var2' (rank 1) and routine " "argument 'dummy1' (rank 2)" in str(err.value)) # Array of wrong intrinsic type. @@ -1958,7 +2017,8 @@ def test_check_argument_type_matches(fortran_reader): call4.arguments[0], DataSymbol("dummy1", ArrayType(DataTypeSymbol("MY_type", UnresolvedType()), - shape=[3, 4]))) + shape=[ArrayType.Extent.ATTRIBUTE, + ArrayType.Extent.ATTRIBUTE]))) assert ("Rank mismatch of call argument 'athing' (rank 1) and routine " "argument 'dummy1' (rank 2)" in str(err.value)) # Doesn't match with array of a different derived type. diff --git a/src/psyclone/tests/psyir/transformations/inline_trans_test.py b/src/psyclone/tests/psyir/transformations/inline_trans_test.py index cda89d5229..6062b2b358 100644 --- a/src/psyclone/tests/psyir/transformations/inline_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/inline_trans_test.py @@ -2213,10 +2213,10 @@ def test_validate_array_reshape(fortran_reader): inline_trans = InlineTrans() with pytest.raises(TransformationError) as err: inline_trans.validate(call) - assert ("Cannot inline routine 's' because the target of the call cannot " - "be found:" in str(err.value)) - assert ("Rank mismatch of call argument 'a(:,:)' (rank 2) and routine " - "argument 'x' (rank 1)" in str(err.value)) + assert ("Cannot inline routine 's' because it reshapes an argument" + in str(err.value)) + assert ("actual argument 'a(:,:)' has rank 2 but the corresponding " + "formal argument, 'x', has rank 1" in str(err.value)) # Check that _validate_inline_of_call_and_routine_argument_pairs() also # catches this error. (Necessary in case type-checking has been disabled # in the call to get_callee().) From 1265a6299b702c3bbe365bca7156d685ced1a8c1 Mon Sep 17 00:00:00 2001 From: Matthew Naylor Date: Thu, 11 Jun 2026 10:52:43 +0100 Subject: [PATCH 2/4] Add comments about implicit reshaping in argument matching --- src/psyclone/psyir/nodes/call.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/psyclone/psyir/nodes/call.py b/src/psyclone/psyir/nodes/call.py index 906fd3f521..23f2013f36 100644 --- a/src/psyclone/psyir/nodes/call.py +++ b/src/psyclone/psyir/nodes/call.py @@ -703,7 +703,10 @@ def _check_argument_type_matches( ) -> None: """Checks whether the supplied call and routine arguments are compatible. This also supports 'optional' arguments by using - partial types. + partial types. Array arguments are only required to have the same + rank if we are dealing with an interface call (polymorphism) or + the dummy argument does not have an explicit shape (in which + case Fortran permits implicit reshaping). :param call_arg: One argument of the call :param routine_arg: One argument of the routine @@ -732,7 +735,10 @@ def type_symbols_match(type1: Union[DataTypeSymbol, DataType], dim.lower is not ArrayType.Extent.ATTRIBUTE and dim.upper is not ArrayType.Extent.ATTRIBUTE for dim in dummy_type.shape]) - # Do arguments need to have the same rank? + # Arguments are only required to have the same rank if we are + # dealing with an interface call (polymorphism) or the dummy + # argument does not have an explicit shape (in which case + # Fortran permits implicit reshaping) match_rank = (isinstance(self.routine.symbol, GenericInterfaceSymbol) or not has_explicit_shape) From e4c6dc4c2199a837d4891525619a14ede5dbc0e6 Mon Sep 17 00:00:00 2001 From: Matthew Naylor Date: Thu, 11 Jun 2026 10:54:54 +0100 Subject: [PATCH 3/4] Add comments about implicit reshaping to PSyIR `Call` tests --- src/psyclone/tests/psyir/nodes/call_test.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/psyclone/tests/psyir/nodes/call_test.py b/src/psyclone/tests/psyir/nodes/call_test.py index 67f367ba18..b965e7c149 100644 --- a/src/psyclone/tests/psyir/nodes/call_test.py +++ b/src/psyclone/tests/psyir/nodes/call_test.py @@ -1971,7 +1971,8 @@ def test_check_argument_type_matches(fortran_reader): call2._check_argument_type_matches( call2.arguments[0], DataSymbol("dummy1", ArrayType(ScalarType.real_type(), shape=[10]))) - # Array of wrong rank. + # Array of wrong rank. Implicit reshaping is not permitted because + # the dummy argument is not an explicit-shape array. with pytest.raises(CallMatchingArgumentsNotFound) as err: call2._check_argument_type_matches( call2.arguments[0], @@ -2019,7 +2020,9 @@ def test_check_argument_type_matches(fortran_reader): assert ("Argument type mismatch of call argument 'athing' (Array) and routine argument 'dummy1' " "(MY_type: DataTypeSymbol)" in str(err.value)) - # Doesn't match with array of derived type with wrong rank. + # Doesn't match with array of derived type with wrong rank. Implicit + # reshaping is not permitted because the dummy argument is not an + # explicit-shape array. with pytest.raises(CallMatchingArgumentsNotFound) as err: call4._check_argument_type_matches( call4.arguments[0], From b0507f1f1d7a2bed853b04519c9b53adba0c8d6f Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Fri, 12 Jun 2026 20:49:30 +0100 Subject: [PATCH 4/4] #3442 update changelog --- changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog b/changelog index 55aa1b64fd..a713b06ab2 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,6 @@ + 31) PR #3442. Extends Call.get_callee() to permit implicit reshaping + of arguments. + 30) PR #3451 towards #3449. Adds support for keeping 'ACC routine' and 'OMP declare target' directives in existing code.