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 #1558) support out-of-order parameter statements #2309

Merged
merged 3 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
107 changes: 67 additions & 40 deletions src/psyclone/psyir/frontend/fparser2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2248,6 +2248,63 @@ def _get_partial_datatype(self, node, scope, visibility_map):

return datatype, init_expr

def _process_parameter_stmts(self, nodes, parent):
'''
Examine the supplied list of fparser2 nodes and handle any
PARAMETER statements. This is done separately so that it can be
performed after all the declarations have been processed (since
a PARAMETER statement can come *before* a symbol's declaration.)

:param nodes: fparser2 AST nodes containing declaration statements.
:type nodes: list of :py:class:`fparser.two.utils.Base`
:param parent: PSyIR node in which to insert the symbols found.
:type parent: :py:class:`psyclone.psyir.nodes.KernelSchedule`

:raises NotImplementedError: if there are any issues parsing a
parameter statement.

'''
for node in nodes:
if not isinstance(node, Fortran2003.Implicit_Part):
continue
for stmt in node.children:
if not isinstance(stmt, Fortran2003.Parameter_Stmt):
continue
for parameter_def in stmt.children[1].items:
name, expr = parameter_def.items
try:
symbol = parent.symbol_table.lookup(str(name))
except Exception as err:
# If there is any problem put the whole thing
# in a codeblock (as we presume the original
# code is correct).
raise NotImplementedError(
f"Could not parse '{stmt}' because: "
f"{err}.") from err

if not isinstance(symbol, DataSymbol):
raise NotImplementedError(
f"Could not parse '{stmt}' because "
f"'{symbol.name}' is not a DataSymbol.")
if isinstance(symbol.datatype, UnknownType):
raise NotImplementedError(
f"Could not parse '{stmt}' because "
f"'{symbol.name}' has an UnknownType.")

# Parse its initialization into a dummy Assignment
# (but connected to the parent scope since symbols
# must be resolved)
dummynode = Assignment(parent=parent)
self.process_nodes(parent=dummynode, nodes=[expr])

# Add the initialization expression in the symbol
# constant_value attribute
ct_expr = dummynode.children[0].detach()
symbol.initial_value = ct_expr
symbol.is_constant = True
# Ensure the interface to this Symbol is static
symbol.interface = StaticInterface()

def process_declarations(self, parent, nodes, arg_list,
visibility_map=None):
'''
Expand Down Expand Up @@ -2433,51 +2490,21 @@ def process_declarations(self, parent, nodes, arg_list,
# These node types are handled separately
pass
elif isinstance(node, Fortran2003.Implicit_Part):
for stmt in node.children:
if isinstance(stmt, Fortran2003.Parameter_Stmt):
for parameter_def in stmt.children[1].items:
name, expr = parameter_def.items
try:
symbol = parent.symbol_table.lookup(str(name))
except Exception as err:
# If there is any problem put the whole thing
# in a codeblock (as we presume the original
# code is correct).
raise NotImplementedError(
f"Could not parse '{stmt}' because: "
f"{err}.") from err

if not isinstance(symbol, DataSymbol):
raise NotImplementedError(
f"Could not parse '{stmt}' because "
f"'{symbol.name}' is not a DataSymbol.")
if isinstance(symbol.datatype, UnknownType):
raise NotImplementedError(
f"Could not parse '{stmt}' because "
f"'{symbol.name}' has an UnknownType.")

# Parse its initialization into a dummy Assignment
# (but connected to the parent scope since symbols
# must be resolved)
dummynode = Assignment(parent=parent)
self.process_nodes(parent=dummynode, nodes=[expr])

# Add the initialization expression in the symbol
# constant_value attribute
ct_expr = dummynode.children[0].detach()
symbol.initial_value = ct_expr
symbol.is_constant = True
# Ensure the interface to this Symbol is static
symbol.interface = StaticInterface()
else:
# TODO #1254: We currently silently ignore the rest of
# the Implicit_Part statements
pass
# Any PARAMETER statements are handled separately by the
# call to _process_parameter_stmts below.
# TODO #1254: We currently silently ignore the rest of
# the Implicit_Part statements
pass
else:
raise NotImplementedError(
f"Error processing declarations: fparser2 node of type "
f"'{type(node).__name__}' not supported")

# Process the nodes again, looking for PARAMETER statements. This is
# done after the main declarations loop because they modify existing
# symbols and can appear in any order.
self._process_parameter_stmts(nodes, parent)

# We process the nodes again looking for common blocks. We do this
# here, after the main declarations loop, because they modify the
# interface of existing symbols and can appear in any order.
Expand Down
36 changes: 33 additions & 3 deletions src/psyclone/tests/psyir/frontend/fparser2_parameter_stmts_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@
from fparser.common.readfortran import FortranStringReader
from fparser.two.Fortran2003 import Specification_Part
from psyclone.psyir.frontend.fparser2 import Fparser2Reader
from psyclone.psyir.nodes import Routine, Literal, BinaryOperation, \
Container, CodeBlock, Reference
from psyclone.psyir.symbols import Symbol, StaticInterface
from psyclone.psyir.nodes import (
Routine, Literal, BinaryOperation, Container, CodeBlock, Reference,
UnaryOperation)
from psyclone.psyir.symbols import Symbol, StaticInterface, ScalarType


@pytest.mark.usefixtures("f2008_parser")
Expand Down Expand Up @@ -230,3 +231,32 @@ def test_unsupported_parameter_statements_produce_codeblocks(fortran_reader,
END SUBROUTINE my_sub
END MODULE my_mod
'''


def test_parameter_before_decln(fortran_reader):
'''
Test when a PARAMETER statement occurs *before* the named symbol is
actually declared.
'''
psyir = fortran_reader.psyir_from_source('''\
module test_mod
implicit none
PARAMETER(MPI_DISPLACEMENT_CURRENT = - 54278278)
INTEGER*8 :: MPI_DISPLACEMENT_CURRENT
PARAMETER(MPI_TROUBLE = atan(-1.0))
real :: mpi_trouble
contains

subroutine some_sub()
end subroutine some_sub
end module test_mod
''')
# We should have succeeded in parsing the code and creating a Container.
assert isinstance(psyir.children[0], Container)
sym = psyir.children[0].symbol_table.lookup("MPI_DISPLACEMENT_CURRENT")
# The Symbol should be a runtime constant with an initial value.
assert sym.is_constant
assert isinstance(sym.initial_value, UnaryOperation)
sym2 = psyir.children[0].symbol_table.lookup("MPI_TROUBLE")
assert sym2.is_constant
assert sym2.datatype.intrinsic == ScalarType.Intrinsic.REAL
Loading