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

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a527543
Add `type, extends` and `procedure` support in `StructureType`.
JulienRemy Jun 25, 2024
b95abdc
Add support for `class` keyword in declarations.
JulienRemy Jul 1, 2024
349bd2b
Cleanup.
JulienRemy Jul 2, 2024
f3bb061
#2642 Implement `extends`, `procedure` in derived types, `class` keyw…
JulienRemy Jul 2, 2024
db3d952
#2642 Add missing `class` keyword in Fortran backend.
JulienRemy Jul 15, 2024
6d0bb74
#2642 Fix `replace` calls for #2643 workaround.
JulienRemy Jul 15, 2024
a449c31
Merge branch 'master' into 2642_implement_extends_procedure_and_class
Nov 28, 2024
16ed2e4
#2642 Fix flake8 formatting and some tests.
Nov 28, 2024
e182c18
#2642 Some codecov progress, some tests still missing.
Nov 28, 2024
ff993ea
Merge branch 'master' into 2642_implement_extends_procedure_and_class
Nov 28, 2024
8c789b7
#2642 More tests.
Nov 29, 2024
4247a00
Merge branch 'master' into 2642_implement_extends_procedure_and_class
Nov 29, 2024
1cd37e4
#2642 Edit dev doc
Nov 29, 2024
0719559
#2642 Cleanup
Nov 29, 2024
6698427
#2642 More codecov
Nov 29, 2024
cc6fb67
#2642 Flake8
Nov 29, 2024
c8f74cb
#2642 Remove nonsensical test. Edit psyGen w.r.t. procedure support.
Dec 2, 2024
c35d98b
#2642 Avoid useless extends DataTypeSymbol visibility and procedure d…
Dec 2, 2024
0923123
Merge branch 'master' into 2642_implement_extends_procedure_and_class
Dec 2, 2024
18679fa
#2642 Add test about unkown parent type not being declared in the mod…
Dec 2, 2024
706bffc
#2642 Add psyGen tests and cleanup.
Dec 2, 2024
b72336b
#2642 Edits w.r.t. Andy's review
Dec 11, 2024
809ebf2
#2642 codecov
Dec 11, 2024
6f18710
#2642 flake8...
Dec 11, 2024
9419a05
#2642 Refactor, codecov
Dec 11, 2024
421e546
Merge branch 'master' into 2642_implement_extends_procedure_and_class
arporter Dec 18, 2024
a862a21
#2642 Fix parent type to UnresolvedInterface and add to symbol table.
Dec 18, 2024
cbc5857
#2642 Edits wrt Andy's second review (all except `class(*)`)
Dec 19, 2024
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
9 changes: 4 additions & 5 deletions doc/developer_guide/psyir_symbols.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,15 @@ explicitly listed may be assumed to be unsupported):
+----------------------+--------------------+--------------------+
| |Supported |Unsupported |
+======================+====================+====================+
|Variables |ALLOCATABLE |CLASS |
|Variables |ALLOCATABLE, CLASS | |
+----------------------+--------------------+--------------------+
| |CHARACTER, DOUBLE |COMPLEX, CHARACTER |
| |PRECISION, INTEGER, |with LEN or KIND |
| |LOGICAL, REAL | |
+----------------------+--------------------+--------------------+
| |Derived Types |'extends', |
| | |'abstract' or with |
| | |CONTAINS; Operator |
| | |overloading |
| |Derived Types, |'abstract', |
| |'extends', |operator overloading|
| |CONTAINS | |
+----------------------+--------------------+--------------------+
| |DIMENSION |Array extents |
| | |specified using |
Expand Down
20 changes: 14 additions & 6 deletions 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,16 +224,22 @@ def create_from_psyir(symbol):

datatype = symbol.datatype

if not isinstance(datatype, UnsupportedFortranType):
if not isinstance(datatype, StructureType):
raise InternalError(
f"Expected kernel metadata to be stored in the PSyIR as "
f"an UnsupportedFortranType, but found "
f"a StructureType, but found "
f"{type(datatype).__name__}.")

# In an UnsupportedFortranType, the declaration is stored as a
# string, so use create_from_fortran_string()
# 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.
type_declaration = FortranWriter().gen_typedecl(symbol)
type_declaration = type_declaration.replace(", public", "")
type_declaration = type_declaration.replace(", private", "")
return GOceanKernelMetadata.create_from_fortran_string(
arporter marked this conversation as resolved.
Show resolved Hide resolved
datatype.declaration)
type_declaration)

@staticmethod
def create_from_fortran_string(fortran_string):
Expand Down
62 changes: 37 additions & 25 deletions src/psyclone/domain/lfric/kernel/lfric_kernel_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,38 +44,40 @@
from fparser.two.utils import walk, get_child

