Skip to content
Merged
2 changes: 2 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
21) PR #3521 for #3460. Fixes issues with recursive datatype copies.

20) PR #3518 for #3516. Fix issue with disappearing Interface declarations.

19) PR #3515 for #3513. Add a compilation test job with ifx and OpenMP flags.
Expand Down
3 changes: 2 additions & 1 deletion src/psyclone/psyir/symbols/data_type_symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ def copy(self):
:rtype: :py:class:`psyclone.psyir.symbols.TypeSymbol`

'''
copy = type(self)(self.name, self.datatype, visibility=self.visibility,
copy = type(self)(self.name, self.datatype.copy(),
visibility=self.visibility,
interface=self.interface.copy())
copy.preceding_comment = self.preceding_comment
copy.inline_comment = self.inline_comment
Expand Down
9 changes: 6 additions & 3 deletions src/psyclone/psyir/symbols/datasymbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,10 +317,13 @@ def copy(self):
new_init_value = self.initial_value.copy()
else:
new_init_value = None
if self.is_array:
# pylint: disable=import-outside-toplevel
from psyclone.psyir.symbols.datatypes import UnsupportedFortranType
if self.is_array or isinstance(self.datatype, UnsupportedFortranType):
# Ensure any References in the shape definition of an ArrayType
# are also copied. They will still point to the
# same Symbols as the original.
# or in the partial datatype of an UnsupportedFortranType are also
# copied. They will still point to the same Symbols as the
# original.
new_datatype = self.datatype.copy()
else:
new_datatype = self.datatype
Expand Down
36 changes: 28 additions & 8 deletions src/psyclone/psyir/symbols/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,16 +303,32 @@ def replace_symbols_using(self, table_or_symbol):
have matching names. If there is no match for a given Symbol then it
is left unchanged.

This base implementation simply propagates the call to any child Nodes.
A partial datatype that is a DataTypeSymbol is replaced directly. It
must not be traversed because the datatype definition may be recursive.

:param table_or_symbol: the symbol table from which to get replacement
symbols or a single, replacement Symbol.
:type table_or_symbol: :py:class:`psyclone.psyir.symbols.SymbolTable` |
:py:class:`psyclone.psyir.symbols.Symbol`

'''
if self.partial_datatype:
self.partial_datatype.replace_symbols_using(table_or_symbol)
partial_datatype = self.partial_datatype
if not partial_datatype:
return

if not isinstance(partial_datatype, DataTypeSymbol):
partial_datatype.replace_symbols_using(table_or_symbol)
return

if isinstance(table_or_symbol, Symbol):
if table_or_symbol.name.lower() != partial_datatype.name.lower():
return
replacement = table_or_symbol
else:
replacement = table_or_symbol.lookup(
partial_datatype.name, otherwise=partial_datatype)

self._partial_datatype = replacement

@property
def intrinsic(self):
Expand Down Expand Up @@ -1467,16 +1483,20 @@ def replace_symbols_using(self, table_or_symbol):
component.datatype.name, otherwise=component.datatype)

else:
component.datatype.replace_symbols_using(table_or_symbol)
new_type = component.datatype
# Make a copy before updating any Symbol references so that
# replacing them does not modify the original StructureType.
new_type = component.datatype.copy()
new_type.replace_symbols_using(table_or_symbol)

if component.initial_value:
component.initial_value.replace_symbols_using(table_or_symbol)
initial_value = component.initial_value
if initial_value:
initial_value = initial_value.copy()
initial_value.replace_symbols_using(table_or_symbol)

# Construct the new ComponentType
key_name = component.name.lower()
self.add(key_name, new_type, component.visibility,
component.initial_value,
initial_value,
preceding_comment=component.preceding_comment,
inline_comment=component.inline_comment)

Expand Down
32 changes: 3 additions & 29 deletions src/psyclone/psyir/transformations/inline_trans.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
ArrayType,
ScalarType,
DataSymbol,
StructureType,
SymbolError,
UnresolvedType,
UnsupportedType,
Expand Down Expand Up @@ -238,6 +237,9 @@ def apply(self,
f"have been caught by the validate() method. Original error "
f"was {err}") from err

# TODO #3536: A shallow copy will create common subtrees/references
Comment thread
LonelyCat124 marked this conversation as resolved.
# between the inlined and the original.

# Replace any references to formal arguments with copies of the
# actual arguments.
formal_args = routine_table.argument_list
Expand Down Expand Up @@ -275,34 +277,6 @@ def apply(self,
new_shape.append(ArrayType.ArrayBounds(lower, upper))
sym.datatype = ArrayType(sym.datatype.elemental_type, new_shape)

for sym in table.datatypesymbols:
if not isinstance(sym.datatype, StructureType):
continue
for name, ctype in sym.datatype.components.items():
if isinstance(ctype.datatype, ArrayType):
new_shape = []
for dim in ctype.datatype.shape:
lower = self._replace_formal_args_in_expr(
dim.lower, node, formal_args,
routine_node=routine,
use_first_callee_and_no_arg_check=(
use_first_callee_and_no_arg_check),
)
upper = self._replace_formal_args_in_expr(
dim.upper, node, formal_args,
routine_node=routine,
use_first_callee_and_no_arg_check=(
use_first_callee_and_no_arg_check),
)
new_shape.append(ArrayType.ArrayBounds(lower, upper))
sym.datatype.components[name] = (
StructureType.ComponentType(
name=name,
datatype=ArrayType(ctype.datatype.elemental_type,
new_shape),
visibility=ctype.visibility,
initial_value=ctype.initial_value))

# Copy the nodes from the Routine into the call site.
# TODO #924 - while doing this we should ensure that any References
# to common/shared Symbols in the inlined code are updated to point
Expand Down
61 changes: 61 additions & 0 deletions src/psyclone/tests/psyir/symbols/datatype_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1392,3 +1392,64 @@ def test_structuretype___copy__():
# The components should be the same objects
assert copied.components["nancy"] == stype.components["nancy"]
assert copied.components["peggy"] == stype.components["peggy"]


def test_copy_with_recursions(fortran_reader):
'''Test the copy operation when the datatypes contain recursions between
them.'''
code = '''
MODULE recursions
IMPLICIT NONE

TYPE(recursive_type), POINTER :: use_before_decl
TYPE recursive_type
TYPE(recursive_type) :: supported
TYPE(recursive_type), POINTER :: unsupported
END TYPE recursive_type
TYPE(recursive_type), POINTER :: use_after_decl

CONTAINS
SUBROUTINE mysub(arg)
TYPE(recursive_type), POINTER, INTENT(inout) :: arg
END SUBROUTINE mysub

END MODULE recursions
'''
psyir = fortran_reader.psyir_from_source(code)
copiedtree = psyir.copy()

original_type = psyir.children[0].symbol_table.lookup("recursive_type")
copied_type = copiedtree.children[0].symbol_table.lookup("recursive_type")
assert copied_type is not original_type

# Both components of the copied type must refer to the copied type symbol,
# and the equivalent components in the original must remain unchanged.
original_supported = original_type.datatype.lookup("supported").datatype
copied_supported = copied_type.datatype.lookup("supported").datatype
assert original_supported is original_type
assert copied_supported is copied_type

original_unsupported = original_type.datatype.lookup(
"unsupported").datatype.partial_datatype
copied_unsupported = copied_type.datatype.lookup(
"unsupported").datatype.partial_datatype
assert original_unsupported is original_type
assert copied_unsupported is copied_type

# Symbols declared both before and after the type definition must refer to
# the type symbol belonging to their respective trees.
for name in ["use_before_decl", "use_after_decl"]:
original_symbol = psyir.children[0].symbol_table.lookup(name)
copied_symbol = copiedtree.children[0].symbol_table.lookup(name)
assert copied_symbol is not original_symbol
assert copied_symbol.datatype is not original_symbol.datatype
assert original_symbol.datatype.partial_datatype is original_type
assert copied_symbol.datatype.partial_datatype is copied_type

# The routine arguments must similarly refer to the type symbol belonging
# to their respective trees.
original_arg = psyir.walk(Routine)[0].symbol_table.lookup("arg")
copied_arg = copiedtree.walk(Routine)[0].symbol_table.lookup("arg")
assert copied_arg is not original_arg
assert original_arg.datatype.partial_datatype is original_type
assert copied_arg.datatype.partial_datatype is copied_type
47 changes: 32 additions & 15 deletions src/psyclone/tests/psyir/transformations/inline_trans_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@
from psyclone.psyir.backend.fortran import FortranWriter
from psyclone.psyir.nodes import (
Assignment, Call, IntrinsicCall, Loop, Node, Reference,
Routine, Statement)
Routine, Statement, Literal)
from psyclone.psyir.symbols import (
AutomaticInterface, DataSymbol, ImportInterface, UnresolvedType)
AutomaticInterface, DataSymbol, ImportInterface, UnresolvedType,
ScalarType)
from psyclone.psyir.transformations import (
InlineTrans, TransformationError)
from psyclone.tests.utilities import Compile, get_invoke
Expand Down Expand Up @@ -2812,21 +2813,20 @@ def test_apply_symbol_dependencies(fortran_reader, fortran_writer, tmp_path):
when inlined.

