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
13 changes: 10 additions & 3 deletions src/psyclone/psyir/nodes/call.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,12 +487,15 @@ def copy(self):

return new_copy

def get_callees(self) -> List[Routine]:
def get_callees(self, load_external_files: bool = True) -> List[Routine]:
'''
Searches for the implementation(s) of all potential target routines
for this Call. It does *not* attempt to resolve static polymorphism
by checking the argument types.

:param load_external_files: allow this method to load external files
to find the needed declarations.

:returns: the Routine(s) that this call targets.

:raises NotImplementedError: if the routine is not found or a
Expand Down Expand Up @@ -583,7 +586,9 @@ def get_callees(self) -> List[Routine]:
else:
target_name = cursor.name
try:
container = csym.find_container_psyir(local_node=self)
container = csym.find_container_psyir(
local_node=self,
load_external_files=load_external_files)
except SymbolError:
raise NotImplementedError(
f"RoutineSymbol '{rsym.name}' is imported from "
Expand Down Expand Up @@ -828,6 +833,7 @@ def get_argument_map(self, routine: Routine) -> List[int]:
def get_callee(
self,
use_first_callee_and_no_arg_check: bool = False
load_external_files: bool = True
) -> Tuple[Routine, List[int]]:
'''
Searches for the implementation(s) of the target routine for this Call
Expand All @@ -842,14 +848,15 @@ def get_callee(
:param use_first_callee_and_no_arg_check: whether or not (the default)
to just find the first potential callee without checking its
arguments.
:param load_external_files: allow this method to load external files
to find the needed declarations.

:returns: A tuple of two elements. The first element is the routine
that this call targets. The second one a list of arguments
providing the information on matching argument indices.

:raises NotImplementedError: if the routine is not local and not found
in any containers in scope at the call site.

'''
routine_list = self.get_callees()

Expand Down
48 changes: 39 additions & 9 deletions src/psyclone/psyir/symbols/containersymbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,15 @@
# -----------------------------------------------------------------------------

''' This module contains the ContainerSymbol and its interfaces.'''
from typing import TYPE_CHECKING, Optional

from psyclone.psyir.symbols.symbol import Symbol, SymbolError
from psyclone.psyir.symbols.interfaces import SymbolInterface
from psyclone.configuration import Config

if TYPE_CHECKING: # pragma: no cover
from psyclone.psyir.nodes import Container, Node


class ContainerSymbol(Symbol):
''' Symbol that represents a reference to a Container. The reference
Expand Down Expand Up @@ -125,7 +129,11 @@ def copy(self):
new_symbol.is_intrinsic = self.is_intrinsic
return new_symbol

def find_container_psyir(self, local_node=None):
def find_container_psyir(
self,
local_node: Optional['Node'] = None,
load_external_files: bool = True
) -> 'Container':
''' Searches for the Container that this Symbol refers to. If it is
not available, use the interface to import the container. If
`local_node` is supplied then the PSyIR tree below it is searched for
Expand All @@ -134,6 +142,8 @@ def find_container_psyir(self, local_node=None):
:param local_node: root of PSyIR sub-tree to include in search for
the container.
:type local_node: Optional[:py:class:`psyclone.psyir.nodes.Node`]
:param load_external_files: allow this method to load external files
to find the needed declarations.

:returns: referenced container.
:rtype: :py:class:`psyclone.psyir.nodes.Container`
Expand All @@ -151,7 +161,8 @@ def find_container_psyir(self, local_node=None):
self._reference = local
return self._reference
# We didn't find it so now attempt to import the container.
self._reference = self._interface.get_container(self._name)
self._reference = self._interface.get_container(
self._name, load_external_files)
return self._reference

def __str__(self):
Expand Down Expand Up @@ -212,12 +223,18 @@ def is_intrinsic(self, value):
class ContainerSymbolInterface(SymbolInterface):
''' Abstract implementation of the ContainerSymbol Interface '''

@staticmethod
def get_container(name):
def __init__(self):
self._container_psyir = None

def get_container(self, name: str, load_external_files: bool = True):
''' Abstract method to import an external container, the specific
implementation depends on the language used.

:param str name: name of the external entity to be imported.
:param name: name of the external entity to be imported.
:param load_external_files: whether to search, parse and link an
external source file to populate the required container PSyIR
node (doing this operation in an already created PSyIR is
expensive, so explore using the RESOLVE_IMPORTS option first).

:raises NotImplementedError: this is an abstract method.
'''
Expand All @@ -227,12 +244,15 @@ def get_container(name):
class FortranModuleInterface(ContainerSymbolInterface):
''' Implementation of ContainerSymbolInterface for Fortran modules '''

@staticmethod
def get_container(name):
def get_container(self, name: str, load_external_files: bool = True):
''' Imports a Fortran module as a PSyIR Container (via the
ModuleManager) and returns it.

:param str name: name of the module to be imported.
:param name: name of the module to be imported.
:param load_external_files: whether to search, parse and link an
external source file to populate the required container PSyIR
node (doing this operation in an already created PSyIR is
expensive, so explore using the RESOLVE_IMPORTS option first).

:returns: container associated with the given name.
:rtype: :py:class:`psyclone.psyir.nodes.Container`
Expand All @@ -241,6 +261,15 @@ def get_container(name):
import path.

'''
if not load_external_files:
if self._container_psyir is None:
raise SymbolError(
f"Module '{name}' has not been loaded and linked to the "
f"local PSyIR symbol. Use the RESOLVE_IMPORTS parameter "
f" in the psyclone script or the 'load_external_files="
"True' in this method to attempt to do so.")
return self._container_psyir

# pylint: disable-next=import-outside-toplevel
from psyclone.parse import ModuleManager
mod_manager = ModuleManager.get()
Expand All @@ -259,7 +288,8 @@ def get_container(name):
raise SymbolError(
f"Module '{name}' not found in any of the include_paths "
f"directories {Config.get().include_paths}.")
return minfo.get_psyir()
self._container_psyir = minfo.get_psyir()
return self._container_psyir


# For Sphinx AutoAPI documentation generation
Expand Down
3 changes: 2 additions & 1 deletion src/psyclone/tests/psyir/nodes/call_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1495,7 +1495,8 @@ def test_call_get_callees_resolved_not_found(fortran_reader, monkeypatch):
" but the source defining that container could not be found. The "
"module search path is set to [" in str(err.value))
monkeypatch.setattr(ContainerSymbol, "find_container_psyir",
lambda _1, local_node=None: None)
lambda _1, local_node=None,
load_external_files=False: None)
with pytest.raises(NotImplementedError) as err:
_ = call.get_callees()
assert ("RoutineSymbol 'this_one' is imported from Container 'another_mod'"
Expand Down
10 changes: 5 additions & 5 deletions src/psyclone/tests/psyir/symbols/containersymbol_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def test_containersymbol_resolve_external_container(monkeypatch):
sym = ContainerSymbol("my_mod")

monkeypatch.setattr(sym._interface, "get_container",
lambda x: "MockContainer")
lambda x, _: "MockContainer")

# At the beginning the reference is never resolved (lazy evaluation)
assert not sym._reference
Expand All @@ -170,14 +170,14 @@ def test_containersymbol_resolve_external_container(monkeypatch):

# Check that subsequent invocations do not update the container reference
monkeypatch.setattr(sym._interface, "get_container",
staticmethod(lambda x: "OtherContainer"))
staticmethod(lambda x, _: "OtherContainer"))
assert sym.find_container_psyir() == "MockContainer"


def test_containersymbol_generic_interface():
'''Check ContainerSymbolInterface abstract methods '''

abstractinterface = ContainerSymbolInterface
abstractinterface = ContainerSymbolInterface()

with pytest.raises(NotImplementedError) as error:
abstractinterface.get_container("name")
Expand All @@ -188,13 +188,13 @@ def test_containersymbol_fortranmodule_interface(monkeypatch, tmpdir):
'''Check that the FortranModuleInterface imports Fortran modules
as containers or produces the appropriate errors'''

fminterface = FortranModuleInterface
fminterface = FortranModuleInterface()
path = str(tmpdir)

# Try with a non-existent module and no include path
monkeypatch.setattr(Config.get(), "_include_paths", [])
with pytest.raises(SymbolError) as error:
fminterface.get_container("fake_module")
fminterface.get_container("fake_module", True)
assert ("Module 'fake_module' not found in any of the include_paths "
"directories []." in str(error.value))

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 @@ -328,7 +328,7 @@ def test_get_external_symbol(monkeypatch):
# Monkeypatch the container's FortranModuleInterface so that it always
# appears to be unable to find the "some_mod" module

def fake_import(name):
def fake_import(name, load_external_files):
raise SymbolError("Oh dear")
monkeypatch.setattr(other_container._interface, "get_container",
fake_import)
Expand Down
Loading