from psyclone.domain.lfric import LFRicConstants
from psyclone.domain.lfric.kernel.columnwise_operator_arg_metadata import \
ColumnwiseOperatorArgMetadata
from psyclone.domain.lfric.kernel.columnwise_operator_arg_metadata import (
ColumnwiseOperatorArgMetadata)
from psyclone.domain.lfric.kernel.field_arg_metadata import FieldArgMetadata
from psyclone.domain.lfric.kernel.field_vector_arg_metadata import \
FieldVectorArgMetadata
from psyclone.domain.lfric.kernel.inter_grid_arg_metadata import \
InterGridArgMetadata
from psyclone.domain.lfric.kernel.inter_grid_vector_arg_metadata import \
InterGridVectorArgMetadata
from psyclone.domain.lfric.kernel.operator_arg_metadata import \
OperatorArgMetadata
from psyclone.domain.lfric.kernel.field_vector_arg_metadata import (
FieldVectorArgMetadata)
from psyclone.domain.lfric.kernel.inter_grid_arg_metadata import (
InterGridArgMetadata)
from psyclone.domain.lfric.kernel.inter_grid_vector_arg_metadata import (
InterGridVectorArgMetadata)
from psyclone.domain.lfric.kernel.operator_arg_metadata import (
OperatorArgMetadata)
from psyclone.domain.lfric.kernel.common_metadata import CommonMetadata
from psyclone.domain.lfric.kernel.common_meta_arg_metadata import \
CommonMetaArgMetadata
from psyclone.domain.lfric.kernel.evaluator_targets_metadata import \
EvaluatorTargetsMetadata
from psyclone.domain.lfric.kernel.meta_args_metadata import \
MetaArgsMetadata
from psyclone.domain.lfric.kernel.meta_funcs_metadata import \
MetaFuncsMetadata
from psyclone.domain.lfric.kernel.meta_mesh_metadata import \
MetaMeshMetadata
from psyclone.domain.lfric.kernel.meta_ref_element_metadata import \
MetaRefElementMetadata
from psyclone.domain.lfric.kernel.operates_on_metadata import \
OperatesOnMetadata
from psyclone.domain.lfric.kernel.common_meta_arg_metadata import (
CommonMetaArgMetadata)
from psyclone.domain.lfric.kernel.evaluator_targets_metadata import (
EvaluatorTargetsMetadata)
from psyclone.domain.lfric.kernel.meta_args_metadata import (
MetaArgsMetadata)
from psyclone.domain.lfric.kernel.meta_funcs_metadata import (
MetaFuncsMetadata)
from psyclone.domain.lfric.kernel.meta_mesh_metadata import (
MetaMeshMetadata)
from psyclone.domain.lfric.kernel.meta_ref_element_metadata import (
MetaRefElementMetadata)
from psyclone.domain.lfric.kernel.operates_on_metadata import (
OperatesOnMetadata)
from psyclone.domain.lfric.kernel.scalar_arg_metadata import ScalarArgMetadata
from psyclone.domain.lfric.kernel.shapes_metadata import ShapesMetadata
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,16 @@ 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, False)
return LFRicKernelMetadata.create_from_fortran_string(
arporter marked this conversation as resolved.
Show resolved Hide resolved
type_declaration)

if not isinstance(datatype, UnsupportedFortranType):
raise InternalError(
f"Expected kernel metadata to be stored in the PSyIR as "
Expand Down
14 changes: 13 additions & 1 deletion src/psyclone/psyGen.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@
UnresolvedType,
ImportInterface, INTEGER_TYPE,
RoutineSymbol, Symbol)
from psyclone.psyir.symbols.datatypes import UnsupportedFortranType
from psyclone.psyir.symbols.datatypes import (UnsupportedFortranType,
StructureType)

# The types of 'intent' that an argument to a Fortran subroutine
# may have
Expand Down Expand Up @@ -1753,6 +1754,8 @@ def _rename_psyir(self, suffix):
# the kernel metadata.
container_table = container.symbol_table
for sym in container_table.datatypesymbols:
# Either the DataTypeSymbol is of UnsupportedFortranType,
# in which case we replace in its whole declaration.
if isinstance(sym.datatype, UnsupportedFortranType):
new_declaration = sym.datatype.declaration.replace(
orig_kern_name, new_kern_name)
Expand All @@ -1761,6 +1764,15 @@ def _rename_psyir(self, suffix):
new_declaration,
partial_datatype=sym.datatype.partial_datatype)
# pylint: enable=protected-access
# Or the DataTypeSymbol is a StructureType, in which case we
# replace the "code" procedure component initial value.
elif isinstance(sym.datatype, StructureType):
arporter marked this conversation as resolved.
Show resolved Hide resolved
new_kernel_symbol = container_table.lookup(new_kern_name)
new_initial_value = Reference(new_kernel_symbol)
sym.datatype.replace_procedure_component_initial_value(
orig_kern_name,
new_initial_value
)

@property
def modified(self):
Expand Down
7 changes: 4 additions & 3 deletions src/psyclone/psyad/domain/lfric/lfric_adjoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
from psyclone.psyad import AdjointVisitor
from psyclone.psyad.domain.common import create_adjoint_name
from psyclone.psyir.nodes import Routine
from psyclone.psyir.symbols import ContainerSymbol, UnsupportedFortranType
from psyclone.psyir.symbols import ContainerSymbol, StructureType
from psyclone.psyir.symbols.symbol import ArgumentInterface, ImportInterface


Expand Down Expand Up @@ -80,8 +80,9 @@ def generate_lfric_adjoint(tl_psyir, active_variables):
# linear kernel.
tl_container = find_container(tl_psyir)
for sym in tl_container.symbol_table.datatypesymbols:
if (isinstance(sym.datatype, UnsupportedFortranType) and
"extends(kernel_type)" in sym.datatype.declaration.lower()):
if (isinstance(sym.datatype, StructureType) and
sym.datatype.extends is not None and
sym.datatype.extends.name.lower() == "kernel_type"):
tl_metadata_name = sym.name
break
else:
Expand Down
78 changes: 72 additions & 6 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 @@ -488,6 +492,54 @@ 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 or a
StructureType.ComponentType.
: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):
arporter marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -696,8 +748,17 @@ 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 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 @@ -710,12 +771,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 @@ -724,6 +779,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 @@ -937,6 +1000,9 @@ def gen_decls(self, symbol_table, is_module_scope=False):
# We ignore all symbols with a PreprocessorInterface
if isinstance(sym.interface, PreprocessorInterface):
all_symbols.remove(sym)
# We remove the '*' symbol used in 'class(*) :: var'
if sym.name == "*":
all_symbols.remove(sym)

# If the symbol table contains any symbols with an
# UnresolvedInterface interface (they are not explicitly
Expand Down
Loading
Loading