Skip to content
Merged
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
3 changes: 3 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
28) PR #3543 for #3392. Stores commonblock names inside their interfaces
and adds them into the output.

27) PR #3542 towards #3516. Improve handling of declaration comments.

26) PR #3545 for #3537. Fix issues with WHERE constructs with comments.
Expand Down
6 changes: 6 additions & 0 deletions src/psyclone/psyir/backend/fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,12 @@ def gen_vardecl(self,
if symbol.inline_comment != "":
result += f" {self._COMMENT_PREFIX}{symbol.inline_comment}"

if isinstance(symbol, Symbol) and symbol.is_commonblock:
result += (
f"\n{self._nindent}common /{symbol.interface.name}/ "
f"{symbol.name}"
)

return result + "\n"

def gen_interfacedecl(self, symbol):
Expand Down
29 changes: 11 additions & 18 deletions src/psyclone/psyir/frontend/fparser2.py
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,8 @@ def _fparser2_tree_from_fparser2_reader(
parse_tree = Fortran2003.Pointer_Assignment_Stmt(source_code)
elif partial_code == "statement":
parse_tree = Fortran2003.Execution_Part(reader)
elif partial_code == "specs":
parse_tree = Fortran2003.Specification_Part(reader)
# When parsing intermediate expressione a None value
# is the same as a NoMatch, unrecognised 'partial_code'
# values will also be considered a NoMatch
Expand Down Expand Up @@ -2891,17 +2893,14 @@ def _process_data_statements(nodes, psyir_parent):
sym.interface = StaticInterface()

@staticmethod
def _process_common_blocks(nodes, psyir_parent):
''' Process the fparser2 common block declaration statements. This is
done after the other declarations and it will keep the statement
as a UnsupportedFortranType and update the referenced symbols to a
CommonBlockInterface.
def _process_common_blocks(nodes: list[Base], psyir_parent: ScopingNode):
''' Process the fparser2 common block declaration statements. This
is done after the symbols have already been created, it just assigns
the CommonBlockInterface to them.

:param nodes: fparser2 AST nodes containing declaration statements.
:type nodes: List[:py:class:`fparser.two.utils.Base`]
:param psyir_parent: the PSyIR Node with a symbol table in which to
add the Common Blocks and update the symbols interfaces.
:type psyir_parent: :py:class:`psyclone.psyir.nodes.ScopingNode`

:raises NotImplementedError: if one of the Symbols in a common block
has initialisation (including when it is a parameter). This is not
Expand All @@ -2913,22 +2912,16 @@ def _process_common_blocks(nodes, psyir_parent):
'''
for node in nodes:
if isinstance(node, Fortran2003.Common_Stmt):
# Place the declaration statement into a UnsupportedFortranType
# (for now we just want to reproduce it). The name of the
# commonblock is not in the same namespace as the variable
# symbols names (and there may be multiple of them in a
# single statement). So we use an internal symbol name.
psyir_parent.symbol_table.new_symbol(
root_name="_PSYCLONE_INTERNAL_COMMONBLOCK",
symbol_type=DataSymbol,
datatype=UnsupportedFortranType(str(node)))

# Get the names of the symbols accessed with the commonblock,
# they are already defined in the symbol table but they must
# now have a common-block interface.
try:
# Loop over every COMMON block defined in this Common_Stmt
for cb_object in node.children[0]:
Comment thread
LonelyCat124 marked this conversation as resolved.
# Get the name of the common block
name = cb_object[0]
name_str = name.string if name is not None else ""

for symbol_name in cb_object[1].items:
sym = psyir_parent.symbol_table.lookup(
str(symbol_name))
Expand All @@ -2939,7 +2932,7 @@ def _process_common_blocks(nodes, psyir_parent):
f" ({sym.initial_value.debug_string()}) "
f"but appears in a common block. This is "
f"not valid Fortran.")
sym.interface = CommonBlockInterface()
sym.interface = CommonBlockInterface(name_str)
except KeyError as error:
raise NotImplementedError(
f"The symbol interface of a common block variable "
Expand Down
38 changes: 36 additions & 2 deletions src/psyclone/psyir/symbols/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
''' This module contains the SymbolInterface class and its subclasses. '''

from enum import Enum
from typing import Any

# pylint: disable=too-few-public-methods

Expand Down Expand Up @@ -67,10 +68,43 @@ def __str__(self):

class CommonBlockInterface(SymbolInterface):
''' A symbol declared in the local scope but acts as a global that
can be accessed by any scope referencing the same CommonBlock name.'''
can be accessed by any scope referencing the same CommonBlock name.

:param common_block_name: the name of the common block.

:raises TypeError: if the common_block_name is not a str
'''

def __init__(self, common_block_name: str):
super().__init__()
if not isinstance(common_block_name, str):
raise TypeError(
f"The common block name should be a valid string, but"
f" found '{type(common_block_name).__name__}'")
self._name = common_block_name

def __str__(self):
return "CommonBlock"
return f"CommonBlock '{self._name}'"

def __eq__(self, other: Any) -> bool:
return (
super().__eq__(other) and
self._name.lower() == other.name.lower()
)

@property
def name(self) -> str:
'''
:returns: the name of the common block.

'''
return self._name

def copy(self) -> 'CommonBlockInterface':
'''
:returns: a copy of this object.
'''
return self.__class__(self._name)


class UnresolvedInterface(SymbolInterface):
Expand Down
9 changes: 6 additions & 3 deletions src/psyclone/tests/psyir/backend/fortran_common_block_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,16 @@ def test_fw_common_blocks(fortran_reader, fortran_writer, tmpdir):
assert code == (
"subroutine sub()\n"
" integer :: a\n"
" common /name1/ a\n"
" integer :: b\n"
" common /name1/ b\n"
" integer :: c\n"
" common /name1/ c\n"
" real :: d\n"
" common /name2/ d\n"
" real :: e\n"
" common // e\n"
" real :: f\n"
" COMMON /name1/ a, b\n"
" COMMON /name1/ c /name2/ d\n"
" COMMON // e, f\n\n\n"
" common // f\n\n\n"
"end subroutine sub\n")
assert Compile(tmpdir).string_compiles(fortran_writer(psyir))
80 changes: 32 additions & 48 deletions src/psyclone/tests/psyir/frontend/fparser2_common_block_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
from fparser.two.Fortran2003 import Specification_Part
from psyclone.psyir.frontend.fparser2 import Fparser2Reader
from psyclone.psyir.nodes import Routine
from psyclone.psyir.symbols import (
CommonBlockInterface, ScalarType, UnsupportedFortranType)
from psyclone.psyir.symbols import CommonBlockInterface, ScalarType


@pytest.mark.usefixtures("f2008_parser")
Expand All @@ -33,15 +32,11 @@ def test_named_common_block():
fparser2spec = Specification_Part(reader)
processor.process_declarations(routine, fparser2spec.content, [])

# There is a name1 commonblock symbol
commonblock = symtab.lookup("_PSYCLONE_INTERNAL_COMMONBLOCK")
assert isinstance(commonblock.datatype, UnsupportedFortranType)
assert commonblock.datatype.declaration == "COMMON /name1/ a, b, c"

# The variables have been updated to a common block interface
assert isinstance(symtab.lookup("a").interface, CommonBlockInterface)
assert isinstance(symtab.lookup("b").interface, CommonBlockInterface)
assert isinstance(symtab.lookup("c").interface, CommonBlockInterface)
name1_cb = CommonBlockInterface('name1')
assert symtab.lookup("a").interface == name1_cb
assert symtab.lookup("b").interface == name1_cb
assert symtab.lookup("c").interface == name1_cb

# The same common block can also bring other variables in a separate
# statement
Expand All @@ -52,12 +47,8 @@ def test_named_common_block():
fparser2spec = Specification_Part(reader)
processor.process_declarations(routine, fparser2spec.content, [])

# This is stored in a separate symbol, but the declaration has the right
# text
commonblock_2 = symtab.lookup("_PSYCLONE_INTERNAL_COMMONBLOCK_1")
assert commonblock_2.datatype.declaration == "COMMON /name1/ d, e, f"
assert isinstance(symtab.lookup("d").interface, CommonBlockInterface)
assert isinstance(symtab.lookup("e").interface, CommonBlockInterface)
assert symtab.lookup("d").interface == name1_cb
assert symtab.lookup("e").interface == name1_cb
fsym = symtab.lookup("f")
assert isinstance(fsym.interface, CommonBlockInterface)
assert fsym.datatype.intrinsic is ScalarType.Intrinsic.REAL
Expand All @@ -79,48 +70,46 @@ def test_unnamed_commonblock():
fparser2spec = Specification_Part(reader)
processor.process_declarations(routine, fparser2spec.content, [])

# There is an UnsupportedFortranType symbol containing the commonblock
commonblock = symtab.lookup("_PSYCLONE_INTERNAL_COMMONBLOCK")
assert isinstance(commonblock.datatype, UnsupportedFortranType)
assert commonblock.datatype.declaration == "COMMON // a, b, c"

# The variables have been updated to a common block interface
assert isinstance(symtab.lookup("a").interface, CommonBlockInterface)
assert isinstance(symtab.lookup("b").interface, CommonBlockInterface)
assert isinstance(symtab.lookup("c").interface, CommonBlockInterface)
# The variables have been updated to the unnamed common block interface
unnamed_cb = CommonBlockInterface("")
assert symtab.lookup("a").interface == unnamed_cb
assert symtab.lookup("b").interface == unnamed_cb
assert symtab.lookup("c").interface == unnamed_cb


@pytest.mark.usefixtures("f2008_parser")
def test_multiple_commonblocks_in_statement():
def test_multiple_commonblocks_and_comments():
''' Test that common block statements with multiple common blocks
are handled correctly.'''
and comments are handled correctly.'''

# Create a dummy test routine
routine = Routine.create("test_routine")
symtab = routine.symbol_table
processor = Fparser2Reader()

# And provide a common block containing two named blocks
reader = FortranStringReader('''
code = ('''
integer :: a, b, c, d
common /name1/ a, b /name2/ c
common /name2/ d''')
fparser2spec = Specification_Part(reader)
! This is the first common block
common /name1/ a, b /name2/ c ! Inline comment
! This is the second common block
common /name2/ d ! Inline comment
! Comment after
''')
fparser2spec = processor.generate_parse_tree_from_source(
code, partial_code="specs")
processor.process_declarations(routine, fparser2spec.content, [])

# There is a UnsupportedFortranType symbol containing each the commonblock
commonblock = symtab.lookup("_PSYCLONE_INTERNAL_COMMONBLOCK")
assert isinstance(commonblock.datatype, UnsupportedFortranType)
assert commonblock.datatype.declaration == "COMMON /name1/ a, b /name2/ c"
commonblock = symtab.lookup("_PSYCLONE_INTERNAL_COMMONBLOCK_1")
assert isinstance(commonblock.datatype, UnsupportedFortranType)
assert commonblock.datatype.declaration == "COMMON /name2/ d"

# The variables have been updated to a common block interface
assert isinstance(symtab.lookup("a").interface, CommonBlockInterface)
assert isinstance(symtab.lookup("b").interface, CommonBlockInterface)
assert isinstance(symtab.lookup("c").interface, CommonBlockInterface)
assert isinstance(symtab.lookup("d").interface, CommonBlockInterface)
name1_cb = CommonBlockInterface('name1')
name2_cb = CommonBlockInterface('name2')
assert symtab.lookup("a").interface == name1_cb
assert symtab.lookup("b").interface == name1_cb
assert symtab.lookup("c").interface == name2_cb
assert symtab.lookup("d").interface == name2_cb

# The comments are currently discarded
assert symtab.lookup("a").preceding_comment == ""


@pytest.mark.usefixtures("f2008_parser")
Expand All @@ -140,11 +129,6 @@ def test_named_commonblock_with_posterior_declaration():
fparser2spec = Specification_Part(reader)
processor.process_declarations(routine, fparser2spec.content, [])

# There is an UnsupportedFortranType symbol containing the commonblock
commonblock = symtab.lookup("_PSYCLONE_INTERNAL_COMMONBLOCK")
assert isinstance(commonblock.datatype, UnsupportedFortranType)
assert commonblock.datatype.declaration == "COMMON /name1/ a, b"

# The variables have been updated to a common block interface
assert isinstance(symtab.lookup("a").interface, CommonBlockInterface)
assert isinstance(symtab.lookup("b").interface, CommonBlockInterface)
Expand Down
26 changes: 22 additions & 4 deletions src/psyclone/tests/psyir/symbols/interfaces_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,30 @@ def test_static_interface():


def test_commonblockinterface():
'''Test we can create an CommonBlockInterface instance and check its
__str__ value
'''Test we can create an CommonBlockInterface instance and tests its
__str__, __eq__, copy, and property methods.

'''
interface = CommonBlockInterface()
assert str(interface) == "CommonBlock"
interface = CommonBlockInterface("name")
interface.name == "name"
assert str(interface) == "CommonBlock 'name'"

# Interfaces can be unnamed
interface2 = CommonBlockInterface("")
interface2.name == ""
assert str(interface2) == "CommonBlock ''"

# Check that they only accept strings
with pytest.raises(TypeError) as err:
_ = CommonBlockInterface(3)
assert ("The common block name should be a valid string, but found 'int'"
in str(err.value))

# Test copy and equality
assert interface != interface2
copy = interface.copy()
assert interface is not copy
assert interface == copy


def test_unresolvedinterface():
Expand Down
2 changes: 1 addition & 1 deletion src/psyclone/tests/psyir/symbols/symbol_table_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2886,7 +2886,7 @@ def test_rename_symbol_errors():

# Cannot rename a common block symbol
asym = symbols.DataSymbol("a", symbols.ScalarType.integer_type(),
interface=symbols.CommonBlockInterface())
interface=symbols.CommonBlockInterface(""))
table.add(asym)
with pytest.raises(symbols.SymbolError) as err:
table.rename_symbol(asym, "b")
Expand Down
2 changes: 1 addition & 1 deletion src/psyclone/tests/psyir/symbols/symbol_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def test_symbol_interface_setter_and_is_properties():
assert not symbol.is_commonblock
assert not symbol.is_unknown_interface

symbol.interface = CommonBlockInterface()
symbol.interface = CommonBlockInterface("")
assert not symbol.is_automatic
assert not symbol.is_import
assert not symbol.is_argument
Expand Down
Loading