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

(Closes #2642, initial step towards #2643) Implement extends(type), procedure in derived type, class in declarations. #2644

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/psyclone/domain/gocean/kernel/psyir.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@
from psyclone.errors import InternalError
from psyclone.parse.utils import ParseError
from psyclone.psyir.frontend.fortran import FortranReader
from psyclone.psyir.backend.fortran import FortranWriter
from psyclone.psyir.nodes import Container
from psyclone.psyir.symbols import DataTypeSymbol, UnsupportedFortranType
from psyclone.psyir.symbols import DataTypeSymbol, UnsupportedFortranType, \
StructureType


class GOceanContainer(Container):
Expand Down Expand Up @@ -222,6 +224,18 @@ def create_from_psyir(symbol):

datatype = symbol.datatype

# TODO #2643: This is a temporary solution using FortranWriter
# to allow the current metadata extraction to work with StructureType,
# instead of relying on UnsupportedFortranType.
# This will be removed when the metadata is extracted from the PSyIR
# itself.
if isinstance(datatype, StructureType):
type_declaration = FortranWriter().gen_typedecl(symbol)
type_declaration = type_declaration.replace(", public", "")
type_declaration = type_declaration.replace(", private", "")
return GOceanKernelMetadata.create_from_fortran_string(
type_declaration)

if not isinstance(datatype, UnsupportedFortranType):
raise InternalError(
f"Expected kernel metadata to be stored in the PSyIR as "
Expand Down
16 changes: 15 additions & 1 deletion src/psyclone/domain/lfric/kernel/lfric_kernel_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@
from psyclone.errors import InternalError
from psyclone.parse.utils import ParseError
from psyclone.psyir.frontend.fortran import FortranReader
from psyclone.psyir.symbols import DataTypeSymbol, UnsupportedFortranType
from psyclone.psyir.backend.fortran import FortranWriter
from psyclone.psyir.symbols import DataTypeSymbol, UnsupportedFortranType, \
StructureType

# pylint: disable=too-many-lines
# pylint: disable=too-many-instance-attributes
Expand Down Expand Up @@ -698,6 +700,18 @@ def create_from_psyir(symbol):

datatype = symbol.datatype

# TODO #2643: This is a temporary solution using FortranWriter
# to allow the current metadata extraction to work with StructureType,
# instead of relying on UnsupportedFortranType.
# This will be removed when the metadata is extracted from the PSyIR
# itself.
if isinstance(datatype, StructureType):
type_declaration = FortranWriter().gen_typedecl(symbol)
type_declaration = type_declaration.replace(", public", "")
type_declaration = type_declaration.replace(", private", "")
return LFRicKernelMetadata.create_from_fortran_string(
type_declaration)

if not isinstance(datatype, UnsupportedFortranType):
raise InternalError(
f"Expected kernel metadata to be stored in the PSyIR as "
Expand Down
84 changes: 77 additions & 7 deletions src/psyclone/psyir/backend/fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,15 @@ def gen_datatype(datatype, name):
'''
if isinstance(datatype, DataTypeSymbol):
# Symbol is of derived type
if datatype.is_class:
return f"class({datatype.name})"
return f"type({datatype.name})"

if (isinstance(datatype, ArrayType) and
isinstance(datatype.intrinsic, DataTypeSymbol)):
# Symbol is an array of derived types
if datatype.intrinsic.is_class:
return f"class({datatype.intrinsic.name})"
return f"type({datatype.intrinsic.name})"

try:
Expand Down Expand Up @@ -500,6 +504,53 @@ def gen_use(self, symbol, symbol_table):

return f"{self._nindent}use{intrinsic_str}{symbol.name}\n"

def gen_proceduredecl(self, symbol, include_visibility=True):
'''Create and return the Fortran procedure declaration for this Symbol.

:param symbol: the symbol instance.
:type symbol: Union[:py:class:`psyclone.psyir.symbols.DataSymbol`,
:py:class:`psyclone.psyir.symbols.datatypes.
StructureType.ComponentType`]
:param bool include_visibility: whether to include the visibility of
the symbol in the generated declaration (default True).

:returns: the Fortran procedure declaration as a string.
:rtype: str

:raises VisitorError: if the symbol is not a RoutineSymbol.
:raises InternalError: if the visibility is to be included but is
neither PUBLIC nor PRIVATE.
'''
if not isinstance(symbol, (DataSymbol, StructureType.ComponentType)):
raise VisitorError(
f"gen_proceduredecl() expects a 'DataSymbol' or "
f"'StructureType.ComponentType' as its first "
f"argument but got '{type(symbol).__name__}'")

if isinstance(symbol.datatype, UnsupportedFortranType):
return f"{self._nindent}{symbol.datatype.declaration}\n"

result = f"{self._nindent}procedure"

if include_visibility:
if symbol.visibility == Symbol.Visibility.PRIVATE:
result += ", private"
elif symbol.visibility == Symbol.Visibility.PUBLIC:
result += ", public"
else:
raise InternalError(
f"A Symbol must be either public or private but symbol "
f"'{symbol.name}' has visibility '{symbol.visibility}'")

# Specify name
result += f" :: {symbol.name}"

# Specify initialisation expression
if symbol.initial_value:
result += " => " + self._visit(symbol.initial_value)

return result + "\n"

def gen_vardecl(self, symbol, include_visibility=False):
'''Create and return the Fortran variable declaration for this Symbol
or derived-type member.
Expand Down Expand Up @@ -708,8 +759,23 @@ def gen_typedecl(self, symbol, include_visibility=True):
f"Fortran backend cannot generate code for symbol "
f"'{symbol.name}' of type '{type(symbol.datatype).__name__}'")

if isinstance(symbol.datatype, UnresolvedType):
raise VisitorError(
f"Local Symbol '{symbol.name}' is of UnresolvedType and "
f"therefore no declaration can be created for it. Should it "
f"have an ImportInterface?")

if not isinstance(symbol.datatype, StructureType):
raise VisitorError(
f"gen_typedecl expects a DataTypeSymbol with a StructureType "
f"as its datatype but got: '{type(symbol.datatype).__name__}'")

result = f"{self._nindent}type"

if symbol.datatype.extends:
# This is a component of a derived type
result += f", extends({symbol.datatype.extends.name})"

if include_visibility:
if symbol.visibility == Symbol.Visibility.PRIVATE:
result += ", private"
Expand All @@ -722,12 +788,6 @@ def gen_typedecl(self, symbol, include_visibility=True):
f"type '{type(symbol.visibility).__name__}'")
result += f" :: {symbol.name}\n"

if isinstance(symbol.datatype, UnresolvedType):
raise VisitorError(
f"Local Symbol '{symbol.name}' is of UnresolvedType and "
f"therefore no declaration can be created for it. Should it "
f"have an ImportInterface?")

self._depth += 1

for member in symbol.datatype.components.values():
Expand All @@ -736,6 +796,14 @@ def gen_typedecl(self, symbol, include_visibility=True):
# part of a module.
result += self.gen_vardecl(member,
include_visibility=include_visibility)
if len(symbol.datatype.procedure_components) > 0:
result += f"{self._nindent}contains\n"
self._depth += 1
for procedure in symbol.datatype.procedure_components.values():
result += self.gen_proceduredecl(procedure,
include_visibility)
self._depth -= 1

self._depth -= 1

result += f"{self._nindent}end type {symbol.name}\n"
Expand Down Expand Up @@ -967,7 +1035,9 @@ def gen_decls(self, symbol_table, is_module_scope=False):
unresolved_symbols = []
for sym in all_symbols[:]:
if isinstance(sym.interface, UnresolvedInterface):
unresolved_symbols.append(sym)
# Explicitly deal with '*' as in 'class(*) :: var'
if sym.name != "*":
unresolved_symbols.append(sym)
all_symbols.remove(sym)
try:
internal_interface_symbol = symbol_table.lookup(
Expand Down
Loading