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. diff --git a/src/psyclone/psyir/nodes/call.py b/src/psyclone/psyir/nodes/call.py index 6ed3653f30..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 @@ -726,8 +729,21 @@ 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]) + # 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) + # 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 b665672cbb..b965e7c149 100644 --- a/src/psyclone/tests/psyir/nodes/call_test.py +++ b/src/psyclone/tests/psyir/nodes/call_test.py @@ -1435,6 +1435,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): @@ -1915,12 +1971,15 @@ 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], - DataSymbol("dummy1", ArrayType(ScalarType.real_type(), - shape=[10, 10]))) + DataSymbol("dummy1", + ArrayType(ScalarType.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. @@ -1961,13 +2020,16 @@ 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], 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 f305374efc..63e03d88f6 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().)