Skip to content

A subprogram whose declined draws outnumber its trials fails by name; a dummy handed to what might write it is not read-only - #33

Merged
chenyueqi merged 2 commits into
mainfrom
declined-draws-and-escaping-dummies
Sep 6, 2026
Merged

A subprogram whose declined draws outnumber its trials fails by name; a dummy handed to what might write it is not read-only#33
chenyueqi merged 2 commits into
mainfrom
declined-draws-and-escaping-dummies

Conversation

@chenyueqi

Copy link
Copy Markdown
Member

Closes rows 12, 13 and 14 of #32 -- the three that were on main, from #30.

Declined draws are bounded and repeated (rows 12, 13). differential.bitexact declines a draw the candidate stops or overruns on (SystemExit, IndexError) and one both sides take to NaN, and draws again. It did so up to 24 times a trial, counted the redraws in a per-subprogram metric nothing repeated, and passed once one draw survived: a translation that stopped on inputs the source accepts passed on the survivors. Now:

  • a subprogram whose declined draws outnumber the trials it was compared on fails by name, with the count, the reason of each decline (error stop, subscript past extent, NaN on both sides) and the remedy (ranges, or dims). The reshaped rule, which names the extent, still speaks first.
  • a passing verdict repeats the declined count and reasons in its detail; per-subprogram metrics carry declined by reason beside redrawn.

The whole-trial NaN redraw is kept -- what both sides do after a NaN is the compiler's scheduling -- but it is now counted against the same bound and said aloud.

Read-only is proved, not assumed (row 14). _infer_read_only_intents took a parenthesised reference whose base is not one of this file's subprograms for a subscript, and never walked a function reference, an internal WRITE or an ASSOCIATE; a dummy modified through a use-associated function, which parses exactly like a subscript, was inferred intent(in), dropped from the wrapper's outputs and compared on neither side. The pass now knows the scope's variables (dummies, locals, local parameters, module state): a base it does not name is a call whose variable actuals escape, an expression actual is a temporary and does not, the WRITE unit and the ASSOCIATE selector escape. What cannot be proved read-only stays UNKNOWN, and the gate refuses the routine by name, as before #30.

Held to: full suite (916), mypy, ruff green. Corpus re-run: no unit moves (31/67 mechanical, 433 deferred); minpack's chkder is now named in its unit's detail (19 draws declined, 19 error stop, against 10 trials -- mode takes two of the eight default integer values), a unit already failed on dogleg's moved extents. The intent tightening moves nothing in the corpus.

Tests: the redraw tests now assert the reason and the verdict's mention of it; two new ones hold the bound for an error stop and for NaN; one new interface test holds each escape (function actual, internal WRITE, ASSOCIATE) and each non-escape (array subscript, expression actual).

🤖 Generated with Claude Code

https://claude.ai/code/session_01HYzzevwvTuLBMYjzdHUhGg

chenyueqi and others added 2 commits September 4, 2026 22:49
… a dummy handed to what might write it is not read-only

Three rows of #32 (12, 13, 14), all on main since #30 merged.

* The differential gate declines a draw the candidate stops or overruns
  on (a translated ERROR STOP, a subscript past a dummy's extent) and one
  both sides take to NaN, and draws again. It did so up to 24 times per
  trial, counted the redraws in a per-subprogram metric nobody repeated,
  and passed once one draw survived -- so a translation that stopped on
  inputs the source accepts passed on the survivors, one at a time. Now
  a subprogram whose declined draws outnumber the trials it was compared
  on fails by name, with the count, the reason of each decline (error
  stop, subscript past extent, NaN on both sides) and the remedy: narrow
  the draw with `ranges` or pin `dims`. The reshaped rule, which names
  the extent, still speaks first. A passing verdict repeats the declined
  count and reasons in its detail; the per-subprogram metrics carry
  `declined` by reason beside `redrawn`.

* Read-only intent inference took a parenthesised reference whose base
  is not one of this file's subprograms for a subscript, and never
  walked a function reference, an internal WRITE or an ASSOCIATE. A
  dummy modified through a use-associated function -- which parses
  exactly like a subscript -- was inferred `intent(in)`, dropped from
  the wrapper's outputs and compared on neither side. The pass now knows
  the scope's variables (dummies, locals, local parameters, module
  state); a base it does not name is a call whose variable actuals
  escape, an expression actual is a temporary and does not, and the
  WRITE unit and the ASSOCIATE selector escape. What cannot be proved
  read-only stays UNKNOWN, and the gate keeps refusing the routine by
  name, as it did before #30.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HYzzevwvTuLBMYjzdHUhGg
minpack's chkder takes two of the eight values the default integer range
draws for mode, and was compared on the survivors: 19 draws declined
against 10 trials. It is now named in the unit's detail with the reason
and the remedy (pin mode with `ranges`); the unit was already failed on
dogleg's moved extents. No other unit moves; the per-subprogram metrics
gain `declined` by reason.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HYzzevwvTuLBMYjzdHUhGg
@chenyueqi chenyueqi added bug Something isn't working area:verifier the gates: static.rwset, differential.bitexact/tolerance, oracles area:frontend Fortran parsing, interface extraction, constant folding labels Sep 6, 2026
chenyueqi added a commit that referenced this pull request Sep 6, 2026
… and #35

