Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Towards #2577) Add pointer and target attributes in datatypes, frontend and backend #2822

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
10 changes: 9 additions & 1 deletion src/psyclone/psyir/backend/fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}"

Expand All @@ -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"

Expand Down
42 changes: 36 additions & 6 deletions src/psyclone/psyir/frontend/fparser2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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"
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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[:])
Expand Down
42 changes: 40 additions & 2 deletions src/psyclone/psyir/symbols/data_type_symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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__}"
Expand Down Expand Up @@ -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
Expand Down
15 changes: 14 additions & 1 deletion src/psyclone/psyir/symbols/datasymbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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))
66 changes: 56 additions & 10 deletions src/psyclone/psyir/symbols/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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):
'''
Expand Down Expand Up @@ -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 "
Expand Down Expand Up @@ -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 = ""
Expand Down Expand Up @@ -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 "
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
'''
Expand Down Expand Up @@ -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):
Expand All @@ -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.
Expand Down Expand Up @@ -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`
Expand Down
37 changes: 37 additions & 0 deletions src/psyclone/psyir/symbols/typed_symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading
Loading