Reference Guide  2.5.0
structure_reference.py
1 # -----------------------------------------------------------------------------
2 # BSD 3-Clause License
3 #
4 # Copyright (c) 2020-2024, Science and Technology Facilities Council.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are met:
9 #
10 # * Redistributions of source code must retain the above copyright notice, this
11 # list of conditions and the following disclaimer.
12 #
13 # * Redistributions in binary form must reproduce the above copyright notice,
14 # this list of conditions and the following disclaimer in the documentation
15 # and/or other materials provided with the distribution.
16 #
17 # * Neither the name of the copyright holder nor the names of its
18 # contributors may be used to endorse or promote products derived from
19 # this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 # POSSIBILITY OF SUCH DAMAGE.
33 # -----------------------------------------------------------------------------
34 # Author: A. R. Porter, N. Nobre and S. Siso STFC Daresbury Lab
35 # Author: J. Henrichs, Bureau of Meteorology
36 # -----------------------------------------------------------------------------
37 
38 ''' This module contains the implementation of the StructureReference node. '''
39 
40 from psyclone.core import Signature
41 from psyclone.psyir.nodes.reference import Reference
42 from psyclone.psyir.nodes.member import Member
43 from psyclone.psyir.nodes.array_member import ArrayMember
44 from psyclone.psyir.nodes.array_mixin import ArrayMixin
46  ArrayOfStructuresMember)
47 from psyclone.psyir.nodes.structure_member import StructureMember
48 from psyclone.psyir.symbols import (ArrayType, DataSymbol, DataType,
49  DataTypeSymbol, UnresolvedType, ScalarType,
50  StructureType, UnsupportedType)
51 from psyclone.errors import InternalError
52 
53 
55  '''
56  Node representing a reference to a component of a structure. As such
57  it must have a single child representing the component being accessed.
58 
59  :param symbol: the symbol being referenced.
60  :type symbol: :py:class:`psyclone.psyir.symbols.Symbol`
61  :param kwargs: additional keyword arguments provided to the super class.
62  :type kwargs: unwrapped dict.
63 
64  '''
65  # Textual description of the node.
66  _children_valid_format = "Member"
67  _text_name = "StructureReference"
68 
69  def __init__(self, symbol, **kwargs):
70  super().__init__(symbol=symbol, **kwargs)
71  self._overwrite_datatype_overwrite_datatype = None
72 
73  @staticmethod
74  def _validate_child(position, child):
75  '''
76  :param int position: the position to be validated.
77  :param child: a child to be validated.
78  :type child: :py:class:`psyclone.psyir.nodes.Node`
79 
80  :return: whether the given child and position are valid for this node.
81  :rtype: bool
82 
83  '''
84  if position == 0:
85  return isinstance(child, Member)
86  return False
87 
88  @staticmethod
89  def create(symbol, members, parent=None, overwrite_datatype=None):
90  '''
91  Create a StructureReference instance given a symbol and a
92  list of components. e.g. for "field%bundle(2)%flag" this
93  list would be [("bundle", [Literal("2", INTEGER4_TYPE)]), "flag"].
94 
95  :param symbol: the symbol that this reference is to.
96  :type symbol: :py:class:`psyclone.psyir.symbols.DataSymbol`
97  :param members: the component(s) of the structure that make up \
98  the full access. Any components that are array accesses must \
99  provide the name of the array and a list of DataNodes describing \
100  which part of it is accessed.
101  :type members: list of str or 2-tuples containing (str, \
102  list of nodes describing array access)
103  :param parent: the parent of this node in the PSyIR.
104  :type parent: sub-class of :py:class:`psyclone.psyir.nodes.Node`
105  :param overwrite_datatype: the datatype for the reference, which will \
106  overwrite the value determined by analysing the corresponding \
107  user defined type. This is useful when e.g. the module that \
108  declares the structure cannot be accessed.
109  :type overwrite_datatype: \
110  Optional[:py:class:`psyclone.psyir.symbols.DataType`]
111 
112  :returns: a StructureReference instance.
113  :rtype: :py:class:`psyclone.psyir.nodes.StructureReference`
114 
115  :raises TypeError: if the supplied symbol is not a DataSymbol.
116 
117  '''
118  if not isinstance(symbol, DataSymbol):
119  raise TypeError(
120  f"The 'symbol' argument to StructureReference.create() "
121  f"should be a DataSymbol but found '{type(symbol).__name__}'.")
122 
123  if overwrite_datatype and not isinstance(overwrite_datatype, DataType):
124  raise TypeError(
125  f"The 'overwrite_datatype' argument to "
126  f"StructureReference.create() should be a DataType but found "
127  f"'{type(symbol).__name__}'.")
128 
129  return StructureReference.\
130  _create(symbol, symbol.datatype, members, parent=parent,
131  overwrite_datatype=overwrite_datatype)
132 
133  @classmethod
134  def _create(cls, symbol, symbol_type, members, parent=None,
135  overwrite_datatype=None):
136  # pylint: disable=too-many-arguments
137  '''
138  Create an instance of `cls` given a symbol, a type and a
139  list of components. e.g. for "field%bundle(2)%flag" this list
140  would be [("bundle", [Literal("2", INTEGER4_TYPE)]), "flag"].
141 
142  This 'internal' method is used by both ArrayOfStructuresReference
143  *and* this class which is why it is a class method with the symbol
144  type as a separate argument.
145 
146  :param symbol: the symbol that this reference is to.
147  :type symbol: :py:class:`psyclone.psyir.symbols.DataSymbol`
148  :param symbol_type: the type of the symbol being referenced.
149  :type symbol_type: :py:class:`psyclone.psyir.symbols.DataTypeSymbol`
150  :param members: the component(s) of the structure that are being \
151  accessed. Any components that are array references must \
152  provide the name of the array and a list of DataNodes describing \
153  which part of it is accessed.
154  :type members: list of str or 2-tuples containing (str, \
155  list of nodes describing array access)
156  :param parent: the parent of this node in the PSyIR.
157  :type parent: sub-class of :py:class:`psyclone.psyir.nodes.Node`
158  :param overwrite_datatype: the datatype for the reference, which will \
159  overwrite the value determined by analysing the corresponding \
160  user defined type. This is useful when e.g. the module that \
161  declares the structure cannot be accessed.
162  :type overwrite_datatype: \
163  Optional[:py:class:`psyclone.psyir.symbols.DataType`]
164 
165  :returns: a StructureReference instance.
166  :rtype: :py:class:`psyclone.psyir.nodes.StructureReference`
167 
168  :raises TypeError: if the arguments to the create method are not of \
169  the expected type.
170  :raises ValueError: if no members are provided (since this would then \
171  be a Reference as opposed to a StructureReference).
172  :raises NotImplementedError: if any of the structures being referenced\
173  do not have full type information available.
174 
175  '''
176  if not isinstance(symbol_type, (StructureType, DataTypeSymbol,
177  UnresolvedType, UnsupportedType)):
178  raise TypeError(
179  f"A StructureReference must refer to a symbol that is (or "
180  f"could be) a structure, however symbol '{symbol.name}' has "
181  f"type '{symbol_type}'.")
182  if not isinstance(members, list):
183  raise TypeError(
184  f"The 'members' argument to StructureReference._create() "
185  f"must be a list but found '{type(members).__name__}'.")
186  if not members:
187  raise ValueError(
188  f"A StructureReference must include one or more structure "
189  f"'members' that are being accessed but got an empty list for "
190  f"symbol '{symbol.name}'")
191 
192  # Create the base reference to the symbol that is a structure
193  ref = cls(symbol, parent=parent)
194 
195  # Bottom-up creation of full reference. The last element in the members
196  # list must be either an ArrayMember or a Member.
197  if isinstance(members[-1], tuple):
198  # An access to one or more array elements
199  subref = ArrayMember.create(members[-1][0], members[-1][1])
200  elif isinstance(members[-1], str):
201  # A member access
202  subref = Member(members[-1])
203  else:
204  raise TypeError(
205  f"The list of 'members' passed to StructureType._create() "
206  f"must consist of either 'str' or 2-tuple entries but found "
207  f"'{type(members[-1]).__name__}' in the last entry while "
208  f"attempting to create reference to symbol '{symbol.name}'")
209 
210  # Now do the remaining entries in the members list. Since we know that
211  # each of these forms part of a structure they must be either a
212  # StructureMember or an ArrayOfStructuresMember.
213  child_member = subref
214 
215  for component in reversed(members[:-1]):
216  if isinstance(component, tuple):
217  # This is an array access so we have an ArrayOfStructuresMember
218  subref = ArrayOfStructuresMember.create(
219  component[0], component[1], subref)
220  elif isinstance(component, str):
221  # No array access so just a StructureMember
222  subref = StructureMember.create(component, subref)
223  else:
224  raise TypeError(
225  f"The list of 'members' passed to StructureType._create() "
226  f"must consist of either 'str' or 2-tuple entries but "
227  f"found '{type(component).__name__}' while attempting to "
228  f"create reference to symbol '{symbol.name}'")
229  child_member = subref
230  # Finally, add this chain to the top-level reference
231  ref.addchild(child_member)
232  ref._overwrite_datatype = overwrite_datatype
233  return ref
234 
235  def __str__(self):
236  result = super().__str__()
237  for entity in self._children_children:
238  result += "\n" + str(entity)
239  return result
240 
241  @property
242  def member(self):
243  '''
244  :returns: the member of the structure that this reference is to.
245  :rtype: :py:class:`psyclone.psyir.nodes.Member`
246 
247  :raises InternalError: if the first child of this node is not an \
248  instance of Member.
249  '''
250  if not self.childrenchildrenchildren or not isinstance(self.childrenchildrenchildren[0], Member):
251  raise InternalError(
252  f"{type(self).__name__} malformed or incomplete. It must have "
253  f"a single child that must be a (sub-class of) Member, but "
254  f"found: {self.children}")
255  return self.childrenchildrenchildren[0]
256 
258  ''':returns: the Signature of this structure reference, and \
259  a list of the indices used for each component (empty list \
260  if an access is not an array).
261  :rtype: Tuple[:py:class:`psyclone.core.Signature`, \
262  List[List[:py:class:`psyclone.psyir.nodes.Node`]]]
263 
264  '''
265  # Get the signature of self:
266  my_sig, my_index = super().get_signature_and_indices()
267  # Then the sub-signature of the member, and indices used:
268  sub_sig, indices = self.childrenchildrenchildren[0].get_signature_and_indices()
269  # Combine signature and indices
270  return (Signature(my_sig, sub_sig), my_index + indices)
271 
272  @property
273  def datatype(self):
274  '''
275  Walks down the list of members making up this reference to determine
276  the type that it refers to. If an overwrite datatype was given to this
277  reference, this datatype will be returned instead of determining the
278  type.
279 
280  In order to minimise code duplication, this method also supports
281  ArrayOfStructuresReference by simply allowing for the case where
282  the starting reference is to an Array.
283 
284  :returns: the datatype of this reference.
285  :rtype: :py:class:`psyclone.psyir.symbols.DataType`
286 
287  :raises NotImplementedError: if the structure reference represents \
288  an array of arrays.
289 
290  '''
291  # pylint: disable=too-many-return-statements, too-many-branches
292  if self._overwrite_datatype_overwrite_datatype:
293  return self._overwrite_datatype_overwrite_datatype
294 
295  dtype = self.symbolsymbolsymbolsymbol.datatype
296 
297  if isinstance(dtype, ArrayType):
298  dtype = dtype.intrinsic
299 
300  if isinstance(dtype, DataTypeSymbol):
301  dtype = dtype.datatype
302 
303  if isinstance(dtype, (UnresolvedType, UnsupportedType)):
304  # We don't know the type of the symbol that defines the type
305  # of this structure.
306  dtype = UnresolvedType()
307 
308  # We do have the definition of this structure - walk down it.
309  cursor = self
310  cursor_type = dtype
311 
312  # The next four lines are required when this method is called for an
313  # ArrayOfStructuresReference.
314  if isinstance(cursor, ArrayMixin):
315  # pylint: disable=protected-access
316  shape = cursor._get_effective_shape()
317  else:
318  shape = []
319 
320  # Walk down the structure, collecting information on any array slices
321  # as we go.
322  while isinstance(cursor, (StructureMember, StructureReference)):
323  cursor = cursor.member
324  if isinstance(cursor_type, ArrayType):
325  cursor_type = cursor_type.intrinsic
326  if isinstance(cursor_type, DataTypeSymbol):
327  cursor_type = cursor_type.datatype
328  if not isinstance(cursor_type, (UnresolvedType, UnsupportedType)):
329  # Once we've hit an Unresolved/UnsupportedType the cursor_type
330  # will remain set to that as we can't do any better.
331  cursor_type = cursor_type.components[cursor.name].datatype
332  if isinstance(cursor, ArrayMixin):
333  # pylint: disable=protected-access
334  shape.extend(cursor._get_effective_shape())
335 
336  # We've reached the ultimate member of the structure access.
337  if shape:
338  if isinstance(cursor_type, ArrayType):
339  # It's of array type but does it represent a single element,
340  # a slice or a whole array? (We use `children` rather than
341  # `indices` so as to avoid having to check that `cursor` is
342  # an `ArrayMember`.)
343  if cursor.children:
344  # It has indices so could be a single element or a slice.
345  # pylint: disable=protected-access
346  cursor_shape = cursor._get_effective_shape()
347  else:
348  # No indices so it is an access to a whole array.
349  cursor_shape = cursor_type.shape
350  if cursor_shape and len(shape) != len(cursor_shape):
351  # This ultimate access is an array but we've already
352  # encountered one or more slices earlier in the access
353  # expression.
354  raise NotImplementedError(
355  f"Array of arrays not supported: the ultimate member "
356  f"'{cursor.name}' of the StructureAccess represents "
357  f"an array but other array notation is present in the "
358  f"full access expression: '{self.debug_string()}'")
359 
360  return ArrayType(cursor_type, shape)
361 
362  # We don't have an explicit array access (because `shape` is Falsey)
363  # but is the ultimate member itself an array?
364  if isinstance(cursor_type, ArrayType):
365  if not cursor.children:
366  # It is and there are no index expressions so we return the
367  # ArrayType.
368  return cursor_type
369  # We have an access to a single element of the array.
370  # Currently arrays of scalars are handled in a
371  # different way to all other types of array. Issue #1857 will
372  # fix this anomaly.
373  if isinstance(cursor_type.intrinsic, ScalarType.Intrinsic):
374  return ScalarType(cursor_type.intrinsic, cursor_type.precision)
375  return cursor_type.intrinsic
376  return cursor_type
377 
378 
379 # For AutoAPI documentation generation
380 __all__ = ['StructureReference']
def children(self, my_children)
Definition: node.py:935
def create(symbol, members, parent=None, overwrite_datatype=None)