From f944a708691707f21a2af70663b7fa0117e9b9da Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Mon, 11 Dec 2023 17:09:34 +0000 Subject: [PATCH] #2422 add Fortran backend support for the new symbol --- src/psyclone/psyir/backend/fortran.py | 29 ++++++++++++----- src/psyclone/psyir/frontend/fparser2.py | 4 +-- .../psyir/backend/fortran_gen_decls_test.py | 32 +++++++++++++++---- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index 8180ad8b58..d8c6594f53 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -50,9 +50,9 @@ Operation, Range, Routine, Schedule, UnaryOperation) from psyclone.psyir.symbols import ( ArgumentInterface, ArrayType, ContainerSymbol, DataSymbol, DataTypeSymbol, - DeferredType, RoutineSymbol, ScalarType, Symbol, IntrinsicSymbol, - SymbolTable, UnknownFortranType, UnknownType, UnresolvedInterface, - StructureType) + DeferredType, GenericInterfaceSymbol, IntrinsicSymbol, RoutineSymbol, + ScalarType, StructureType, Symbol, SymbolTable, UnknownFortranType, + UnknownType, UnresolvedInterface) # Mapping from PSyIR types to Fortran data types. Simply reverse the @@ -528,8 +528,9 @@ def gen_vardecl(self, symbol, include_visibility=False): f" from a Fortran USE statement and should be " f"generated by 'gen_use' instead of " f"'gen_vardecl'.") - if isinstance(symbol, RoutineSymbol) and not \ - isinstance(symbol.datatype, UnknownFortranType): + if (isinstance(symbol, RoutineSymbol) and + not isinstance(symbol, GenericInterfaceSymbol) and + not isinstance(symbol.datatype, UnknownFortranType)): raise InternalError(f"Symbol '{symbol.name}' is a RoutineSymbol " f"which is not imported nor an interface " f"(UnknownFortranType). This is already " @@ -537,6 +538,15 @@ def gen_vardecl(self, symbol, include_visibility=False): f"and should not be provided to 'gen_vardecl'." ) + if isinstance(symbol, GenericInterfaceSymbol): + decln = f"{self._nindent}interface {symbol.name}\n" + routines = ", ".join(sym.name for sym in symbol.maps_to) + self._depth += 1 + decln += f"{self._nindent}procedure :: {routines}\n" + self._depth -= 1 + decln += f"{self._nindent}end interface {symbol.name}\n" + return decln + # Whether we're dealing with an array declaration and, if so, the # shape of that array. if isinstance(symbol.datatype, ArrayType): @@ -935,10 +945,13 @@ def gen_decls(self, symbol_table, is_module_scope=False): # 1: Routines (Interfaces) for sym in all_symbols[:]: + # Interfaces to module procedures are captured by the frontend + # as either GenericInterfaceSymbols or RoutineSymbols of + # UnknownFortranType. These must therefore be declared. + if isinstance(sym, GenericInterfaceSymbol): + declarations += self.gen_vardecl( + sym, include_visibility=is_module_scope) if isinstance(sym, RoutineSymbol): - # Interfaces to module procedures are captured by the frontend - # as RoutineSymbols of UnknownFortranType. These must therefore - # be declared. if isinstance(sym.datatype, UnknownType): declarations += self.gen_vardecl( sym, include_visibility=is_module_scope) diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py index bfe0a7bd2f..b2815db209 100644 --- a/src/psyclone/psyir/frontend/fparser2.py +++ b/src/psyclone/psyir/frontend/fparser2.py @@ -2377,7 +2377,6 @@ def _process_interface_block(self, node, symbol_table, visibility_map): # with that name.) symbol_table.add(GenericInterfaceSymbol( name, rsymbols, - interface=UnknownInterface(), visibility=vis)) else: # We've not been able to determine the list of @@ -2385,7 +2384,6 @@ def _process_interface_block(self, node, symbol_table, visibility_map): # create a RoutineSymbol of UnknownFortranType. symbol_table.add(RoutineSymbol( name, - interface=UnknownInterface(), datatype=UnknownFortranType(str(node).lower()), visibility=vis)) except KeyError: @@ -2398,7 +2396,7 @@ def _process_interface_block(self, node, symbol_table, visibility_map): symbol_table.new_symbol( root_name=f"_psyclone_internal_{name}", symbol_type=RoutineSymbol, - interface=UnknownInterface(), + #interface=UnknownInterface(), datatype=UnknownFortranType(str(node).lower()), visibility=vis) diff --git a/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py b/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py index c38c17af57..fc919654df 100644 --- a/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py @@ -41,12 +41,12 @@ from psyclone.psyir.backend.visitor import VisitorError from psyclone.psyir.nodes import (Literal, Reference, BinaryOperation, Container, Routine, Return) -from psyclone.psyir.symbols import (Symbol, DataSymbol, DataTypeSymbol, - SymbolTable, ContainerSymbol, ScalarType, - DeferredType, StructureType, RoutineSymbol, - ImportInterface, UnresolvedInterface, - ArgumentInterface, INTEGER_TYPE, REAL_TYPE, - StaticInterface) +from psyclone.psyir.symbols import ( + ArgumentInterface, ContainerSymbol, DataSymbol, DataTypeSymbol, + DeferredType, GenericInterfaceSymbol, ImportInterface, + INTEGER_TYPE, REAL_TYPE, + RoutineSymbol, Symbol, SymbolTable, ScalarType, StaticInterface, + StructureType, UnresolvedInterface) def test_gen_param_decls_dependencies(fortran_writer): @@ -283,7 +283,8 @@ def test_gen_decls_static_variables(fortran_writer): @pytest.mark.parametrize("visibility", ["public", "private"]) -def test_visibility_interface(fortran_reader, fortran_writer, visibility): +def test_visibility_abstract_interface(fortran_reader, fortran_writer, + visibility): '''Test that PSyclone's Fortran backend successfully writes out public/private clauses and symbols when the symbol's declaration is hidden in an abstract interface. @@ -311,3 +312,20 @@ def test_visibility_interface(fortran_reader, fortran_writer, visibility): assert "public :: update_interface" not in result if visibility == "private": assert "private :: update_interface" in result + + +def test_procedure_interface(fortran_reader, fortran_writer): + '''Test that the Fortran backend correctly recreates an interface + declaration from a GenericInterfaceSymbol. + ''' + symbol_table = SymbolTable() + sub1 = RoutineSymbol("sub1") + symbol_table.add(sub1) + sub2 = RoutineSymbol("sub2") + symbol_table.add(sub2) + isub = GenericInterfaceSymbol("subx", [sub1, sub2]) + symbol_table.add(isub) + out = fortran_writer.gen_decls(symbol_table) + assert "interface subx" in out + assert "procedure :: sub1, sub2" in out + assert "end interface subx" in out