The table is unchanged. Per-subprogram metrics now carry PR #31's `shaped`
and `input_profile` and PR #33's `declined` by reason, and minpack's chkder
is named for its declined draws as PR #33's own re-record named it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018vrAgr6x1yLT47EumS39L5
@chenyueqi
chenyueqi merged commit 83f791b into main Sep 6, 2026
chenyueqi added a commit that referenced this pull request Sep 6, 2026
…-bounds

The CLUBB stack and this branch extended the same functions in different
directions; where git could not merge them, this is what was decided:

- An Expr has both a `str` node (a character value) and a `call` node (a
  folded intrinsic); `render` spells both. Constant folding is main's
  typed `with_integer_division`, with the `str` short-cut kept.
- One ALLOCATE scanner walks parentheses across continuation lines for
  both readers: an object's components (`COMPONENT_ALLOCATION`) and a
  module allocatable's own shape (`ALLOCATION`); `ALLOCATE_THIS` goes.
- The recorder keeps this branch's guard for an array the run never
  allocated -- poison of the planned shape -- and generalizes it to CLUBB's
  case: an extent the run owns (`extent_args`, written from `size()`) is
  spelled zero under the guard, so `c%coef` under `sclr_dim = 0` is
  recorded as `(ngrdcol, 0)` of nothing, the shape the kernel's dims
  declare, rather than main's `(0,0)` record. `_record_component` goes;
  the CLUBB test asserts the new spelling.
- The replay reads a logical (`T`/`F`) before it takes any other text as a
  character value. The flat adapter names a type only where a dummy of it
  is declared, and reaches a private specific through its generic.
- The JAX call rewrite takes main's host-call preamble and positional
  list, narrowed to the emitted convention: optionals as keywords, OUT
  scalars omitted, an OUT array passed only where it is the caller's
  buffer (the `buffer` mark, not `dims`). The elided-buffer table is
  main's positional one with this branch's temporary for an OUT scalar the
  anchor unpacks; an unpassed OUT array stays refused.
- The single-exit rewrite is this branch's flag-based one (returns inside
  loops, the guarded remainder as one block), with main's placement of the
  flag and placeholder after the statements before the first return, the
  placeholder shaped like the final value where that is already bound.
  The flag guard now applies at every block level through the visit of
  each branch, which is what main's `_fold_returns` covered (`if (bad)
  then; if (worse) return; y = y + 1; return`); that helper and
  `_guard_after_returns` go.
- The kernel lowerer knows both the trace-time statics and the names that
  size an array or bound a range; a literal store to either stays Python.
- The JAX runtime exports both sides' shims once; SUM folds sequentially
  along an axis too. The NumPy SUM is main's for the same reason.
- `_ret`-flag tests: the statics marker may precede a loop's done flag.
- Ruff and mypy on this branch's own files: long lines wrapped, a
  re-exported constant imported from its home, duplicate runtime
  definitions dropped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018vrAgr6x1yLT47EumS39L5
@chenyueqi
chenyueqi deleted the declined-draws-and-escaping-dummies branch September 6, 2026 18:05
chenyueqi added a commit that referenced this pull request Sep 7, 2026
…#23)

The scalar half landed with PR #33; an array stayed UNKNOWN ("mutates
through its buffer") and the differential gate refuses a subprogram over
an UNKNOWN intent, so recast-clubb carried a table of ten array dummies
with the direction their bodies give them.

Written and never read is ``intent(out)`` -- when every element is: a
bare name, a full section, or a subscript that is, on every axis, the
variable of an enclosing DO whose bounds are to the letter the declared
bounds (``do k = 1, nz; do i = 1, ngrdcol; wp4(i, k) = ...``), and not
under an IF. Written in part (``lhs(i, 2:nzm-1)`` beside boundary rows, a
write in one arm of an IF) or read is ``intent(inout)``: the elements the
body leaves are the caller's. Only read is ``intent(in)``. A write through
a subprogram of the same file counts -- an actual handed to a dummy the
callee declares or was inferred to write -- and one handed to a callee's
``intent(in)`` dummy is a read, not an escape; a dummy handed to a
procedure the file does not describe stays UNKNOWN. The two passes run
twice so a callee's inferred intent reaches its callers.

Measured: recast-clubb's ten overrides come out identical from the
inference, and every one of CLUBB_core's 67 modules has the same intents
with the table and without it, so the table can go with nothing to
re-verify. The corpus baseline moves where it should: bspline's BLAS
module, refused whole over ``dx``/``dy``, is compared now (dscal, drotmg
bit-exact; the rest have their own reasons).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:frontend Fortran parsing, interface extraction, constant folding area:verifier the gates: static.rwset, differential.bitexact/tolerance, oracles bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant