Skip to content
Open
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
104 changes: 101 additions & 3 deletions src/psyclone/psyir/symbols/symbol_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import inspect
import copy
import logging
import re
from typing import Any, List, Optional, Union, TYPE_CHECKING

from psyclone.configuration import Config
Expand All @@ -57,6 +58,7 @@
ImportInterface, RoutineSymbol, Symbol, SymbolError, UnresolvedInterface)
from psyclone.psyir.symbols.intrinsic_symbol import IntrinsicSymbol
from psyclone.psyir.symbols.typed_symbol import TypedSymbol
from psyclone.psyir.symbols.datatypes import UnsupportedFortranType

if TYPE_CHECKING:
from psyclone.psyir.nodes.scoping_node import ScopingNode
Expand Down Expand Up @@ -608,6 +610,8 @@ def add(self, new_symbol: Symbol, tag: Optional[str] = None):
:raises InternalError: if the new_symbol argument is not a symbol.
:raises KeyError: if the symbol name is already in use.
:raises KeyError: if a tag is supplied and it is already in use.
:raises KeyError: if the symbol is a COMMON-block marker and an
identical declaration is already present under another marker name.
:raises SymbolError: if the supplied symbol has an ImportInterface that
refers to a ContainerSymbol that is not in scope.

Expand All @@ -621,6 +625,22 @@ def add(self, new_symbol: Symbol, tag: Optional[str] = None):
raise KeyError(f"Symbol table already contains a symbol with "
f"name '{new_symbol.name}'.")

# Treat a COMMON-block marker whose declaration exactly matches one
# already present (possibly under a different name) as a duplicate.
if (self._normalize(new_symbol.name).startswith(
"_psyclone_internal_commonblock")
and isinstance(new_symbol.datatype, UnsupportedFortranType)):
if any(
sym.datatype.declaration == new_symbol.datatype.declaration
for sym in self.symbols
if (self._normalize(sym.name).startswith(
"_psyclone_internal_commonblock")
and isinstance(sym.datatype, UnsupportedFortranType))
):
raise KeyError(
f"Symbol table already contains a COMMON-block marker "
f"with the same declaration as '{new_symbol.name}'.")

if tag:
if tag in self.get_tags():
raise KeyError(
Expand Down Expand Up @@ -704,6 +724,12 @@ def check_for_clashes(self, other_table, symbols_to_skip=()):
isinstance(other_sym, IntrinsicSymbol)):
continue

# If both symbols have CommonBlockInterface, they represent the
# same shared COMMON-block data. They cannot (and do not need to)
# be renamed, so treat this as a benign clash.
if this_sym.is_commonblock and other_sym.is_commonblock:
continue

if other_sym.is_import and this_sym.is_import:
# Both symbols are imported. That's fine as long as they have
# the same import interface (are imported from the same
Expand Down Expand Up @@ -945,6 +971,7 @@ def _add_symbols_from_table(self, other_table, symbols_to_skip=()):
already been updated to refer to a Container in this table.

'''

for old_sym in other_table.symbols:

if old_sym in symbols_to_skip or isinstance(old_sym,
Expand All @@ -959,11 +986,73 @@ def _add_symbols_from_table(self, other_table, symbols_to_skip=()):
# We have a clash with a symbol in this table.
self._handle_symbol_clash(old_sym, other_table)

def _handle_symbol_clash_common_block(self, old_sym: Symbol) -> bool:
'''
Handles a name clash for COMMON-block related symbols. Called from
:py:meth:`_handle_symbol_clash` as soon as a COMMON-block symbol is
detected. Returns ``True`` if the clash has been fully resolved
(nothing more to do) or ``False`` if the generic rename-and-add path
should be followed instead.

Two kinds of COMMON-block symbol are handled:

* Variables with a
:py:class:`~psyclone.psyir.symbols.CommonBlockInterface`
(``is_commonblock``): the clash has already been approved by
``check_for_clashes``; nothing to do.
* Internal marker symbols (``_PSYCLONE_INTERNAL_COMMONBLOCK_N``):
if the incoming marker's COMMON-block name(s) overlap with any
marker already in ``self``, the block is already declared and a
second declaration would produce a compile error — skip it.
Otherwise fall through to the rename-and-add path.

:param old_sym: the Symbol being added to self.

:returns: ``True`` if the clash is resolved; ``False`` if the
generic rename-and-add path should be followed.

'''
try:
self_sym = self.lookup(old_sym.name)
except KeyError:
# old_sym.name is not in this table: add() must have raised
# because an identical declaration is already present under a
# different marker name. The COMMON block is already declared
# so the incoming marker should simply be skipped.
self_sym = None

if self_sym is None:
# Name absent means same-declaration / different-name duplicate.
return True

if old_sym.is_commonblock and self_sym.is_commonblock:
# check_for_clashes has already approved this; nothing to do.
return True

if (isinstance(old_sym.datatype, UnsupportedFortranType)
and isinstance(self_sym.datatype, UnsupportedFortranType)
and self._normalize(old_sym.name).startswith(
"_psyclone_internal_commonblock")):
# Marker with different declaration but possibly overlapping
# block name(s). Skip if the block is already declared.
_blk_re = re.compile(r"/\s*(\w*)\s*/", re.IGNORECASE)
old_blocks = set(_blk_re.findall(old_sym.datatype.declaration))
for sym in self.symbols:
if (self._normalize(sym.name).startswith(
"_psyclone_internal_commonblock")
and isinstance(sym.datatype, UnsupportedFortranType)):
self_blocks = set(_blk_re.findall(
sym.datatype.declaration))
if old_blocks & self_blocks:
return True

return False

def _handle_symbol_clash(self, old_sym, other_table):
'''
Adds the supplied Symbol to the current table in the presence
of a name clash. `check_for_clashes` MUST have been called
prior to this method in order to check for any unresolvable cases.
Adds the supplied Symbol to the current table in the presence of a
name clash. ``check_for_clashes`` MUST have been called prior to this
method in order to check for any unresolvable cases.

:param old_sym: the Symbol to be added to self.
:type old_sym: :py:class:`psyclone.psyir.symbols.Symbol`
Expand All @@ -976,6 +1065,15 @@ def _handle_symbol_clash(self, old_sym, other_table):
check_for_clashes()).

'''
# Check for COMMON-block markers first, before any lookup, because
# add() may have rejected old_sym because an identical declaration
# already exists under a *different* name. In that case old_sym.name
# is not in this table at all, and lookup() would raise a KeyError.
if old_sym.is_commonblock or self._normalize(old_sym.name).startswith(
"_psyclone_internal_commonblock"):
if self._handle_symbol_clash_common_block(old_sym):
return

self_sym = self.lookup(old_sym.name)
if old_sym.is_import:
# The clashing symbol is imported from a Container and the table
Expand Down
Loading
Loading