From 65993e2b1546482fa43a883bd3771743169f0f10 Mon Sep 17 00:00:00 2001 From: Matthew Naylor Date: Mon, 15 Jun 2026 14:53:43 +0100 Subject: [PATCH 1/6] Add basic support for array constructors in PSyIR --- src/psyclone/psyir/backend/fortran.py | 14 ++ src/psyclone/psyir/frontend/fparser2.py | 37 +++++ src/psyclone/psyir/nodes/__init__.py | 2 + src/psyclone/psyir/nodes/array_constructor.py | 142 ++++++++++++++++++ src/psyclone/psyir/symbols/datasymbol.py | 5 +- .../arrayassignment2loops_trans.py | 13 +- .../tests/core/symbolic_maths_test.py | 2 +- .../psyir/backend/fortran_gen_decls_test.py | 2 +- .../tests/psyir/backend/fortran_test.py | 21 ++- ...fparser2_array_constructor_handler_test.py | 110 ++++++++++++++ .../tests/psyir/frontend/fparser2_test.py | 5 +- .../psyir/nodes/array_constructor_test.py | 98 ++++++++++++ .../arrayassignment2loops_trans_test.py | 11 +- 13 files changed, 449 insertions(+), 13 deletions(-) create mode 100644 src/psyclone/psyir/nodes/array_constructor.py create mode 100644 src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py create mode 100644 src/psyclone/tests/psyir/nodes/array_constructor_test.py diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index ce4cadf042..7cc5f04395 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -1439,6 +1439,20 @@ def literal_node(self, node): return result + def arrayconstructor_node(self, node): + '''This method is called when an ArrayConstrutor instance is + found in the PSyIR tree. + + :param node: an ArrayConstructor PSyIR node. + :type node: :py:class:`psyclone.psyir.nodes.ArrayConstructor` + + :returns: the Fortran code as a string. + :rtype: str + + ''' + contents = ", ".join([self._visit(child) for child in node.children]) + return f"[{contents}]" + def ifblock_node(self, node): '''This method is called when an IfBlock instance is found in the PSyIR tree. diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py index c43856e4d3..30a8c9a561 100644 --- a/src/psyclone/psyir/frontend/fparser2.py +++ b/src/psyclone/psyir/frontend/fparser2.py @@ -60,6 +60,7 @@ from psyclone.errors import InternalError, GenerationError from psyclone.psyir.commentable_mixin import CommentableMixin from psyclone.psyir.nodes import ( + ArrayConstructor, ArrayMember, ACCRoutineDirective, ArrayOfStructuresReference, ArrayReference, Assignment, BinaryOperation, Call, CodeBlock, Container, DataNode, Directive, FileContainer, IfBlock, IntrinsicCall, Literal, Loop, @@ -991,6 +992,7 @@ def __init__( Fortran2003.Allocate_Stmt: self._allocate_handler, Fortran2003.Allocate_Shape_Spec: self._allocate_shape_spec_handler, Fortran2003.Assignment_Stmt: self._assignment_handler, + Fortran2003.Array_Constructor: self._array_constructor_handler, Fortran2003.Structure_Constructor: self._call_handler, Fortran2003.Data_Pointer_Object: self._structure_accessor_handler, Fortran2003.Data_Ref: self._structure_accessor_handler, @@ -4975,6 +4977,41 @@ def _assignment_handler(self, node, parent): return assignment + def _array_constructor_handler(self, node, parent): + ''' + Transforms an fparser2 Array_Constructor to the PSyIR representation. + + :param node: node in fparser2 AST. + :type node: :py:class:`fparser.two.Fortran2003.Array_Constructor` + :param parent: Parent node of the PSyIR node we are constructing. + :type parent: :py:class:`psyclone.psyir.nodes.Node` + + :returns: PSyIR representation of node. + :rtype: :py:class:`psyclone.psyir.nodes.ArrayConstructor` + + :raises NotImplementedError: if the parse tree contains unsupported + elements. + ''' + ac_spec = node.items[1] + if isinstance(ac_spec, Fortran2003.Ac_Value_List): + elems = node.items[1].items + # Check that no elements are 'Ac_Implied_Do' + for elem in elems: + if isinstance(elem, Fortran2003.Ac_Implied_Do): + raise NotImplementedError( + "Array constructors with implied do loops cannot be " + "handled in the PSyIR") + array_cons = ArrayConstructor(ast=node, parent=parent) + self.process_nodes(parent=array_cons, nodes=elems) + return array_cons + elif isinstance(ac_spec, Fortran2003.Ac_Spec): + raise NotImplementedError( + "Array constructors with type specifications cannot be " + "handled in the PSyIR") + else: + raise NotImplementedError( + "Array constructor cannot be handled in the PSyIR") + def _structure_accessor_handler(self, node, parent): ''' Create the PSyIR for structure accessors found in fparser2 Data_Ref, diff --git a/src/psyclone/psyir/nodes/__init__.py b/src/psyclone/psyir/nodes/__init__.py index e964604174..466694a0f1 100644 --- a/src/psyclone/psyir/nodes/__init__.py +++ b/src/psyclone/psyir/nodes/__init__.py @@ -39,6 +39,7 @@ ''' PSyIR nodes package module ''' +from psyclone.psyir.nodes.array_constructor import ArrayConstructor from psyclone.psyir.nodes.acc_clauses import ( ACCAsyncQueueClause, ACCCopyClause, ACCCopyInClause, ACCCopyOutClause) @@ -113,6 +114,7 @@ # this package e.g. 'from psyclone.psyir.nodes import Literal' __all__ = [ 'colored', + 'ArrayConstructor', 'ArrayMember', 'ArrayReference', 'ArrayOfStructuresMember', diff --git a/src/psyclone/psyir/nodes/array_constructor.py b/src/psyclone/psyir/nodes/array_constructor.py new file mode 100644 index 0000000000..9924a3ec2f --- /dev/null +++ b/src/psyclone/psyir/nodes/array_constructor.py @@ -0,0 +1,142 @@ +# ----------------------------------------------------------------------------- +# BSD 3-Clause License +# +# Copyright (c) 2017-2026, Science and Technology Facilities Council. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- +# Authors M. Naylor, University of Cambridge, UK +# ----------------------------------------------------------------------------- + +''' This module contains the ArrayConstructor node implementation.''' + +from psyclone.core import VariablesAccessMap +from psyclone.psyir.symbols import Symbol, UnresolvedType +from psyclone.psyir.nodes.datanode import DataNode + + +class ArrayConstructor(DataNode): + ''' + Node representing an array constructor. + ''' + + # Textual description of the node. + _children_valid_format = "[DataNode]*" + _text_name = "ArrayConstructor" + _colour = "yellow" + + def __init__(self, *elems: DataNode, **kwargs): + super().__init__(**kwargs) + + @staticmethod + def create(*elems: DataNode): + '''Create and ArrayConstructor instance representing an array + with the given elements. + + :param elems: the elements of the array being constructed. + :returns: an ArrayConstructor instance. + :rtype: :py:class:`psyclone.psyir.nodes.IfBlock` + + :raises GenerationError: if the arguments are not of the \ + expected type. + ''' + array_cons = ArrayConstructor() + for elem in elems: + array_cons.children.append(elem) + return array_cons + + @staticmethod + def _validate_child(position, child): + ''' + :param int position: the position to be validated. + :param child: a child to be validated. + :type child: :py:class:`psyclone.psyir.nodes.Node` + + :return: whether the given child and position are valid for this node. + :rtype: bool + ''' + return isinstance(child, DataNode) + + @property + def datatype(self): + ''' + :returns: the type of this array constructor. + :rtype: :py:class:`psyclone.psyir.symbols.DataType` + ''' + return UnresolvedType() + + def node_str(self, colour=True): + ''' + Construct a text representation of this node, optionally containing + colour control codes. + + :param bool colour: whether or not to include colour control codes. + + :returns: description of this PSyIR node. + :rtype: str + ''' + elems = ", ".join([child.node_str(colour=colour) + for child in self.children]) + return (f"{self.coloured_name(colour)}" + f"[{elems}]") + + def get_all_accessed_symbols(self) -> set[Symbol]: + ''' + :returns: a set of all the symbols accessed inside this + ArrayConsructor. + ''' + return super().get_all_accessed_symbols() + + def reference_accesses(self) -> VariablesAccessMap: + ''' + :returns: a map of all the symbol accessed inside this node, the + keys are Signatures (unique identifiers to a symbol and its + structure accessors) and the values are AccessSequence + (a sequence of AccessTypes). + ''' + return super().reference_accesses() + + def replace_symbols_using(self, table_or_symbol): + ''' + Replace any Symbols referred to by this object with those in the + supplied SymbolTable (or just the supplied Symbol instance) if they + have matching names. If there is no match for a given Symbol then it + is left unchanged. + + :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` + + ''' + super().replace_symbols_using(table_or_symbol) + + +# For AutoAPI documentation generation +__all__ = ['ArrayConstructor'] diff --git a/src/psyclone/psyir/symbols/datasymbol.py b/src/psyclone/psyir/symbols/datasymbol.py index 5c9e6861d9..aa70b5cdb9 100644 --- a/src/psyclone/psyir/symbols/datasymbol.py +++ b/src/psyclone/psyir/symbols/datasymbol.py @@ -218,7 +218,7 @@ def initial_value(self, new_value): # pylint: disable=import-outside-toplevel from psyclone.psyir.nodes import ( Assignment, Node, Literal, Operation, Reference, - CodeBlock, IntrinsicCall) + CodeBlock, IntrinsicCall, ArrayConstructor) from psyclone.psyir.symbols.datatypes import (ScalarType, ArrayType, UnsupportedType) @@ -239,7 +239,8 @@ def initial_value(self, new_value): if isinstance(new_value, Node): for node in new_value.walk(Node): if not isinstance(node, (Literal, Operation, Reference, - CodeBlock, IntrinsicCall)): + CodeBlock, IntrinsicCall, + ArrayConstructor)): raise ValueError( f"Error setting initial value for symbol " f"'{self.name}'. PSyIR static expressions can only" diff --git a/src/psyclone/psyir/transformations/arrayassignment2loops_trans.py b/src/psyclone/psyir/transformations/arrayassignment2loops_trans.py index 852f0fe77e..feec8bd583 100644 --- a/src/psyclone/psyir/transformations/arrayassignment2loops_trans.py +++ b/src/psyclone/psyir/transformations/arrayassignment2loops_trans.py @@ -47,7 +47,7 @@ from psyclone.psyGen import Transformation from psyclone.psyir.nodes import ( Assignment, Call, IntrinsicCall, Loop, Literal, Node, Range, Reference, - CodeBlock, Routine, BinaryOperation) + CodeBlock, Routine, BinaryOperation, ArrayConstructor) from psyclone.psyir.nodes.array_mixin import ArrayMixin from psyclone.psyir.nodes.structure_accessor_mixin import ( StructureAccessorMixin) @@ -245,6 +245,17 @@ def validate( raise TransformationError(LazyString( lambda: f"{message}, but found:\n{node.debug_string()}")) + # Do not allow to transform expressions with ArrayConstructors + # TODO: this could be relaxed in future + if node.has_descendant(ArrayConstructor): + message = (f"{self.name} does not support array assignments that" + " contain an ArrayConstructor anywhere in" + " the expression") + if verbose: + node.append_preceding_comment(message) + raise TransformationError(LazyString( + lambda: f"{message}, but found:\n{node.debug_string()}")) + try: node.scope except SymbolError: diff --git a/src/psyclone/tests/core/symbolic_maths_test.py b/src/psyclone/tests/core/symbolic_maths_test.py index bd042da0c9..2561fc0021 100644 --- a/src/psyclone/tests/core/symbolic_maths_test.py +++ b/src/psyclone/tests/core/symbolic_maths_test.py @@ -251,7 +251,7 @@ def test_symbolic_maths_never_equal_error(fortran_reader): source = ( "program test_prog\n" " integer :: a(2)\n" - " a(:) = (/1, 2/)\n" + " a(:) = (/integer :: 1, 2/)\n" "end program test_prog\n") psyir = fortran_reader.psyir_from_source(source) assignment = psyir.children[0][0] 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 067237d141..9519fb2a07 100644 --- a/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py @@ -164,7 +164,7 @@ def test_gen_param_decls_case_insensitive(fortran_reader, "integer(kind=an_int), parameter :: a_second_int = 5_i_def\n" "integer, parameter :: nfieldnames3d = 4\n" "integer, dimension(nfieldnames3d), parameter :: " - "interpolationlevels = [2, 0, HUGE(InterpolationLevels) / 3, 0]\n") + "InterpolationLevels = [2, 0, HUGE(InterpolationLevels) / 3, 0]\n") def test_gen_decls(fortran_writer): diff --git a/src/psyclone/tests/psyir/backend/fortran_test.py b/src/psyclone/tests/psyir/backend/fortran_test.py index 81442f18c7..850067794e 100644 --- a/src/psyclone/tests/psyir/backend/fortran_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_test.py @@ -54,7 +54,8 @@ ArrayReference, ArrayOfStructuresReference, Range, StructureReference, Schedule, Routine, Return, FileContainer, IfBlock, OMPTaskloopDirective, OMPMasterDirective, OMPParallelDirective, Loop, OMPNumTasksClause, - OMPDependClause, IntrinsicCall, OMPReductionClause, UnknownDirective) + OMPDependClause, IntrinsicCall, OMPReductionClause, UnknownDirective, + ArrayConstructor) from psyclone.psyir.symbols import ( ArgumentInterface, ContainerSymbol, DataSymbol, GenericInterfaceSymbol, ImportInterface, RoutineSymbol, StaticInterface, Symbol, SymbolTable, @@ -1742,7 +1743,7 @@ def test_fw_codeblock_2(fortran_reader, fortran_writer, tmpdir): '''Check the FortranWriter class codeblock method correctly prints out the Fortran representation when there is a code block that is part of a line (not a whole line). In this case the data initialisation - of the array 'a' "(/ 0.0 /)" is a code block. + of the array 'a' "(/ real :: 0.0 /)" is a code block. ''' # Generate fparser2 parse tree from Fortran code. @@ -1751,7 +1752,7 @@ def test_fw_codeblock_2(fortran_reader, fortran_writer, tmpdir): "contains\n" "subroutine tmp()\n" " real a(1)\n" - " a = (/ 0.0 /)\n" + " a = (/ real :: 0.0 /)\n" "end subroutine tmp\n" "end module test") psyir = fortran_reader.psyir_from_source(code) @@ -1761,7 +1762,7 @@ def test_fw_codeblock_2(fortran_reader, fortran_writer, tmpdir): # Generate Fortran from the PSyIR result = fortran_writer(psyir) - assert "a = (/0.0/)" in result + assert "a = (/REAL :: 0.0/)" in result assert Compile(tmpdir).string_compiles(result) @@ -2351,3 +2352,15 @@ def test_fw_unknowndirective(fortran_writer): assert fortran_writer(direc) == "!$omp atomic\n" direc = UnknownDirective(" IVDEP", "DIR") assert fortran_writer(direc) == "!DIR$ IVDEP\n" + + +def test_array_constructor(fortran_writer): + ''' + Test that the ArrayConstructor visitor generates the expected string. + ''' + array_cons = ArrayConstructor.create( + Literal("1", ScalarType.integer_type()), + Literal("2", ScalarType.integer_type()), + Literal("3", ScalarType.integer_type())) + output = fortran_writer(array_cons) + assert output == "[1, 2, 3]" diff --git a/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py new file mode 100644 index 0000000000..3c2e38ef37 --- /dev/null +++ b/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py @@ -0,0 +1,110 @@ +# ----------------------------------------------------------------------------- +# BSD 3-Clause License +# +# Copyright (c) 2019-2026, Science and Technology Facilities Council. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- +# Authors: M. Naylor, University of Cambridge + + +'''Performs py.test tests on the support for array constructors in the fparser2 +PSyIR front-end ''' + +from psyclone.psyir.nodes import ( + ArrayConstructor, Reference, Literal, BinaryOperation) + + +def test_handling_array_constructor_assignment(fortran_reader): + '''Check that the fparser2 frontend can handle simple + array constructors, without type specs and implicit do loops. + ''' + code = """ +program my_prog + implicit none + integer :: arr(3) + integer :: x + x = 10 + arr(:) = [1, x, x+2] +end program my_prog +""" + prog = fortran_reader.psyir_from_source(code) + ctrs = prog.walk(ArrayConstructor) + + # Program has one array constructor with three elements + assert len(ctrs) == 1 + assert len(ctrs[0].children) == 3 + ctr = ctrs[0] + + # The first element is the literal 1 + assert isinstance(ctr.children[0], Literal) + assert ctr.children[0].value == "1" + + # The second element is the reference "x" + assert isinstance(ctr.children[1], Reference) + assert ctr.children[1].name == "x" + + # The third element is the binary operation "x+2" + assert isinstance(ctr.children[2], BinaryOperation) + assert ctr.children[2].operator == BinaryOperation.Operator.ADD + assert isinstance(ctr.children[2].operands[0], Reference) + assert ctr.children[2].operands[0].name == "x" + assert isinstance(ctr.children[2].operands[1], Literal) + assert ctr.children[2].operands[1].value == "2" + + +def test_handling_array_constructor_arg(fortran_reader): + '''Check that the fparser2 frontend can handle array + constructors in arguments. + ''' + code = """ +program my_prog + implicit none + integer :: arr(4), arr2(2,2) + arr(:) = [0, 1, 2, 3] + arr2(:,:) = reshape(arr, [2,2]) +end program my_prog +""" + prog = fortran_reader.psyir_from_source(code) + ctrs = prog.walk(ArrayConstructor) + + # Program has two array constructors + assert len(ctrs) == 2 + + # The first has four elements (0, 1, 2, and 3) + assert len(ctrs[0].children) == 4 + for i in range(0, 4): + assert isinstance(ctrs[0].children[i], Literal) + assert ctrs[0].children[i].value == str(i) + + # The second has two elements (both 2) + assert len(ctrs[1].children) == 2 + for i in range(0, 2): + assert isinstance(ctrs[1].children[i], Literal) + assert ctrs[1].children[i].value == "2" diff --git a/src/psyclone/tests/psyir/frontend/fparser2_test.py b/src/psyclone/tests/psyir/frontend/fparser2_test.py index 5e11cba552..462499da17 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_test.py @@ -58,7 +58,7 @@ Schedule, CodeBlock, Assignment, Return, UnaryOperation, BinaryOperation, IfBlock, Reference, ArrayReference, Literal, KernelSchedule, RegionDirective, Routine, StandaloneDirective, - Call, IntrinsicCall) + Call, IntrinsicCall, ArrayConstructor) from psyclone.psyir.symbols import ( DataSymbol, ContainerSymbol, ArgumentInterface, ArrayType, SymbolError, ScalarType, RoutineSymbol, UnsupportedFortranType, @@ -776,7 +776,8 @@ def test_array_declarations_with_initialisations(fortran_reader): all_syms = [gsym, hsym] assert all(isinstance(sym, DataSymbol) for sym in all_syms) - assert all(isinstance(sym.initial_value, CodeBlock) for sym in all_syms) + assert isinstance(gsym.initial_value, ArrayConstructor) + assert isinstance(hsym.initial_value, CodeBlock) lsym = inner_st.lookup('l') assert isinstance(lsym.initial_value, Reference) diff --git a/src/psyclone/tests/psyir/nodes/array_constructor_test.py b/src/psyclone/tests/psyir/nodes/array_constructor_test.py new file mode 100644 index 0000000000..b2818e6ae1 --- /dev/null +++ b/src/psyclone/tests/psyir/nodes/array_constructor_test.py @@ -0,0 +1,98 @@ +# ----------------------------------------------------------------------------- +# BSD 3-Clause License +# +# Copyright (c) 2019-2026, Science and Technology Facilities Council. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- +# Authors: M. Naylor, University of Cambridge +# ----------------------------------------------------------------------------- + +''' Performs py.test tests on the ArrayConstructor PSyIR node. ''' + +import pytest +from psyclone.errors import GenerationError +from psyclone.psyir.nodes import ( + ArrayConstructor, Literal, BinaryOperation, Assignment, + Reference, IfBlock) +from psyclone.core import AccessType, Signature +from psyclone.psyir.symbols import ScalarType, DataSymbol +from psyclone.psyir.nodes.node import colored + + +def test_array_construction_valid(): + '''Test array construction and addition of children.''' + expr1 = Literal("1", ScalarType.integer_type()) + expr2 = Literal("2", ScalarType.integer_type()) + array_cons = ArrayConstructor.create(expr1, expr2) + expr3 = Literal("3", ScalarType.integer_type()) + expr4 = Literal("4", ScalarType.integer_type()) + expr5 = BinaryOperation.create(BinaryOperation.Operator.ADD, + expr3, expr4) + array_cons.children.append(expr5) + assert isinstance(array_cons.children[0], Literal) + assert isinstance(array_cons.children[1], Literal) + assert isinstance(array_cons.children[2], BinaryOperation) + + +def test_array_construction_invalid(): + '''Test invalid array construction.''' + # Construct an IfBlock + if_condition = Literal('true', ScalarType.boolean_type()) + if_body = [Assignment.create( + Reference(DataSymbol("tmp", ScalarType.real_single_type())), + Literal("10", ScalarType.integer_type()))] + ifblock = IfBlock.create(if_condition, if_body) + # Check that IfBlock cannot be an element of an ArrayConstructor + with pytest.raises(GenerationError) as err: + ArrayConstructor.create(ifblock) + assert ("Generation Error: Item 'IfBlock' can't be child 0 of " + "'ArrayConstructor'. The valid format is: '[DataNode]*" + in str(err.value)) + + +def test_array_construction_reference_accesses(): + '''Test the reference_accesses() method of an array constructor''' + ref = Reference(DataSymbol("tmp", ScalarType.integer_type())) + arr = ArrayConstructor.create(ref) + accs = arr.reference_accesses() + access_seq = accs[Signature("tmp")] + assert len(access_seq) == 1 + assert access_seq[0].access_type is AccessType.READ + + +def test_array_constructor_node_str(): + ''' Check the node_str method of the ArrayConstructor class.''' + lit = Literal("1", ScalarType.integer_single_type()) + coloured_lit = colored("Literal", Literal._colour) + array_cons = ArrayConstructor.create(lit) + coloured_array_cons = colored("ArrayConstructor", ArrayConstructor._colour) + assert (f"{coloured_array_cons}[{coloured_lit}" + "[value:'1', Scalar]]" + in str(array_cons)) diff --git a/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py b/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py index d0a15ec3d7..a1379311f3 100644 --- a/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py @@ -939,13 +939,15 @@ def test_validate_indirect_indexing(fortran_reader): INTEGER, DIMENSION(8,kfld) :: ishtSi2 INTEGER :: jf ! Assignment with CodeBlock on RHS. - iwewe(:) = (/ jpwe,jpea,jpwe,jpea /) + iwewe(:) = (/integer :: jpwe,jpea,jpwe,jpea /) ! Assignment with CodeBlock in array-index expression. - iwewe(:) = ishtSi((/ jpwe,jpea,jpwe,jpea /), 1) + iwewe(:) = ishtSi((/integer :: jpwe,jpea,jpwe,jpea /), 1) ! Index expression that evaluate to an array is a valid range ishtSi(5:8,jf) = ishtSi2(iwewe+1, jf) ! Index expression contains a call to an unknown function. ishtSi(5:8,jf) = ishtSi2(my_func(1), jf) + ! Assignment with ArrayConstructor on RHS. + iwewe(:) = (/jpwe,jpea,jpwe,jpea /) end program test ''') assignments = psyir.walk(Assignment) @@ -966,6 +968,11 @@ def test_validate_indirect_indexing(fortran_reader): assert ("ArrayAssignment2LoopsTrans does not accept calls which are not " "guaranteed to return a scalar or be elemental, but found " "'my_func'" in str(err.value)) + with pytest.raises(TransformationError) as err: + trans.validate(assignments[4]) + assert ("ArrayAssignment2LoopsTrans does not support array assignments " + "that contain an ArrayConstructor anywhere in the expression" + in str(err.value)) def test_validate_structure(fortran_reader): From 95cdeeaf4309d595b6e8bec514cc09bce238aaa0 Mon Sep 17 00:00:00 2001 From: Matthew Naylor Date: Mon, 15 Jun 2026 15:45:23 +0100 Subject: [PATCH 2/6] Fix mistakes in `ArrayConstructor` and it's backend --- src/psyclone/psyir/backend/fortran.py | 2 +- src/psyclone/psyir/nodes/array_constructor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index 7cc5f04395..1f5f13731b 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -1451,7 +1451,7 @@ def arrayconstructor_node(self, node): ''' contents = ", ".join([self._visit(child) for child in node.children]) - return f"[{contents}]" + return "[" + contents + "]" def ifblock_node(self, node): '''This method is called when an IfBlock instance is found in the diff --git a/src/psyclone/psyir/nodes/array_constructor.py b/src/psyclone/psyir/nodes/array_constructor.py index 9924a3ec2f..dd7aa00998 100644 --- a/src/psyclone/psyir/nodes/array_constructor.py +++ b/src/psyclone/psyir/nodes/array_constructor.py @@ -51,7 +51,7 @@ class ArrayConstructor(DataNode): _text_name = "ArrayConstructor" _colour = "yellow" - def __init__(self, *elems: DataNode, **kwargs): + def __init__(self, **kwargs): super().__init__(**kwargs) @staticmethod From e534772eee7a4c244623b8463abc2c71fa308a66 Mon Sep 17 00:00:00 2001 From: Matthew Naylor Date: Tue, 16 Jun 2026 09:24:43 +0100 Subject: [PATCH 3/6] Add some tests for nested constructors --- src/psyclone/psyir/backend/fortran.py | 2 +- .../tests/psyir/backend/fortran_test.py | 6 ++++-- .../fparser2_array_constructor_handler_test.py | 17 +++++++++++++---- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index 1f5f13731b..a4de95fbec 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -1440,7 +1440,7 @@ def literal_node(self, node): return result def arrayconstructor_node(self, node): - '''This method is called when an ArrayConstrutor instance is + '''This method is called when an ArrayConstructor instance is found in the PSyIR tree. :param node: an ArrayConstructor PSyIR node. diff --git a/src/psyclone/tests/psyir/backend/fortran_test.py b/src/psyclone/tests/psyir/backend/fortran_test.py index 850067794e..847d6e9955 100644 --- a/src/psyclone/tests/psyir/backend/fortran_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_test.py @@ -2361,6 +2361,8 @@ def test_array_constructor(fortran_writer): array_cons = ArrayConstructor.create( Literal("1", ScalarType.integer_type()), Literal("2", ScalarType.integer_type()), - Literal("3", ScalarType.integer_type())) + ArrayConstructor.create( + Literal("3", ScalarType.integer_type()), + Literal("4", ScalarType.integer_type()))) output = fortran_writer(array_cons) - assert output == "[1, 2, 3]" + assert output == "[1, 2, [3, 4]]" diff --git a/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py index 3c2e38ef37..b01142b965 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py @@ -51,15 +51,18 @@ def test_handling_array_constructor_assignment(fortran_reader): integer :: arr(3) integer :: x x = 10 - arr(:) = [1, x, x+2] + arr(:) = [1, x, x+2, [x]] end program my_prog """ prog = fortran_reader.psyir_from_source(code) ctrs = prog.walk(ArrayConstructor) - # Program has one array constructor with three elements - assert len(ctrs) == 1 - assert len(ctrs[0].children) == 3 + # Program has two array constructors, one with 4 elements one with 1 + assert len(ctrs) == 2 + assert len(ctrs[0].children) == 4 + assert len(ctrs[1].children) == 1 + + # Look at the outer array constructor first ctr = ctrs[0] # The first element is the literal 1 @@ -78,6 +81,12 @@ def test_handling_array_constructor_assignment(fortran_reader): assert isinstance(ctr.children[2].operands[1], Literal) assert ctr.children[2].operands[1].value == "2" + # The fourth element is a one-element ArrayConstructor holding "x" + assert isinstance(ctr.children[3], ArrayConstructor) + assert len(ctr.children[3].children) == 1 + assert isinstance(ctr.children[3].children[0], Reference) + assert ctr.children[3].children[0].name == "x" + def test_handling_array_constructor_arg(fortran_reader): '''Check that the fparser2 frontend can handle array From 2daa946e708150e939e3880e6568b40072b9acea Mon Sep 17 00:00:00 2001 From: Matthew Naylor Date: Tue, 16 Jun 2026 10:55:48 +0100 Subject: [PATCH 4/6] Implement `ArrayConstructor.datatype` and improve coverage --- src/psyclone/psyir/frontend/fparser2.py | 6 ++-- src/psyclone/psyir/nodes/array_constructor.py | 15 ++++++++-- ...fparser2_array_constructor_handler_test.py | 30 +++++++++++++++++++ .../arrayassignment2loops_trans_test.py | 4 ++- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py index 30a8c9a561..eefddcce97 100644 --- a/src/psyclone/psyir/frontend/fparser2.py +++ b/src/psyclone/psyir/frontend/fparser2.py @@ -5008,9 +5008,11 @@ def _array_constructor_handler(self, node, parent): raise NotImplementedError( "Array constructors with type specifications cannot be " "handled in the PSyIR") - else: + else: # pragma: no cover + # This should never be reached, but we defensively raise + # an exception just in case. raise NotImplementedError( - "Array constructor cannot be handled in the PSyIR") + "Unexpected array constructor form encountered") def _structure_accessor_handler(self, node, parent): ''' diff --git a/src/psyclone/psyir/nodes/array_constructor.py b/src/psyclone/psyir/nodes/array_constructor.py index dd7aa00998..57be10fcf0 100644 --- a/src/psyclone/psyir/nodes/array_constructor.py +++ b/src/psyclone/psyir/nodes/array_constructor.py @@ -37,7 +37,8 @@ ''' This module contains the ArrayConstructor node implementation.''' from psyclone.core import VariablesAccessMap -from psyclone.psyir.symbols import Symbol, UnresolvedType +from psyclone.psyir.symbols import ( + Symbol, UnresolvedType, ScalarType, ArrayType) from psyclone.psyir.nodes.datanode import DataNode @@ -89,7 +90,17 @@ def datatype(self): :returns: the type of this array constructor. :rtype: :py:class:`psyclone.psyir.symbols.DataType` ''' - return UnresolvedType() + # The result of an array constructor is always a rank-1 array. + # We look through the children to find the array-element type. + elem_type = UnresolvedType() + for child in self.children: + if isinstance(child.datatype, ArrayType): + elem_type = child.datatype.elemental_type + break + elif isinstance(child.datatype, ScalarType): + elem_type = child.datatype + break + return ArrayType(elem_type, [ArrayType.Extent.ATTRIBUTE]) def node_str(self, colour=True): ''' diff --git a/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py index b01142b965..c022ced52e 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py @@ -39,6 +39,8 @@ from psyclone.psyir.nodes import ( ArrayConstructor, Reference, Literal, BinaryOperation) +from psyclone.psyir.symbols import ScalarType, ArrayType + def test_handling_array_constructor_assignment(fortran_reader): @@ -117,3 +119,31 @@ def test_handling_array_constructor_arg(fortran_reader): for i in range(0, 2): assert isinstance(ctrs[1].children[i], Literal) assert ctrs[1].children[i].value == "2" + + +def test_handling_array_constructor_datatype(fortran_reader): + '''Check that the datatype of a parsed ArrayConstructor is correct. + ''' + code = """ +program my_prog + implicit none + integer :: x + integer, allocatable :: arr(:) + integer :: arr2(2, 2) + arr(:) = [x] + arr(:) = [x+1] + arr(:) = [[1]] + arr2(:,:) = 0 + arr(:) = [arr2] + arr(:) = [1, 2, arr2] +end program my_prog +""" + prog = fortran_reader.psyir_from_source(code) + + # All array constructors are rank-1 arrays of scalar integers + for ctr in prog.walk(ArrayConstructor): + dt = ctr.datatype + assert isinstance(dt, ArrayType) + assert len(dt.shape) == 1 + assert isinstance(dt.elemental_type, ScalarType) + assert dt.elemental_type.intrinsic == ScalarType.Intrinsic.INTEGER diff --git a/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py b/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py index a1379311f3..86c288d5de 100644 --- a/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py @@ -969,10 +969,12 @@ def test_validate_indirect_indexing(fortran_reader): "guaranteed to return a scalar or be elemental, but found " "'my_func'" in str(err.value)) with pytest.raises(TransformationError) as err: - trans.validate(assignments[4]) + trans.validate(assignments[4], verbose=True) assert ("ArrayAssignment2LoopsTrans does not support array assignments " "that contain an ArrayConstructor anywhere in the expression" in str(err.value)) + assert ("that contain an ArrayConstructor" + in assignments[4].preceding_comment) def test_validate_structure(fortran_reader): From adab76ccdfa029407432e6944b2869681a6fb1d1 Mon Sep 17 00:00:00 2001 From: Matthew Naylor Date: Tue, 16 Jun 2026 11:02:14 +0100 Subject: [PATCH 5/6] flake8 --- src/psyclone/psyir/frontend/fparser2.py | 2 +- .../psyir/frontend/fparser2_array_constructor_handler_test.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py index eefddcce97..7fb17e0297 100644 --- a/src/psyclone/psyir/frontend/fparser2.py +++ b/src/psyclone/psyir/frontend/fparser2.py @@ -5008,7 +5008,7 @@ def _array_constructor_handler(self, node, parent): raise NotImplementedError( "Array constructors with type specifications cannot be " "handled in the PSyIR") - else: # pragma: no cover + else: # pragma: no cover # This should never be reached, but we defensively raise # an exception just in case. raise NotImplementedError( diff --git a/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py index c022ced52e..2dcad6836e 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py @@ -42,7 +42,6 @@ from psyclone.psyir.symbols import ScalarType, ArrayType - def test_handling_array_constructor_assignment(fortran_reader): '''Check that the fparser2 frontend can handle simple array constructors, without type specs and implicit do loops. From d7229e48edd16938826fe22cb3b79bdccbf30875 Mon Sep 17 00:00:00 2001 From: Matthew Naylor Date: Thu, 18 Jun 2026 09:23:18 +0100 Subject: [PATCH 6/6] Add tests for derived types inside array constructors --- src/psyclone/psyir/nodes/array_constructor.py | 12 +-- ...fparser2_array_constructor_handler_test.py | 91 ++++++++++++++++--- .../psyir/nodes/array_constructor_test.py | 5 +- 3 files changed, 85 insertions(+), 23 deletions(-) diff --git a/src/psyclone/psyir/nodes/array_constructor.py b/src/psyclone/psyir/nodes/array_constructor.py index 57be10fcf0..14a0ab4af3 100644 --- a/src/psyclone/psyir/nodes/array_constructor.py +++ b/src/psyclone/psyir/nodes/array_constructor.py @@ -38,7 +38,7 @@ from psyclone.core import VariablesAccessMap from psyclone.psyir.symbols import ( - Symbol, UnresolvedType, ScalarType, ArrayType) + Symbol, UnresolvedType, ScalarType, ArrayType, DataTypeSymbol) from psyclone.psyir.nodes.datanode import DataNode @@ -100,6 +100,9 @@ def datatype(self): elif isinstance(child.datatype, ScalarType): elem_type = child.datatype break + elif isinstance(child.datatype, DataTypeSymbol): + elem_type = child.datatype + break return ArrayType(elem_type, [ArrayType.Extent.ATTRIBUTE]) def node_str(self, colour=True): @@ -112,15 +115,12 @@ def node_str(self, colour=True): :returns: description of this PSyIR node. :rtype: str ''' - elems = ", ".join([child.node_str(colour=colour) - for child in self.children]) - return (f"{self.coloured_name(colour)}" - f"[{elems}]") + return f"{self.coloured_name(colour)}[]" def get_all_accessed_symbols(self) -> set[Symbol]: ''' :returns: a set of all the symbols accessed inside this - ArrayConsructor. + ArrayConstructor. ''' return super().get_all_accessed_symbols() diff --git a/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py index 2dcad6836e..d5702ae02a 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_array_constructor_handler_test.py @@ -37,9 +37,10 @@ '''Performs py.test tests on the support for array constructors in the fparser2 PSyIR front-end ''' +import pytest from psyclone.psyir.nodes import ( - ArrayConstructor, Reference, Literal, BinaryOperation) -from psyclone.psyir.symbols import ScalarType, ArrayType + ArrayConstructor, Reference, Literal, BinaryOperation, CodeBlock) +from psyclone.psyir.symbols import ScalarType, ArrayType, DataTypeSymbol def test_handling_array_constructor_assignment(fortran_reader): @@ -120,29 +121,93 @@ def test_handling_array_constructor_arg(fortran_reader): assert ctrs[1].children[i].value == "2" -def test_handling_array_constructor_datatype(fortran_reader): +@pytest.mark.parametrize("arr_type", ["real", "integer"]) +def test_handling_array_constructor_datatype(fortran_reader, arr_type): '''Check that the datatype of a parsed ArrayConstructor is correct. ''' - code = """ + if arr_type == "real": + one = "1.0" + else: + one = "1" + code = f""" program my_prog implicit none - integer :: x - integer, allocatable :: arr(:) - integer :: arr2(2, 2) + {arr_type}, allocatable :: arr(:) + {arr_type} :: arr2(2, 2) + {arr_type} :: x + x = {one} arr(:) = [x] - arr(:) = [x+1] - arr(:) = [[1]] - arr2(:,:) = 0 + arr(:) = [x+{one}] + arr(:) = [[{one}]] + arr2(:,:) = {one} arr(:) = [arr2] - arr(:) = [1, 2, arr2] + arr(:) = [{one}, arr2] + arr(:) = [{arr_type} ::] ! Currently parsed to CodeBlock + arr(:) = [{arr_type} :: {one}, x] ! Currently parsed to CodeBlock end program my_prog """ prog = fortran_reader.psyir_from_source(code) - # All array constructors are rank-1 arrays of scalar integers + # Check that all ArrayConstructors are rank-1 arrays of scalars + count = 0 for ctr in prog.walk(ArrayConstructor): dt = ctr.datatype assert isinstance(dt, ArrayType) assert len(dt.shape) == 1 assert isinstance(dt.elemental_type, ScalarType) - assert dt.elemental_type.intrinsic == ScalarType.Intrinsic.INTEGER + if arr_type == "real": + assert dt.elemental_type.intrinsic == ScalarType.Intrinsic.REAL + else: + assert dt.elemental_type.intrinsic == ScalarType.Intrinsic.INTEGER + count += 1 + + # Check number of ArrayConstructors + assert count == 6 + + # Check number of CodeBlocks + assert len(prog.walk(CodeBlock)) == 2 + + +def test_handling_array_constructor_derived_type(fortran_reader): + '''Check that the datatype of a parsed ArrayConstructor is correct + in the case of a derived type. + ''' + code = """ +module my_mod + type :: my_type + integer :: val + end type +contains + subroutine sub() + implicit none + type(my_type), allocatable :: arr(:) + type(my_type) :: arr2(2, 2) + type(my_type) :: x + x%val = 1 + arr(:) = [x] + arr(:) = [[x]] + arr2(:,:) = x + arr(:) = [arr2] + arr(:) = [x, arr2] + arr(:) = [my_type ::] ! Currently parsed to CodeBlock + arr(:) = [my_type :: x, x] ! Currently parsed to CodeBlock + end subroutine +end module +""" + prog = fortran_reader.psyir_from_source(code) + + # Check that all ArrayConstructors are rank-1 arrays of derived type + count = 0 + for ctr in prog.walk(ArrayConstructor): + dt = ctr.datatype + assert isinstance(dt, ArrayType) + assert len(dt.shape) == 1 + assert isinstance(dt.elemental_type, DataTypeSymbol) + assert dt.elemental_type.name == "my_type" + count += 1 + + # Check number of ArrayConstructors + assert count == 5 + + # Check number of CodeBlocks + assert len(prog.walk(CodeBlock)) == 2 diff --git a/src/psyclone/tests/psyir/nodes/array_constructor_test.py b/src/psyclone/tests/psyir/nodes/array_constructor_test.py index b2818e6ae1..fd31b40edc 100644 --- a/src/psyclone/tests/psyir/nodes/array_constructor_test.py +++ b/src/psyclone/tests/psyir/nodes/array_constructor_test.py @@ -90,9 +90,6 @@ def test_array_construction_reference_accesses(): def test_array_constructor_node_str(): ''' Check the node_str method of the ArrayConstructor class.''' lit = Literal("1", ScalarType.integer_single_type()) - coloured_lit = colored("Literal", Literal._colour) array_cons = ArrayConstructor.create(lit) coloured_array_cons = colored("ArrayConstructor", ArrayConstructor._colour) - assert (f"{coloured_array_cons}[{coloured_lit}" - "[value:'1', Scalar]]" - in str(array_cons)) + assert f"{coloured_array_cons}[]" == array_cons.node_str()