Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c13926c
#2202 use dependence-analysis code from parameter declns for all decl…
arporter Feb 20, 2026
9097665
#2202 small test fix [skip ci]
arporter Feb 23, 2026
0985ae4
#2202 fix tests
arporter Feb 23, 2026
8f68d2f
#2202 fix test when dist_mem is False
arporter Feb 23, 2026
453cd42
#2202 rm _gen_param_decls method
arporter Feb 23, 2026
72f247e
#2202 tidy implementation
arporter Feb 23, 2026
06a2702
#2202 add new (failing) test [skip ci]
arporter Feb 23, 2026
3ad77ac
Merge branch 'master' into 2202_decln_ordering
arporter Feb 25, 2026
669c1d4
Merge branch 'master' into 2202_decln_ordering
arporter Apr 28, 2026
1f815d4
#2202 fixes post merge
arporter Apr 28, 2026
4bca1f3
#2202 fix tests after merge
arporter Apr 28, 2026
e1d2f35
Merge branch 'master' into 2202_decln_ordering
arporter Apr 28, 2026
1bd9004
Merge branch 'master' into 2202_decln_ordering
arporter Apr 29, 2026
7c53c0a
#2202 add explicit test for ordering with declaration of complex type
arporter Apr 29, 2026
eb3dfdc
Merge branch 'master' into 2202_decln_ordering
arporter Sep 2, 2026
075cdbe
#2202 fix tests after merge
arporter Sep 2, 2026
084a8bd
#2202 fix doctest errors
arporter Sep 2, 2026
831a1d8
#2202 rm TODO referencing issue
arporter Sep 2, 2026
bf3336a
Merge branch 'master' into 2202_decln_ordering
arporter Sep 2, 2026
adca257
#2202 fix minor typo
arporter Sep 3, 2026
067999a
Merge branch 'master' into 2202_decln_ordering
arporter Oct 8, 2026
649054c
#2202 WIP fixing tests [skip ci]
arporter Oct 8, 2026
1aca6de
Merge branch 'master' into 2202_decln_ordering
arporter Oct 8, 2026
561119f
#2202 fix ordering of common-block statements in output
arporter Oct 8, 2026
36e9a37
#2202 WIP fixing tests after fixing ordering of syms without deps [sk…
arporter Oct 8, 2026
603070a
#2202 fix lint
arporter Oct 8, 2026
3b71578
#2202 ensure abstract interface declared last and WIP fixing ordering…
arporter Oct 8, 2026
a78ac86
#2202 add ruff section to pyproject.toml
arporter Oct 8, 2026
83e443a
#2202 shorten line length [skip ci]
arporter Oct 8, 2026
34a1313
#2202 WIP fixing tests [skip ci]
arporter Oct 9, 2026
437b89b
#2202 fix linting of test files [skip ci]
arporter Oct 9, 2026
415271e
#2202 Update tests for declaration ordering
arporter Oct 9, 2026
9c74394
#2202 Update reference-element stub test
arporter Oct 9, 2026
7826257
#2202 fix linting
arporter Oct 12, 2026
80bc457
#2202 fix top-level lfric tests
arporter Oct 12, 2026
5eefa39
#2202 fix decln ordering in psyad tests [skip ci]
arporter Oct 12, 2026
c19f445
#2202 add partial datatype when constructing symbols to replace selec…
arporter Oct 12, 2026
e12a64b
#2202 fixes to frontend tests
arporter Oct 12, 2026
3862ea4
For #2202 - fix all tests under psyir.
arporter Oct 12, 2026
eae56ef
#2202 more test fixes [skip ci]
arporter Oct 12, 2026
36ae0f7
#2202 fix linting
arporter Oct 12, 2026
a9424f3
Merge branch 'master' into 2202_decln_ordering
arporter Oct 14, 2026
babe93c
#2202 fix psyad tests
arporter Oct 14, 2026
5c13124
#2202 fix doctest failures
arporter Oct 15, 2026
29c3405
#2202 give symbols associated with DATA stmts a fake partial_datatype…
arporter Oct 15, 2026
8506716
Merge branch 'master' into 2202_decln_ordering
arporter Oct 15, 2026
f0f502f
#2202 WIP re-ordering test output [skip ci]
arporter Oct 16, 2026
77dcca8
#2202 tidy change to backend [skip ci]
arporter Oct 16, 2026
d75b049
#2202 fix decln ordering in psyir tests [skip ci]
arporter Oct 19, 2026
faa9d40
Merge branch 'master' into 2202_decln_ordering
arporter Oct 19, 2026
58598ca
#2202 wip fixing top-level tests [skip ci]
arporter Oct 19, 2026
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
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,7 @@ version = { attr = "psyclone.version.__VERSION__" }
xfail_strict = true
addopts = "--ignore=external -p no:pep8"
filterwarnings = ["ignore::DeprecationWarning"]