'''
# TODO #3534: Add an example of a len expression in an argument decalration
code = (
"module test_mod\n"
" integer, parameter :: N = 10\n"
"contains\n"
"subroutine main()\n"
" real, dimension(10, 10) :: var = 0.0\n"
" real, dimension(N+10, 10) :: var = 0.0\n"
" call sub(var, 10)\n"
"end subroutine main\n"
"subroutine sub(x, ilen)\n"
" integer, intent(in) :: ilen\n"
" real, dimension(ilen, ilen), intent(inout) :: x\n"
" real, dimension(ilen, ilen) :: work\n"
" type nasty\n"
" integer, dimension(ilen+1) :: flag\n"
" end type nasty\n"
" type(nasty) :: oh_deary_me\n"
" real, dimension(N+ilen, ilen), intent(inout) :: x\n"
" real, dimension(N+ilen, ilen) :: work\n"
" character(len=3) :: string_work\n"
" work = 2.0\n"
" x(:,:) = x(:,:) + work(:,:)\n"
"end subroutine sub\n"
Expand All @@ -2836,17 +2836,34 @@ def test_apply_symbol_dependencies(fortran_reader, fortran_writer, tmp_path):
call = psyir.walk(Call)[0]
inline_trans = InlineTrans()
inline_trans.apply(call)

# ilen should not be in the caller
main = psyir.children[0].find_routine_psyir("main")
assert "ilen" not in main.symbol_table
main_output = fortran_writer(main)
assert "real, dimension(n + 10,10) :: work" in main_output

# The the original should be unmodified
original = psyir.children[0].find_routine_psyir("sub")
original_output = fortran_writer(original)
assert "real, dimension(n + ilen,ilen) :: work" in original_output

# The resulting code must be valid Fortran
output = fortran_writer(psyir)
assert '''\
type :: nasty
integer, dimension(10 + 1) :: flag
end type nasty''' in output
assert "real, dimension(10,10) :: work" in output
assert "type(nasty) :: oh_deary_me" in output
assert Compile(tmp_path).string_compiles(output)

# After inlining whe should be able to modify them independently
str_work = main.symbol_table.lookup("string_work")
str_work.datatype.length = Literal("1", ScalarType.integer_type())
main_output = fortran_writer(main)
original_output = fortran_writer(original)
assert "character(len=1) :: string_work" in main_output

# The original should be (len=3)
assert "character(len=1) :: string_work" in original_output
pytest.xfail("#3536: After inlining symbols should be independent"
"copies")


def test_apply_array_access_check_unresolved_override_option(
fortran_reader):
Expand Down
Loading