diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index 743352628f..44e759e19d 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -600,6 +600,11 @@ def gen_vardecl(self, symbol, include_visibility=False): f"A Symbol must be either public or private but symbol " f"'{symbol.name}' has visibility '{symbol.visibility}'") + if symbol.datatype.is_pointer: + result += ", pointer" + elif symbol.datatype.is_target: + result += ", target" + # Specify name result += f" :: {symbol.name}" @@ -614,7 +619,10 @@ def gen_vardecl(self, symbol, include_visibility=False): f"value ({self._visit(symbol.initial_value)}) and " f"therefore (in Fortran) must have a StaticInterface. " f"However it has an interface of '{symbol.interface}'.") - result += " = " + self._visit(symbol.initial_value) + if symbol.is_pointer: + result += " => " + self._visit(symbol.initial_value) + else: + result += " = " + self._visit(symbol.initial_value) return result + "\n" diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py index c6855064cf..f6b9d2d42e 100644 --- a/src/psyclone/psyir/frontend/fparser2.py +++ b/src/psyclone/psyir/frontend/fparser2.py @@ -1684,10 +1684,21 @@ def _process_decln(self, scope, symbol_table, decl, visibility_map=None, decln_access_spec = None # 6) Whether this declaration has the SAVE attribute. has_save_attr = False + # 7) Whether this declaration has the POINTER or TARGET attribute. + has_pointer_attr = False + has_target_attr = False if attr_specs: for attr in attr_specs.items: - if isinstance(attr, (Fortran2003.Attr_Spec, - Fortran2003.Component_Attr_Spec)): + # NOTE: for a routine declaration, 'POINTER' or 'TARGET' is + # an Attr_Spec, but in a derived type declaration component + # it is not. + normalized_string = str(attr).lower().replace(' ', '') + if normalized_string == "pointer": + has_pointer_attr = True + elif normalized_string == "target": + has_target_attr = True + elif isinstance(attr, (Fortran2003.Attr_Spec, + Fortran2003.Component_Attr_Spec)): normalized_string = str(attr).lower().replace(' ', '') if normalized_string == "save": if interface is not None: @@ -1730,8 +1741,9 @@ def _process_decln(self, scope, symbol_table, decl, visibility_map=None, f"{err.value}") from err else: raise NotImplementedError( - f"Could not process declaration '{decl}'. Unrecognised" - f" attribute type '{type(attr).__name__}'.") + f"Could not process declaration '{decl}'. " + f"Unrecognised attribute type " + f"'{type(attr).__name__}'.") # There are some combinations of attributes that are not valid # Fortran but fparser does not check, so we need to check for them @@ -1742,6 +1754,16 @@ def _process_decln(self, scope, symbol_table, decl, visibility_map=None, f"SAVE and PARAMETER attributes are not compatible but " f"found:\n {decl}") + # TODO check these are needed + if has_pointer_attr and has_target_attr: + raise GenerationError( + f"POINTER and TARGET attributes are not compatible but " + f"found:\n {decl}") + if has_pointer_attr and has_constant_value: + raise GenerationError( + f"POINTER and PARAMETER attributes are not compatible but " + f"found:\n {decl}") + # Now we've checked for save and parameter existing # together, we can allow parameter without save and set it # to the same interface as save. @@ -1753,6 +1775,10 @@ def _process_decln(self, scope, symbol_table, decl, visibility_map=None, raise GenerationError( f"ALLOCATABLE and PARAMETER attributes are not compatible " f"but found:\n {decl}") + if allocatable and has_pointer_attr: + raise GenerationError( + f"ALLOCATABLE and POINTER attributes are not compatible " + f"but found:\n {decl}") if isinstance(interface, ArgumentInterface) and has_constant_value: raise GenerationError( f"INTENT and PARAMETER attributes are not compatible but" @@ -1845,9 +1871,13 @@ def _process_decln(self, scope, symbol_table, decl, visibility_map=None, if entity_shape: # array - datatype = ArrayType(base_type, entity_shape) + datatype = ArrayType(base_type, entity_shape, + is_pointer=has_pointer_attr, + is_target=has_target_attr) else: # scalar + base_type._is_pointer = has_pointer_attr + base_type._is_target = has_target_attr datatype = base_type # Make sure the declared symbol exists in the SymbolTable. @@ -2060,7 +2090,7 @@ def _get_partial_datatype(self, node, scope, visibility_map): orig_entity_decl_children = list(entity_decl.children[:]) # 2: Remove any unsupported attributes - unsupported_attribute_names = ["pointer", "target", "optional"] + unsupported_attribute_names = ["asynchronous", "volatile", "optional"] attr_spec_list = node.children[1] orig_node_children = list(node.children[:]) orig_attr_spec_list_children = (list(node.children[1].children[:]) diff --git a/src/psyclone/psyir/symbols/data_type_symbol.py b/src/psyclone/psyir/symbols/data_type_symbol.py index 92df96eb9c..d3f48596b5 100644 --- a/src/psyclone/psyir/symbols/data_type_symbol.py +++ b/src/psyclone/psyir/symbols/data_type_symbol.py @@ -52,17 +52,35 @@ class DataTypeSymbol(Symbol): :type visibility: :py:class:`psyclone.psyir.symbols.Symbol.Visibility` :param interface: the interface to this symbol. :type interface: :py:class:`psyclone.psyir.symbols.SymbolInterface` + :param is_pointer: whether this is a pointer symbol. + :type is_pointer: bool + :param is_target: whether this is a target symbol. + :type is_target: bool ''' def __init__(self, name, datatype, visibility=Symbol.DEFAULT_VISIBILITY, - interface=None): + interface=None, + is_pointer=False, + is_target=False): + if not isinstance(is_pointer, bool): + raise TypeError(f"is_pointer should be a boolean but found " + f"'{type(is_pointer).__name__}'.") + if not isinstance(is_target, bool): + raise TypeError(f"is_target should be a boolean but found " + f"'{type(is_target).__name__}'.") + if is_pointer and is_target: + raise ValueError("A symbol cannot be both a pointer and a target.") + super(DataTypeSymbol, self).__init__(name, visibility, interface) # The following attribute has a setter method (with error checking) self._datatype = None self.datatype = datatype + self._is_pointer = is_pointer + self._is_target = is_target + def copy(self): '''Create and return a copy of this object. Any references to the original will not be affected so the copy will not be referred @@ -74,7 +92,9 @@ def copy(self): ''' return type(self)(self.name, self.datatype, visibility=self.visibility, - interface=self.interface.copy()) + interface=self.interface.copy(), + is_pointer=self.is_pointer, + is_target=self.is_target) def __str__(self): return f"{self.name}: {type(self).__name__}" @@ -123,6 +143,24 @@ def copy_properties(self, symbol_in): f"found '{type(symbol_in).__name__}'.") super(DataTypeSymbol, self).copy_properties(symbol_in) self._datatype = symbol_in.datatype + self._is_pointer = symbol_in.is_pointer + self._is_target = symbol_in.is_target + + @property + def is_pointer(self): + ''' + :returns: True if this symbol is a pointer. + :rtype: bool + ''' + return self._is_pointer + + @property + def is_target(self): + ''' + :returns: True if this symbol is a target. + :rtype: bool + ''' + return self._is_target # For automatic documentation generation diff --git a/src/psyclone/psyir/symbols/datasymbol.py b/src/psyclone/psyir/symbols/datasymbol.py index c1ddb9e20d..27aff694b7 100644 --- a/src/psyclone/psyir/symbols/datasymbol.py +++ b/src/psyclone/psyir/symbols/datasymbol.py @@ -92,7 +92,7 @@ def _process_arguments(self, **kwargs): intrinsic types available in the TYPE_MAP_TO_PYTHON map. By default it is None.\n :type initial_value: Optional[item of TYPE_MAP_TO_PYTHON | - :py:class:`psyclone.psyir.nodes.Node`]\n + :py:class:`psyclone.psyir.nodes.Node`]\n and the arguments in :py:class:`psyclone.psyir.symbols.TypedSymbol` :type kwargs: unwrapped dict. @@ -357,3 +357,16 @@ def replace_symbols_using(self, table): # Ensure any Symbols referenced in the initial value are updated. if self.initial_value: self.initial_value.replace_symbols_using(table) + + @property + def is_unresolved(self): + ''' + :returns: whether the Symbol has an UnresolvedInterface or its + datatype is an UnresolvedType. + :rtype: bool + ''' + # Import here to avoid circular dependencies + # pylint: disable=import-outside-toplevel + from psyclone.psyir.symbols.datatypes import UnresolvedType + return (super().is_unresolved + or isinstance(self.datatype, UnresolvedType)) diff --git a/src/psyclone/psyir/symbols/datatypes.py b/src/psyclone/psyir/symbols/datatypes.py index ed9a635413..b677c2ef11 100644 --- a/src/psyclone/psyir/symbols/datatypes.py +++ b/src/psyclone/psyir/symbols/datatypes.py @@ -51,7 +51,26 @@ class DataType(metaclass=abc.ABCMeta): - '''Abstract base class from which all types are derived.''' + '''Abstract base class from which all types are derived. + + :param bool is_pointer: whether this datatype is a pointer. + :param bool is_target: whether this datatype is a target. + + :raises TypeError: if is_pointer or is_target are not of type bool. + :raises ValueError: if is_pointer and is_target are both True. + ''' + def __init__(self, is_pointer=False, is_target=False): + if not isinstance(is_pointer, bool): + raise TypeError(f"Expected 'is_pointer' to be a bool but got " + f"'{type(is_pointer).__name__}'") + if not isinstance(is_target, bool): + raise TypeError(f"Expected 'is_target' to be a bool but got " + f"'{type(is_target).__name__}'") + if is_pointer and is_target: + raise ValueError("A DataType cannot be both a pointer and a " + "target.") + self._is_pointer = is_pointer + self._is_target = is_target @abc.abstractmethod def __str__(self): @@ -68,7 +87,25 @@ def __eq__(self, other): :returns: whether this type is equal to the 'other' type. :rtype: bool ''' - return type(other) is type(self) + return (type(other) is type(self) + and self.is_pointer == other.is_pointer + and self.is_target == other.is_target) + + @property + def is_pointer(self): + ''' + :returns: whether this datatype is a pointer. + :rtype: bool + ''' + return self._is_pointer + + @property + def is_target(self): + ''' + :returns: whether this datatype is a target. + :rtype: bool + ''' + return self._is_target def copy(self): ''' @@ -118,7 +155,8 @@ class UnsupportedType(DataType, metaclass=abc.ABCMeta): :raises TypeError: if the supplied declaration_txt is not a str. ''' - def __init__(self, declaration_txt): + def __init__(self, declaration_txt, is_pointer=False, is_target=False): + super().__init__(is_pointer, is_target) if not isinstance(declaration_txt, str): raise TypeError( f"UnsupportedType constructor expects the original variable " @@ -155,8 +193,9 @@ class UnsupportedFortranType(UnsupportedType): :py:class:`psyclone.psyir.symbols.DataTypeSymbol`] ''' - def __init__(self, declaration_txt, partial_datatype=None): - super().__init__(declaration_txt) + def __init__(self, declaration_txt, partial_datatype=None, + is_pointer=False, is_target=False): + super().__init__(declaration_txt, is_pointer, is_target) # This will hold the Fortran type specification (as opposed to # the whole declaration). self._type_text = "" @@ -331,7 +370,9 @@ class Precision(Enum): Intrinsic.BOOLEAN: bool, Intrinsic.REAL: float} - def __init__(self, intrinsic, precision): + def __init__(self, intrinsic, precision, is_pointer=False, + is_target=False): + super().__init__(is_pointer, is_target) if not isinstance(intrinsic, ScalarType.Intrinsic): raise TypeError( f"ScalarType expected 'intrinsic' argument to be of type " @@ -498,7 +539,8 @@ class ArrayBounds: lower: Any upper: Any - def __init__(self, datatype, shape): + def __init__(self, datatype, shape, is_pointer=False, is_target=False): + super().__init__(is_pointer, is_target) # This import must be placed here to avoid circular dependencies. # pylint: disable-next=import-outside-toplevel @@ -849,7 +891,9 @@ def copy(self): # This dimension is specified with an ArrayType.Extent # so no need to copy. new_shape.append(dim) - return ArrayType(self.datatype, new_shape) + array_copy = ArrayType(self.datatype, new_shape, self.is_pointer, + self.is_target) + return array_copy def replace_symbols_using(self, table): ''' @@ -926,6 +970,7 @@ class ComponentType: initial_value: Any def __init__(self): + super().__init__(is_pointer=False, is_target=False) self._components = OrderedDict() def __str__(self): @@ -943,7 +988,7 @@ def create(components): :py:class:`psyclone.psyir.symbols.DataType` | :py:class:`psyclone.psyir.symbols.DataTypeSymbol`, :py:class:`psyclone.psyir.symbols.Symbol.Visibility`, - Optional[:py:class:`psyclone.psyir.symbols.DataNode`] + Optional[:py:class:`psyclone.psyir.symbols.DataNode`], ]] :returns: the new type object. @@ -973,7 +1018,8 @@ def add(self, name, datatype, visibility, initial_value): Create a component with the supplied attributes and add it to this StructureType. - :param str name: the name of the new component. + :param str name: the name of the new component., is_pointer=False, + is_target=False :param datatype: the type of the new component. :type datatype: :py:class:`psyclone.psyir.symbols.DataType` | :py:class:`psyclone.psyir.symbols.DataTypeSymbol` diff --git a/src/psyclone/psyir/symbols/typed_symbol.py b/src/psyclone/psyir/symbols/typed_symbol.py index 89c0c9dd1d..2fdfbbd679 100644 --- a/src/psyclone/psyir/symbols/typed_symbol.py +++ b/src/psyclone/psyir/symbols/typed_symbol.py @@ -62,6 +62,43 @@ def __init__(self, name, datatype, **kwargs): super(TypedSymbol, self).__init__(name) self._process_arguments(datatype=datatype, **kwargs) + @property + def is_unsupported(self): + ''' + :returns: whether the datatype of the Symbol is an UnsupportedType. + :rtype: bool + ''' + # Import here to avoid circular dependencies + # pylint: disable=import-outside-toplevel + from psyclone.psyir.symbols.datatypes import UnsupportedType + return isinstance(self.datatype, UnsupportedType) + + @property + def is_potentially_aliased(self): + ''' + :returns: whether the Symbol is potentially aliased, i.e. it is a \ + pointer, target, unresolved or unsupported. + :rtype: bool + ''' + return (self.datatype.is_pointer or self.datatype.is_target + or self.is_unresolved or self.is_unsupported) + + @property + def is_pointer(self): + ''' + :returns: whether the Symbol is a pointer. + :rtype: bool + ''' + return self.datatype.is_pointer + + @property + def is_target(self): + ''' + :returns: whether the Symbol is a target. + :rtype: bool + ''' + return self.datatype.is_target + def _process_arguments(self, **kwargs): ''' Process the arguments for the constructor and the specialise methods. In this case the datatype argument. diff --git a/src/psyclone/psyir/transformations/intrinsics/matmul2code_trans.py b/src/psyclone/psyir/transformations/intrinsics/matmul2code_trans.py index 29fe2f8363..b52ab22431 100644 --- a/src/psyclone/psyir/transformations/intrinsics/matmul2code_trans.py +++ b/src/psyclone/psyir/transformations/intrinsics/matmul2code_trans.py @@ -285,7 +285,9 @@ def validate(self, node, options=None): operation is not an assignment. :raises TransformationError: if the matmul arguments are not in the required form. - :raises TransformationError: if sub-sections of an array are present + :raises TransformationError: if any of the arrays are potentially \ + aliased. + :raises TransformationError: if sub-sections of an array are present \ in the arguments. ''' @@ -331,6 +333,15 @@ def validate(self, node, options=None): f"be references to arrays but found '{result.symbol}', " f"'{matrix1.symbol}' and '{matrix2.symbol}'.") + # The arrays must not be potentially aliased + if any(var.symbol.is_potentially_aliased for var in + [matrix1, matrix2, result]): + raise TransformationError( + f"Expected result and operands of MATMUL IntrinsicCall to " + f"be references to arrays that are not potentially aliased " + f"but found '{result.symbol}', '{matrix1.symbol}' and " + f"'{matrix2.symbol}'.") + # The first child (matrix1) should be declared as an array # with at least 2 dimensions. if len(matrix1.symbol.shape) < 2: diff --git a/src/psyclone/tests/domain/gocean/transformations/gocean1p0_transformations_test.py b/src/psyclone/tests/domain/gocean/transformations/gocean1p0_transformations_test.py index a9542e136a..4f6f6741d6 100644 --- a/src/psyclone/tests/domain/gocean/transformations/gocean1p0_transformations_test.py +++ b/src/psyclone/tests/domain/gocean/transformations/gocean1p0_transformations_test.py @@ -1328,7 +1328,7 @@ def test_acc_enter_directive_infrastructure_setup(): USE iso_c_binding, ONLY: c_ptr USE kind_params_mod, ONLY: go_wp TYPE(c_ptr), intent(in) :: from - REAL(KIND=go_wp), DIMENSION(:, :), INTENT(INOUT), TARGET :: to + REAL(KIND=go_wp), dimension(:,:), intent(inout), target :: to INTEGER, intent(in) :: startx INTEGER, intent(in) :: starty INTEGER, intent(in) :: nx diff --git a/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py b/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py index 2e0306e67d..121abf0893 100644 --- a/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py +++ b/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py @@ -337,7 +337,7 @@ def test_invoke_opencl_initialisation_grid(): use ocl_utils_mod, only: check_status type(r2d_field), intent(inout), target :: field integer(kind=c_size_t) size_in_bytes - integer(kind=c_intptr_t), pointer :: cmd_queues(:) + integer(kind=c_intptr_t), dimension(:), pointer :: cmd_queues integer(kind=c_intptr_t) cl_mem integer ierr @@ -430,7 +430,7 @@ def test_opencl_routines_initialisation(kernel_outputdir): use clfortran use fortcl, only: get_cmd_queues type(c_ptr), intent(in) :: from - real(kind=go_wp), intent(inout), dimension(:, :), target :: to + real(kind=go_wp), dimension(:,:), intent(inout), target :: to integer, intent(in) :: startx integer, intent(in) :: starty integer, intent(in) :: nx @@ -439,7 +439,7 @@ def test_opencl_routines_initialisation(kernel_outputdir): integer(kind=c_size_t) size_in_bytes integer(kind=c_size_t) offset_in_bytes integer(kind=c_intptr_t) cl_mem - integer(kind=c_intptr_t), pointer :: cmd_queues(:) + integer(kind=c_intptr_t), dimension(:), pointer :: cmd_queues integer ierr integer i @@ -477,7 +477,7 @@ def test_opencl_routines_initialisation(kernel_outputdir): use kind_params_mod, only: go_wp use clfortran use fortcl, only: get_cmd_queues - real(kind=go_wp), intent(in), dimension(:, :), target :: from + real(kind=go_wp), dimension(:,:), intent(in), target :: from type(c_ptr), intent(in) :: to integer, intent(in) :: startx integer, intent(in) :: starty @@ -487,7 +487,7 @@ def test_opencl_routines_initialisation(kernel_outputdir): integer(kind=c_intptr_t) cl_mem integer(kind=c_size_t) size_in_bytes integer(kind=c_size_t) offset_in_bytes - integer(kind=c_intptr_t), pointer :: cmd_queues(:) + integer(kind=c_intptr_t), dimension(:), pointer :: cmd_queues integer ierr integer i diff --git a/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py b/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py index 3961dd42ba..5eaf99165e 100644 --- a/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py @@ -47,7 +47,7 @@ RoutineSymbol, ScalarType, Symbol, SymbolTable, UnresolvedType, StructureType, ImportInterface, UnresolvedInterface, ArgumentInterface, INTEGER_TYPE, REAL_TYPE, StaticInterface, PreprocessorInterface, - CHARACTER_TYPE) + CHARACTER_TYPE, ArrayType) def test_gen_param_decls_dependencies(fortran_writer): @@ -209,6 +209,65 @@ def test_gen_decls(fortran_writer): "'unknown'" in str(excinfo.value)) +def test_gen_decls_pointer_target(fortran_writer): + '''Test that the gen_decls method correctly handles symbols with + is_pointer=True xor is_target=True attributes in their datatypes. + ''' + symbol_table = SymbolTable() + + # Test with is_pointer=True on a scalar + int_ptr_type = ScalarType(ScalarType.Intrinsic.INTEGER, + ScalarType.Precision.UNDEFINED, + is_pointer=True) + pointer_symbol = DataSymbol("ptr_var", int_ptr_type) + symbol_table.add(pointer_symbol) + result = fortran_writer.gen_decls(symbol_table) + assert "integer, pointer :: ptr_var" in result + + # Test with is_target=True on a scalar + int_tgt_type = ScalarType(ScalarType.Intrinsic.INTEGER, + ScalarType.Precision.UNDEFINED, + is_target=True) + target_symbol = DataSymbol("tgt_var", int_tgt_type) + symbol_table.add(target_symbol) + result = fortran_writer.gen_decls(symbol_table) + assert "integer, target :: tgt_var" in result + + # Test with is_pointer=True on an array + array_ptr_type = ArrayType(ScalarType(ScalarType.Intrinsic.INTEGER, + ScalarType.Precision.UNDEFINED), + [ArrayType.Extent.ATTRIBUTE], + is_pointer=True) + pointer_symbol = DataSymbol("ptr_array", array_ptr_type) + symbol_table.add(pointer_symbol) + result = fortran_writer.gen_decls(symbol_table) + assert "integer, dimension(:), pointer :: ptr_array" in result + + # Test with is_target=True on an array + array_tgt_type = ArrayType(ScalarType(ScalarType.Intrinsic.INTEGER, + ScalarType.Precision.UNDEFINED), + [ArrayType.Extent.ATTRIBUTE], + is_target=True) + target_symbol = DataSymbol("tgt_array", array_tgt_type) + symbol_table.add(target_symbol) + result = fortran_writer.gen_decls(symbol_table) + assert "integer, dimension(:), target :: tgt_array" in result + + # Test with is_pointer=True on a derived type + dtypesym = DataTypeSymbol("dtype", StructureType(), is_pointer=True) + dtype_ptr_sym = DataSymbol("dtype_ptr", dtypesym) + symbol_table.add(dtype_ptr_sym) + result = fortran_writer.gen_decls(symbol_table) + assert "type(dtype), pointer :: dtype_ptr" in result + + # Test with is_target=True on a derived type + dtypesym = DataTypeSymbol("dtype", StructureType(), is_target=True) + dtype_tgt_sym = DataSymbol("dtype_tgt", dtypesym) + symbol_table.add(dtype_tgt_sym) + result = fortran_writer.gen_decls(symbol_table) + assert "type(dtype), target :: dtype_tgt" in result + + def test_gen_decls_nested_scope(fortran_writer): ''' Test that gen_decls() correctly checks for potential wildcard imports of an unresolved symbol in an outer scope. diff --git a/src/psyclone/tests/psyir/frontend/fparser2_pointer_test.py b/src/psyclone/tests/psyir/frontend/fparser2_pointer_test.py index f796162014..75be8aa98d 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_pointer_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_pointer_test.py @@ -38,7 +38,100 @@ ''' Performs py.test tests on the handling of pointers in the fparser2 PSyIR front-end. ''' -from psyclone.psyir.nodes import CodeBlock, Assignment +from psyclone.psyir.nodes import CodeBlock, Assignment, Routine +from psyclone.psyir.symbols import ScalarType, ArrayType, DataTypeSymbol + + +def test_pointer_declaration(fortran_reader): + ''' Test that pointer declarations are parsed correctly. ''' + test_module = ''' + subroutine mysub() + integer, pointer :: a + integer, pointer :: b(:) + integer, pointer :: c(:,:) + type(my_type), pointer :: d + end subroutine + ''' + file_container = fortran_reader.psyir_from_source(test_module) + assert not file_container.walk(CodeBlock) + routine = file_container.walk(Routine)[0] + symbol_table = routine.symbol_table + + sym_a = symbol_table.lookup("a") + assert isinstance(sym_a.datatype, ScalarType) + assert sym_a.datatype.intrinsic == ScalarType.Intrinsic.INTEGER + assert sym_a.datatype.is_pointer + assert sym_a.is_pointer + assert not sym_a.is_target + + sym_b = symbol_table.lookup("b") + assert isinstance(sym_b.datatype, ArrayType) + assert sym_b.datatype.intrinsic == ScalarType.Intrinsic.INTEGER + assert sym_b.datatype.is_pointer + assert sym_b.is_pointer + assert not sym_b.datatype.datatype.is_pointer + assert not sym_b.is_target + + sym_c = symbol_table.lookup("c") + assert isinstance(sym_c.datatype, ArrayType) + assert sym_c.datatype.intrinsic == ScalarType.Intrinsic.INTEGER + assert sym_c.datatype.is_pointer + assert sym_c.is_pointer + assert not sym_c.datatype.datatype.is_pointer + assert not sym_c.is_target + + sym_d = symbol_table.lookup("d") + assert isinstance(sym_d.datatype, DataTypeSymbol) + assert sym_d.datatype.name == "my_type" + assert sym_d.datatype.is_pointer + assert sym_d.is_pointer + assert not sym_d.is_target + + +def test_target_declaration(fortran_reader): + ''' Test that target declarations are parsed correctly. ''' + test_module = ''' + subroutine mysub() + integer, target :: a + integer, target :: b(:) + integer, target :: c(:,:) + type(my_type), target :: d + end subroutine + ''' + file_container = fortran_reader.psyir_from_source(test_module) + assert not file_container.walk(CodeBlock) + routine = file_container.walk(Routine)[0] + symbol_table = routine.symbol_table + + sym_a = symbol_table.lookup("a") + assert isinstance(sym_a.datatype, ScalarType) + assert sym_a.datatype.intrinsic == ScalarType.Intrinsic.INTEGER + assert sym_a.datatype.is_target + assert sym_a.is_target + assert not sym_a.is_pointer + + sym_b = symbol_table.lookup("b") + assert isinstance(sym_b.datatype, ArrayType) + assert sym_b.datatype.intrinsic == ScalarType.Intrinsic.INTEGER + assert sym_b.datatype.is_target + assert sym_b.is_target + assert not sym_b.datatype.datatype.is_target + assert not sym_b.is_pointer + + sym_c = symbol_table.lookup("c") + assert isinstance(sym_c.datatype, ArrayType) + assert sym_c.datatype.intrinsic == ScalarType.Intrinsic.INTEGER + assert sym_c.datatype.is_target + assert sym_c.is_target + assert not sym_c.datatype.datatype.is_target + assert not sym_c.is_pointer + + sym_d = symbol_table.lookup("d") + assert isinstance(sym_d.datatype, DataTypeSymbol) + assert sym_d.datatype.name == "my_type" + assert sym_d.datatype.is_target + assert sym_d.is_target + assert not sym_d.is_pointer def test_pointer_assignments(fortran_reader): diff --git a/src/psyclone/tests/psyir/frontend/fparser2_subroutine_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_subroutine_handler_test.py index e5cc00e1f5..d340cc5a17 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_subroutine_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_subroutine_handler_test.py @@ -357,7 +357,7 @@ def test_function_unsupported_derived_type(fortran_reader): " type :: my_type\n" " integer :: flag\n" " end type my_type\n" - " type(my_type), pointer :: my_func, var1\n" + " type(my_type), pointer, asynchronous :: my_func, var1\n" " my_func => null()\n" " end function my_func\n" "end module a\n") @@ -367,10 +367,11 @@ def test_function_unsupported_derived_type(fortran_reader): assert routine.return_symbol.name == "my_func" assert isinstance(routine.return_symbol.datatype, UnsupportedFortranType) assert (routine.return_symbol.datatype.declaration.lower() == - "type(my_type), pointer :: my_func") + "type(my_type), pointer, asynchronous :: my_func") sym = routine.symbol_table.lookup("var1") assert isinstance(sym.datatype, UnsupportedFortranType) - assert sym.datatype.declaration.lower() == "type(my_type), pointer :: var1" + assert (sym.datatype.declaration.lower() + == "type(my_type), pointer, asynchronous :: var1") @pytest.mark.parametrize("fn_prefix", ["elemental", "pure", "impure", diff --git a/src/psyclone/tests/psyir/frontend/fparser2_test.py b/src/psyclone/tests/psyir/frontend/fparser2_test.py index f102dd7083..f3f8478aae 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_test.py @@ -399,7 +399,7 @@ def test_get_partial_datatype(): # Entry in symbol table with partial information. Example has one # unsupported attribute (and no others) and an unsupported assignment. - reader = FortranStringReader("integer, pointer :: l1 => null()") + reader = FortranStringReader("integer, asynchronous :: l1 => null()") node = Specification_Part(reader).content[0] ids = [id(entry) for entry in walk(node)] datatype, init = processor._get_partial_datatype(node, fake_parent, {}) @@ -412,7 +412,7 @@ def test_get_partial_datatype(): # Entry in symbol table with partial information. Example has one # unsupported attribute and one supported attribute. - reader = FortranStringReader("real*4, target, dimension(10,20) :: l1") + reader = FortranStringReader("real*4, asynchronous, dimension(10,20):: l1") node = Specification_Part(reader).content[0] ids = [id(entry) for entry in walk(node)] datatype, init = processor._get_partial_datatype(node, fake_parent, {}) @@ -440,7 +440,7 @@ def test_get_partial_datatype(): # Multiple variables in the declaration are also supported but are # not used by PSyclone at the moment. reader = FortranStringReader( - "integer, pointer :: l1 => null(), l2 => null()") + "integer, asynchronous :: l1 => null(), l2 => null()") node = Specification_Part(reader).content[0] ids = [id(entry) for entry in walk(node)] datatype, init = processor._get_partial_datatype(node, fake_parent, {}) @@ -507,6 +507,30 @@ def test_process_declarations(): assert symtab.lookup("p3").visibility == Symbol.Visibility.PRIVATE assert symtab.lookup("p4").visibility == Symbol.Visibility.PRIVATE + # pointer/target attribute + reader = FortranStringReader("real, pointer :: p5") + fparser2spec = Specification_Part(reader).content[0] + processor.process_declarations(fake_parent, [fparser2spec], []) + assert symtab.lookup("p5").is_pointer + assert not symtab.lookup("p5").is_target + assert isinstance(symtab.lookup("p5").datatype, ScalarType) + assert symtab.lookup("p5").datatype.intrinsic == ScalarType.Intrinsic.REAL + reader = FortranStringReader("real, target :: p6") + fparser2spec = Specification_Part(reader).content[0] + processor.process_declarations(fake_parent, [fparser2spec], []) + assert symtab.lookup("p6").is_target + assert not symtab.lookup("p6").is_pointer + assert isinstance(symtab.lookup("p6").datatype, ScalarType) + assert symtab.lookup("p6").datatype.intrinsic == ScalarType.Intrinsic.REAL + reader = FortranStringReader("real, dimension(5), pointer :: p7") + fparser2spec = Specification_Part(reader).content[0] + processor.process_declarations(fake_parent, [fparser2spec], []) + assert symtab.lookup("p7").is_pointer + assert not symtab.lookup("p7").is_target + assert isinstance(symtab.lookup("p7").datatype, ArrayType) + assert symtab.lookup("p7").datatype.intrinsic == ScalarType.Intrinsic.REAL + assert not symtab.lookup("p7").datatype.datatype.is_pointer + # Initialisations of static constant values (parameters) reader = FortranStringReader("integer, parameter :: i1 = 1") fparser2spec = Specification_Part(reader).content[0] @@ -578,15 +602,18 @@ def test_process_declarations(): processor.process_declarations(fake_parent, [fparser2spec], []) ptr_sym = fake_parent.symbol_table.lookup("dptr") assert isinstance(ptr_sym, DataSymbol) - assert isinstance(ptr_sym.datatype, UnsupportedFortranType) + assert isinstance(ptr_sym.datatype, ArrayType) + assert ptr_sym.datatype.intrinsic is ScalarType.Intrinsic.REAL + assert ptr_sym.datatype.is_pointer assert isinstance(ptr_sym.initial_value, IntrinsicCall) @pytest.mark.usefixtures("f2008_parser") @pytest.mark.parametrize("decln_text", - ["integer, pointer :: l1 => null(), l2 => null()", + ["integer, asynchronous :: l1 => null()," + " l2 => null()", "integer, intent(in), optional :: l1, l2", - "integer, target :: l1, l2"]) + "integer, asynchronous :: l1, l2"]) def test_process_declarations_unsupportedfortrantype(decln_text): '''Test that process_declarations method of Fparser2Reader adds datatype information to an UnsupportedFortranType by @@ -626,6 +653,27 @@ def test_process_declarations_errors(): assert ("SAVE and PARAMETER attributes are not compatible but found:\n " "INTEGER, PARAMETER, SAVE :: l1 = 1" in str(error.value)) + reader = FortranStringReader("real, pointer, target :: l1") + fparser2spec = Specification_Part(reader).content[0] + with pytest.raises(GenerationError) as error: + processor.process_declarations(fake_parent, [fparser2spec], []) + assert ("POINTER and TARGET attributes are not compatible but found:\n " + "REAL, POINTER, TARGET :: l1" in str(error.value)) + + reader = FortranStringReader("real, pointer, parameter :: l1 = 1.0") + fparser2spec = Specification_Part(reader).content[0] + with pytest.raises(GenerationError) as error: + processor.process_declarations(fake_parent, [fparser2spec], []) + assert ("POINTER and PARAMETER attributes are not compatible but found:\n " + "REAL, POINTER, PARAMETER :: l1 = 1.0" in str(error.value)) + + reader = FortranStringReader("real, pointer, allocatable :: l1") + fparser2spec = Specification_Part(reader).content[0] + with pytest.raises(GenerationError) as error: + processor.process_declarations(fake_parent, [fparser2spec], []) + assert ("ALLOCATABLE and POINTER attributes are not compatible but found:" + "\n REAL, POINTER, ALLOCATABLE :: l1" in str(error.value)) + reader = FortranStringReader("integer, parameter, intent(in) :: l1 = 1") fparser2spec = Specification_Part(reader).content[0] with pytest.raises(GenerationError) as error: @@ -765,29 +813,29 @@ def test_process_unsupported_declarations(fortran_reader): processor = Fparser2Reader() # Multiple symbols with a single attribute - reader = FortranStringReader("integer, private, pointer :: d, e") + reader = FortranStringReader("integer, private, asynchronous :: d, e") fparser2spec = Specification_Part(reader).content[0] processor.process_declarations(fake_parent, [fparser2spec], []) dsym = fake_parent.symbol_table.lookup("d") assert isinstance(dsym.datatype, UnsupportedFortranType) - assert dsym.datatype.declaration == "INTEGER, PRIVATE, POINTER :: d" + assert dsym.datatype.declaration == "INTEGER, PRIVATE, ASYNCHRONOUS :: d" esym = fake_parent.symbol_table.lookup("e") assert isinstance(esym.datatype, UnsupportedFortranType) - assert esym.datatype.declaration == "INTEGER, PRIVATE, POINTER :: e" + assert esym.datatype.declaration == "INTEGER, PRIVATE, ASYNCHRONOUS :: e" # Multiple attributes reader = FortranStringReader( - "INTEGER, PRIVATE, DIMENSION(3), POINTER :: f, g") + "INTEGER, PRIVATE, DIMENSION(3), ASYNCHRONOUS :: f, g") fparser2spec = Specification_Part(reader).content[0] processor.process_declarations(fake_parent, [fparser2spec], []) fsym = fake_parent.symbol_table.lookup("f") assert isinstance(fsym.datatype, UnsupportedFortranType) assert (fsym.datatype.declaration == - "INTEGER, PRIVATE, DIMENSION(3), POINTER :: f") + "INTEGER, PRIVATE, DIMENSION(3), ASYNCHRONOUS :: f") gsym = fake_parent.symbol_table.lookup("g") assert isinstance(gsym.datatype, UnsupportedFortranType) assert (gsym.datatype.declaration == - "INTEGER, PRIVATE, DIMENSION(3), POINTER :: g") + "INTEGER, PRIVATE, DIMENSION(3), ASYNCHRONOUS :: g") # Test with unsupported intrinsic type. Note the space before complex # below which stops the line being treated as a comment. @@ -1271,9 +1319,9 @@ def test_process_save_attribute_declarations(parser): assert isinstance(fake_parent.symbol_table.lookup("var4").interface, StaticInterface) - # Test that when it is part of an UnsupportedType (target attribute in - # this case) it becomes an UnknownInterface. - reader = FortranStringReader("integer, target :: var5") + # Test that when it is part of an UnsupportedType (asynchronous attribute + # in this case) it becomes an UnknownInterface. + reader = FortranStringReader("integer, volatile, save :: var5") fparser2spec = Specification_Part(reader).content[0] processor.process_declarations(fake_parent, [fparser2spec], []) assert isinstance(fake_parent.symbol_table.lookup("var5").datatype, @@ -1281,6 +1329,16 @@ def test_process_save_attribute_declarations(parser): assert isinstance(fake_parent.symbol_table.lookup("var5").interface, UnknownInterface) + # Test that it works in combination with another attribute, here target. + reader = FortranStringReader("integer, target, save :: var6") + fparser2spec = Specification_Part(reader).content[0] + processor.process_declarations(fake_parent, [fparser2spec], []) + assert (fake_parent.symbol_table.lookup("var6").datatype.intrinsic + == ScalarType.Intrinsic.INTEGER) + assert fake_parent.symbol_table.lookup("var6").is_target + assert isinstance(fake_parent.symbol_table.lookup("var6").interface, + StaticInterface) + @pytest.mark.usefixtures("f2008_parser") def test_process_declarations_intent(): @@ -1678,14 +1736,14 @@ def test_process_declarations_unrecognised_attribute(): a symbol with UnsupportedFortranType and the correct visibility. ''' fake_parent = KernelSchedule.create("dummy") processor = Fparser2Reader() - reader = FortranStringReader("integer, private, target :: idx1\n") + reader = FortranStringReader("integer, private, volatile :: idx1\n") fparser2spec = Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.children, []) sym = fake_parent.symbol_table.lookup("idx1") assert isinstance(sym.datatype, UnsupportedFortranType) assert sym.visibility == Symbol.Visibility.PRIVATE # No access statement so should be public (the default in Fortran) - reader = FortranStringReader("integer, target :: idx2\n") + reader = FortranStringReader("integer, volatile :: idx2\n") fparser2spec = Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.children, []) sym = fake_parent.symbol_table.lookup("idx2") @@ -1694,7 +1752,7 @@ def test_process_declarations_unrecognised_attribute(): # No access statement so should pick up the default visibility supplied # to the symbol table. fake_parent.symbol_table.default_visibility = Symbol.Visibility.PRIVATE - reader = FortranStringReader("integer, target :: idx3\n") + reader = FortranStringReader("integer, volatile :: idx3\n") fparser2spec = Specification_Part(reader) processor.process_declarations( fake_parent, fparser2spec.children, [], {}) @@ -1703,7 +1761,7 @@ def test_process_declarations_unrecognised_attribute(): assert sym.visibility == Symbol.Visibility.PRIVATE # No access statement but visibility provided in visibility_map argument # to process_declarations() - reader = FortranStringReader("integer, target :: idx4\n") + reader = FortranStringReader("integer, volatile :: idx4\n") fparser2spec = Specification_Part(reader) processor.process_declarations( fake_parent, fparser2spec.children, [], diff --git a/src/psyclone/tests/psyir/symbols/data_type_symbol_test.py b/src/psyclone/tests/psyir/symbols/data_type_symbol_test.py index 11dc71ea85..537f483f57 100644 --- a/src/psyclone/tests/psyir/symbols/data_type_symbol_test.py +++ b/src/psyclone/tests/psyir/symbols/data_type_symbol_test.py @@ -51,6 +51,35 @@ def test_create_datatypesymbol(): assert isinstance(sym.datatype, UnresolvedType) assert str(sym) == "my_type: DataTypeSymbol" + with pytest.raises(TypeError) as err: + _ = DataTypeSymbol("my_type", UnresolvedType(), is_pointer="true", + is_target=False) + assert ("is_pointer should be a boolean but found 'str'" in str(err.value)) + + with pytest.raises(TypeError) as err: + _ = DataTypeSymbol("my_type", UnresolvedType(), is_pointer=True, + is_target="false") + assert ("is_target should be a boolean but found 'str'" in str(err.value)) + + # Impossible combination of is_pointer and is_target + with pytest.raises(ValueError) as err: + _ = DataTypeSymbol("my_type", UnresolvedType(), is_pointer=True, + is_target=True) + assert ("A symbol cannot be both a pointer and a target" in str(err.value)) + + # Possible combinations of is_pointer and is_target + _ = DataTypeSymbol("my_type", UnresolvedType(), is_pointer=True, + is_target=False) + _ = DataTypeSymbol("my_type", UnresolvedType(), is_pointer=False, + is_target=True) + _ = DataTypeSymbol("my_type", UnresolvedType(), is_pointer=False, + is_target=False) + + # Default values of is_pointer and is_target + sym = DataTypeSymbol("my_type", UnresolvedType()) + assert not sym.is_pointer + assert not sym.is_target + def test_create_datatypesymbol_wrong_datatype(): ''' Check that attempting to specify the type of a DataTypeSymbol with an diff --git a/src/psyclone/tests/psyir/symbols/datatype_test.py b/src/psyclone/tests/psyir/symbols/datatype_test.py index 32b9ebe76b..adda4b10c5 100644 --- a/src/psyclone/tests/psyir/symbols/datatype_test.py +++ b/src/psyclone/tests/psyir/symbols/datatype_test.py @@ -66,6 +66,32 @@ def test_datatype(): assert ("__str__" in msg) +def test_datatype_is_pointer_is_target(): + '''Test that the is_pointer and is_target attributes raise the expected + exceptions when instantiating (a subclass of) DataType.''' + with pytest.raises(TypeError) as excinfo: + _ = UnresolvedType(is_pointer="true", is_target=False) + assert ("Expected 'is_pointer' to be a bool but got 'str'" in + str(excinfo.value)) + with pytest.raises(TypeError) as excinfo: + _ = UnresolvedType(is_pointer=True, is_target="false") + assert ("Expected 'is_target' to be a bool but got 'str'" in + str(excinfo.value)) + # Impossible combination + with pytest.raises(ValueError) as excinfo: + _ = UnresolvedType(is_pointer=True, is_target=True) + assert ("A DataType cannot be both a pointer and a target." in + str(excinfo.value)) + # Possible combinations + _ = UnresolvedType(is_pointer=False, is_target=False) + _ = UnresolvedType(is_pointer=True, is_target=False) + _ = UnresolvedType(is_pointer=False, is_target=True) + # Default values + dtype = UnresolvedType() + assert not dtype.is_pointer + assert not dtype.is_target + + # UnresolvedType class def test_unresolvedtype(): @@ -822,7 +848,7 @@ def test_unsupported_fortran_type_copy(fortran_reader): subroutine test use some_mod, only: some_type, start, stop integer, parameter :: nelem = 4 - type(some_type), pointer :: var(nelem), var2(start:stop) + type(some_type), asynchronous :: var(nelem), var2(start:stop) end subroutine ''' psyir = fortran_reader.psyir_from_source(code) diff --git a/src/psyclone/tests/psyir/symbols/symbol_table_test.py b/src/psyclone/tests/psyir/symbols/symbol_table_test.py index d799f5d7d7..e94ca331dd 100644 --- a/src/psyclone/tests/psyir/symbols/symbol_table_test.py +++ b/src/psyclone/tests/psyir/symbols/symbol_table_test.py @@ -2810,7 +2810,9 @@ def test_resolve_imports(fortran_reader, tmpdir, monkeypatch): subroutine.symbol_table.resolve_imports( symbol_target=subroutine.symbol_table.lookup('b_2')) assert isinstance(b_2, symbols.DataSymbol) - assert isinstance(b_2.datatype, symbols.UnsupportedFortranType) + assert isinstance(b_2.datatype, symbols.ScalarType) + assert b_2.datatype.intrinsic == symbols.ScalarType.Intrinsic.INTEGER + assert b_2.datatype.is_pointer assert isinstance(b_2.interface, symbols.ImportInterface) assert b_2.interface.container_symbol == \ subroutine.symbol_table.lookup('b_mod') @@ -3246,7 +3248,9 @@ def test_resolve_imports_from_child_symtab_uft( symbol = mod.symbol_table.lookup("some_var") # pylint: disable=unidiomatic-typecheck assert type(symbol) is symbols.DataSymbol - assert isinstance(symbol.datatype, symbols.UnsupportedFortranType) + assert isinstance(symbol.datatype, symbols.ScalarType) + assert symbol.datatype.intrinsic == symbols.ScalarType.Intrinsic.INTEGER + assert symbol.datatype.is_pointer assert isinstance(symbol.interface, symbols.ImportInterface) assert symbol.interface.container_symbol.name == "a_mod" @@ -3355,7 +3359,9 @@ def test_resolve_imports_from_child_symtabs_utf( symbol = mod.symbol_table.lookup("some_var") # pylint: disable=unidiomatic-typecheck assert type(symbol) is symbols.DataSymbol - assert isinstance(symbol.datatype, symbols.UnsupportedFortranType) + assert isinstance(symbol.datatype, symbols.ScalarType) + assert symbol.datatype.intrinsic == symbols.ScalarType.Intrinsic.INTEGER + assert symbol.datatype.is_pointer assert isinstance(symbol.interface, symbols.ImportInterface) assert symbol.interface.container_symbol.name == "a_mod" diff --git a/src/psyclone/tests/psyir/transformations/inline_trans_test.py b/src/psyclone/tests/psyir/transformations/inline_trans_test.py index e7efd1c172..082379dbf0 100644 --- a/src/psyclone/tests/psyir/transformations/inline_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/inline_trans_test.py @@ -1693,7 +1693,7 @@ def test_validate_unsupportedtype_argument(fortran_reader): " call sub(ptr)\n" "end subroutine main\n" "subroutine sub(x)\n" - " real, pointer, intent(inout) :: x\n" + " real, volatile, intent(inout) :: x\n" " x = x + 1.0\n" "end subroutine sub\n" "end module test_mod\n" @@ -1704,7 +1704,7 @@ def test_validate_unsupportedtype_argument(fortran_reader): with pytest.raises(TransformationError) as err: inline_trans.validate(routine) assert ("Routine 'sub' cannot be inlined because it contains a Symbol 'x' " - "which is an Argument of UnsupportedType: 'REAL, POINTER, " + "which is an Argument of UnsupportedType: 'REAL, VOLATILE, " "INTENT(INOUT) :: x'" in str(err.value)) @@ -1721,7 +1721,7 @@ def test_validate_unknowninterface(fortran_reader, fortran_writer, tmpdir): " call sub()\n" "end subroutine main\n" "subroutine sub()\n" - " real, pointer :: x\n" + " real, volatile :: x\n" " x = x + 1.0\n" "end subroutine sub\n" "end module test_mod\n" @@ -1732,7 +1732,7 @@ def test_validate_unknowninterface(fortran_reader, fortran_writer, tmpdir): with pytest.raises(TransformationError) as err: inline_trans.validate(routine) assert (" Routine 'sub' cannot be inlined because it contains a Symbol " - "'x' with an UnknownInterface: 'REAL, POINTER :: x'" + "'x' with an UnknownInterface: 'REAL, VOLATILE :: x'" in str(err.value)) # But if the interface is known, it has no problem inlining it @@ -1741,7 +1741,7 @@ def test_validate_unknowninterface(fortran_reader, fortran_writer, tmpdir): inline_trans.apply(routine) assert fortran_writer(psyir.walk(Routine)[0]) == """\ subroutine main() - REAL, POINTER :: x + REAL, VOLATILE :: x x = x + 1.0 diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/matmul2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/matmul2code_trans_test.py index 284f66e70a..a526033d0d 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/matmul2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/matmul2code_trans_test.py @@ -707,10 +707,9 @@ def test_validate_matmat_with_same_mem(fortran_reader): assign = psyir.walk(Assignment)[0] with pytest.raises(TransformationError) as excinfo: trans.validate(assign.rhs) - assert ("Transformation Error: Must have full type information for result " - "and operands of MATMUL IntrinsicCall but found 'result: " - "DataSymbol