[tool.ruff]
# Set the maximum line length to 79 characters
line-length = 79

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a local change for your linter?

227 changes: 100 additions & 127 deletions src/psyclone/psyir/backend/fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,8 @@ def __init__(self, **kwargs):
Fparser2Reader.binary_operators)

# Create and store a CallTreeUtils instance for use when ordering
# parameter declarations. Have to import it here as CallTreeUtils
# also uses this Fortran backend.
# declarations. Have to import it here as CallTreeUtils also uses
# this Fortran backend.
# pylint: disable=import-outside-toplevel
from psyclone.psyir.tools.call_tree_utils import CallTreeUtils
self._call_tree_utils = CallTreeUtils()
Expand Down Expand Up @@ -712,7 +712,7 @@ def _gen_common_block_decls(self, symbols: list[Symbol]) -> str:

:param symbols: symbols that may belong to common blocks.

:returns: the common-block declarations.
:returns: the common-block declarations, ordered by name.

:raises VisitorError: if two Symbols in the same common block have
the same position.
Expand All @@ -726,9 +726,12 @@ def _gen_common_block_decls(self, symbols: list[Symbol]) -> str:
name = symbol.interface.name.lower()
common_blocks.setdefault(name, []).append(symbol)

# Order the symbols by their commonblock interface position
declarations = ""
for name, members in common_blocks.items():
# Order the common-block declarations alphabetically by name.
for name in sorted(common_blocks.keys()):
members = common_blocks[name]
# Order the symbols within the commonblock by their commonblock
# interface position
positions = [symbol.interface.position for symbol in members]
if len(positions) != len(set(positions)):
raise VisitorError(
Expand Down Expand Up @@ -947,78 +950,17 @@ def gen_access_stmts(self, symbol_table: SymbolTable) -> str:
return result
return ""

# pylint: disable=too-many-branches
def _gen_parameter_decls(self, symbol_table, is_module_scope=False):
''' Create the declarations of all parameters present in the supplied
symbol table. Declarations are ordered so as to satisfy any inter-
dependencies between them.

:param symbol_table: the SymbolTable instance.
:type symbol: :py:class:`psyclone.psyir.symbols.SymbolTable`
:param bool is_module_scope: whether or not the declarations are in
a module scoping unit. Default is False.

:returns: Fortran code declaring all parameters.
:rtype: str

:raises VisitorError: if there is no way of resolving
interdependencies between parameter declarations.

'''
declarations = ""
local_constants = []

# First add the local constants
for sym in symbol_table.datasymbols:
if sym.is_import or sym.is_unresolved:
continue # Skip, these don't need declarations
if sym.is_constant:
local_constants.append(sym)

# There may be dependencies between these constants so setup a dict
# holding a set of all their dependencies. The checks have to be done
# with case-insensitive name comparisons because the dependent symbols
# are not always created in the same scope.
local_lowered_names = [sym.name.lower() for sym in local_constants]
decln_inputs = {}
for symbol in local_constants:
dependencies = symbol.get_all_accessed_symbols()
dependencies = {sym for sym in dependencies
# Discard self-dependencies: e.g. "a :: HUGE(a)"
if sym.name.lower() != symbol.name.lower() and
# Discard dependencies that are not local
sym.name.lower() in local_lowered_names}
decln_inputs[symbol] = dependencies

# We now iterate over the declarations, declaring those that have their
# inputs satisfied. Creating a declaration for a given symbol removes
# that symbol as a dependence from any outstanding declarations and
# adds it to the 'declared' set.
declared: set[Symbol] = set()
while local_constants:
for symbol in local_constants[:]:
inputs = decln_inputs[symbol]
if inputs.issubset(declared):
# All inputs are satisfied so this declaration can be added
declared.add(symbol)
local_constants.remove(symbol)
declarations += self.gen_vardecl(
symbol, include_visibility=is_module_scope)
break
else:
# We looped through all of the variables remaining to be
# declared and none had their dependencies satisfied.
raise VisitorError(
f"Unable to satisfy dependencies for the declarations of "
f"{[sym.name for sym in local_constants]}")
return declarations

def gen_decls(self,
symbol_table: SymbolTable,
is_module_scope: bool = False) -> str:
'''Create and return the Fortran declarations for the supplied
SymbolTable.

Declarations are ordered such that any given symbol is declared after
those upon which it depends. Stricly speaking, the Fortran standard
does not mandate this in the majority of cases but compiler
implementations do not always follow the standard.

:param symbol_table: the SymbolTable instance.
:param is_module_scope: whether or not the declarations are in
a module scoping unit. Default is False.
Expand All @@ -1038,6 +980,8 @@ def gen_decls(self,
RoutineSymbols) in the supplied table that do not have an
explicit declaration (UnresolvedInterface) and there are no
wildcard imports or unknown interfaces.
:raises VisitorError: if there is no way of resolving interdependencies
between symbol declarations.

'''
# pylint: disable=too-many-branches
Expand Down Expand Up @@ -1069,20 +1013,22 @@ def gen_decls(self,

# If the symbol table contains any symbols with an
# UnresolvedInterface interface (they are not explicitly
# declared), we need to check that we have at least one
# wildcard import which could be bringing them into this
# scope, or an unknown interface which could be declaring
# them.
# declared), we need to check that we have at least one wildcard
# import which could be bringing them into this scope, or an
# unknown interface which could be declaring them.
unresolved_symbols = []
for sym in all_symbols[:]:
if isinstance(sym.interface, UnresolvedInterface):
unresolved_symbols.append(sym)
all_symbols.remove(sym)
try:
internal_interface_symbol = symbol_table.lookup(
"_psyclone_internal_interface")
except KeyError:
internal_interface_symbol = None

internal_interface_symbol = symbol_table.lookup(
"_psyclone_internal_interface", otherwise=None)
if internal_interface_symbol and (internal_interface_symbol in
all_symbols):
# We will declare an abstract interface last.
all_symbols.remove(internal_interface_symbol)

if unresolved_symbols and not (
symbol_table.wildcard_imports() or
internal_interface_symbol or
Expand All @@ -1102,64 +1048,91 @@ def gen_decls(self,
raise VisitorError(
f"Found a symbol '{sym.name}' with a name greater than "
f"{self.MAX_VARIABLE_NAME_LENGTH} characters in length. "
"This is not standards-compliant Fortran.")

# As a convention, we will declare the variables in the following
# order:
f"This is not standards-compliant Fortran.")

# 1: Routine declarations and interfaces. (Note that accessibility
# statements are generated in gen_access_stmts().)
for sym in all_symbols[:]:
if not isinstance(sym, RoutineSymbol):
continue
# Interfaces can be GenericInterfaceSymbols or RoutineSymbols
# of UnsupportedFortranType.
if isinstance(sym, GenericInterfaceSymbol):
declarations += self.gen_interfacedecl(sym)
elif isinstance(sym.datatype, UnsupportedType):
declarations += self.gen_vardecl(
sym, include_visibility=is_module_scope)
elif not (sym.is_modulevar or sym.is_automatic):
raise VisitorError(
f"Routine symbol '{sym.name}' has '{sym.interface}'. "
f"This is not supported by the Fortran back-end.")
all_symbols.remove(sym)

# 2: Constants.
declarations += self._gen_parameter_decls(symbol_table,
is_module_scope)
for sym in all_symbols[:]:
if isinstance(sym, DataSymbol) and sym.is_constant:
all_symbols.remove(sym)
# There may be dependencies between the symbols so setup a dict
# holding a set of all their dependencies. The checks have to be done
# with case-insensitive name comparisons because the dependent symbols
# are not always created in the same scope.
local_lowered_names = [sym.name.lower() for sym in all_symbols]
decln_inputs: dict[str, set[Symbol]] = {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't usually add type hints for variable declarations- this one has made it through. I seem to remember there was some discussion ongoing on this so I'm nmot sure if we made a decision?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also some that have appeared in tests (so I don't forget)

for symbol in all_symbols:
dependencies = symbol.get_all_accessed_symbols()
dependencies = {sym for sym in dependencies
# Discard self-dependencies: e.g. "a :: HUGE(a)"
if sym.name.lower() != symbol.name.lower() and
# Discard dependencies that are not local
sym.name.lower() in local_lowered_names and
# Discard dependencies on RoutineSymbols (but
# *not* interfaces)
not (isinstance(sym, RoutineSymbol) and
sym is not internal_interface_symbol and
not isinstance(sym, GenericInterfaceSymbol))}
decln_inputs[symbol] = dependencies

# 3: Argument variable declarations
# Sanity check that we haven't got arguments if we're in a module scope
if symbol_table.argument_datasymbols and is_module_scope:
raise VisitorError(
f"Arguments are not allowed in this context but this symbol "
f"table contains argument(s): "
f"'{[sym.name for sym in symbol_table.argument_datasymbols]}'."
)
# We use symbol_table.argument_datasymbols because it has the
# symbol order that we need
for symbol in symbol_table.argument_datasymbols:
declarations += self.gen_vardecl(
symbol, include_visibility=is_module_scope)
all_symbols.remove(symbol)

# 4: Derived-type declarations. These must come before any declarations
# of symbols of these types.
for symbol in all_symbols[:]:
if isinstance(symbol, DataTypeSymbol):
declarations += self.gen_typedecl(
symbol, include_visibility=is_module_scope)
all_symbols.remove(symbol)

# 5: The rest of the symbols
for symbol in all_symbols:
# We now iterate over the declarations, declaring those that have their
# inputs satisfied. Creating a declaration for a given symbol removes
# that symbol as a dependence from any outstanding declarations and
# adds it to the 'declared' set.
declared: set[Symbol] = set()

while all_symbols:

# Find those Symbols which have their dependencies satisfied.
do_next: list[Symbol] = [sym for sym in all_symbols if
decln_inputs[sym].issubset(declared)]
if not do_next:
# We looped through all of the variables remaining to be
# declared and none had their dependencies satisfied.
raise VisitorError(
f"Unable to satisfy dependencies for the declarations of "
f"{[sym.name for sym in all_symbols]}")

# Output the declarations of those Symbols that have their
# dependencies satisfied.
new_declns = []
for symbol in do_next:
declared.add(symbol)
all_symbols.remove(symbol)
if isinstance(symbol, RoutineSymbol):
# Interfaces can be GenericInterfaceSymbols or
# RoutineSymbols of UnsupportedFortranType.
if isinstance(symbol, GenericInterfaceSymbol):
new_declns.append(self.gen_interfacedecl(symbol))
elif isinstance(symbol.datatype, UnsupportedType):
new_declns.append(self.gen_vardecl(
symbol, include_visibility=is_module_scope))
elif not (symbol.is_modulevar or symbol.is_automatic):
raise VisitorError(
f"Routine symbol '{symbol.name}' has "
f"'{symbol.interface}'. This is not supported "
f"by the Fortran back-end.")
elif isinstance(symbol, DataTypeSymbol):
new_declns.append(self.gen_typedecl(
symbol, include_visibility=is_module_scope))
else:
new_declns.append(self.gen_vardecl(
symbol, include_visibility=is_module_scope))
# Sort each section of declarations for readability.
declarations += "".join(sorted(new_declns))
# Now that we've created new declaration(s) (and thus
# potentially resolved some dependencies) we go back to
# the start of the list of remaining symbols.

if internal_interface_symbol:
declarations += self.gen_vardecl(
symbol, include_visibility=is_module_scope)
internal_interface_symbol,
include_visibility=is_module_scope)

declarations += self._gen_common_block_decls(all_symbols)
declarations += self._gen_common_block_decls(list(declared))

return declarations

Expand Down
Loading