xparam.py Rules
February 10, 2026 ยท View on GitHub
This document defines the current rule set used by xparam.py to decide whether a Fortran variable can be made a named constant (parameter).
Scope
- Unit types analyzed:
program,subroutine,function. - Inputs: selected
.f90/.F90files. - Analysis is static and conservative.
Candidate Eligibility (high level)
A variable is a candidate only if all checks pass:
- It is a local declared variable in the unit.
- It is written exactly once.
- Its first write is not in control flow.
- Its first write is deterministic.
- Its initializer expression is PARAMETER-safe.
- It is not written in any contained internal procedure.
Declaration Rules
A declared local is excluded immediately if any of the following holds:
- It is a dummy argument.
- It is already declared with
parameter. - It is not a simple scalar declaration, including:
- explicit entity shape (e.g.
x(10)), pointer,target.
- explicit entity shape (e.g.
Allocatable arrays are handled separately:
- Default behavior: excluded.
- With
--fix-alloc: rank-1 deferred-shape form ((:)) may be considered.
What Counts as a Write
xparam.py increments write count for:
- Declaration initialization (
:: x = ...or pointer init form). - Assignment (
x = ...). - Pointer assignment (
x => ...). allocate(...),deallocate(...),nullify(...)on a variable.readdestinations.call random_number(x)output argument.- Actual arguments mapped to known same-file dummies with
intent(out)orintent(inout). - Assignments in contained internal procedures that target host variables.
A variable must have write count exactly 1.
Control-Flow Rule
First write must not occur in a control-flow context.
Control-flow contexts include block constructs such as:
if ... then ... end ifdo ... end doselect case/type/rank ... end selectwhere ... end whereforall ... end forallassociate ... end associateblock ... end blockcritical ... end critical
Single-line conditional forms are also treated as control flow:
if (cond) x = ...
If first write is in any control-flow context, variable is excluded.
Determinism Rule
First write is excluded as non-deterministic if it contains any of:
random_number,random_seedread,open,inquire,closesystem_clock,cpu_time,date_and_timeget_command_argument,get_environment_variableexecute_command_line
PARAMETER-Safe Expression Rule
Even if set once and deterministic, the first-write expression must be parameter-safe.
Current conservative checks:
- Reject if expression contains a function call pattern (
name(...)). - Collect identifier references (ignoring quoted string contents).
- Every referenced identifier must already be known parameter-like in the same unit analysis order.
This excludes runtime-derived expressions such as:
n = size(x)y = sum(x)/n
Internal Procedure Rule
If a local variable in a host procedure is written in any contained internal subroutine/function, it is excluded.
Example: host variable m assigned in internal bar => cannot be parameter.
Output Categories
constant candidate(s): variables that pass all rules.Excluded locals(with--verbose): variables rejected with reason text.
Fix Modes
--fix (conservative)
- Creates backup before first change in a file (
.bak,.bak1, ...). - Runs to a fixed point by default: re-analyzes and reapplies until no additional safe conversions remain.
- Rewrites only safe candidates where declaration line contains exactly one entity.
- Moves first assignment expression to declaration as
parameterinitializer. - Removes the moved assignment statement line.
- Skips multi-entity declaration lines (e.g.
integer :: i, n). - Reorders generated
parameterdeclarations within the unit as needed so referenced declarations appear earlier.
--fix-all (aggressive)
Everything in --fix, plus:
- Can split multi-entity declarations to extract candidate into its own
parameterdeclaration.
Example transformation:
- Before:
integer :: i, nandn = 3 - After:
integer :: iandinteger, parameter :: n = 3
--fix-alloc (opt-in gate)
- Enables promotion of eligible
allocatablearrays toparameter. - Currently limited to rank-1 deferred-shape declarations (
(:)), when:- write count is exactly one,
- initializer is deterministic and parameter-safe,
- fixed size can be inferred from initializer (currently rank-1
[ ... ]constructor length).
- During rewrite, incompatible attributes are removed and shape is made explicit.
Example transformation:
- Before:
integer, allocatable :: w(:)andw = [30, 40] - After:
integer, parameter :: w(2) = [30, 40]
Important Limitations
- This is not full semantic analysis.
- Unknown external procedure effects are not fully modeled unless intent mapping is available in same file.
- PARAMETER-safety uses conservative heuristics; valid Fortran constant expressions may still be rejected.
- Compile validation is not built into
xparam.py; review and compile after fixes.