What a dummy argument may say: local-parameter bounds, lower bounds, generics, where masks, elementwise logicals - #25
What a dummy argument may say: local-parameter bounds, lower bounds, generics, where masks, elementwise logicals#25chenyueqi wants to merge 4 commits into
Conversation
A bound may name a local parameter of the subprogram (#18): CLUBB's w_term_ma_zt_lhs declares integer, parameter :: t_above = 1, t_below = 2 and sizes weights_zt2zm(ngrdcol, nzm, t_above:t_below) with them. The interface record folds such a bound to the parameter's literal value, and says so under folded_bounds; the wrapper compiled with an undeclared name before, and the sampler had no table for it. A lower bound may not be one (#24): lhs(-2:2, ngrdcol, ndim) has five rows. The flat adapter and the plain wrapper now declare the axis lb:ub, and the bit-exact gate sizes it ub - lb + 1, so both sides hand the callee the layout it was written for instead of two rows and silence. A specific of a public generic is public through it (#17): tridiag_lu_solve over three private specifics left nothing the flat wrapper could spell. The record marks each specific public with public_via, and the adapter uses and calls the generic name, which is the only one that resolves. The where-construct's masks are scaffolding (#19): _wn and _we<d>_<n>, the unclaimed remainder and a masked elsewhere's own mask, join _wm on the read/write check's discard list; new_pdf's sort_roots reported them as target-only reads and writes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cne4hrGgYqhTMzVgzJtXYd
…oo; re-exports go through the generic CLUBB's new_pdf writes any( .not. l_calc_mixt_frac ) over a logical array, and the emitter spelled Python's scalar not, which raises on an array. The WHERE-mask rule -- ~, &, | -- now applies wherever an operand's rank is above zero; a rank the semantics cannot settle keeps the scalar spelling. The flat adapter re-exports every spellable subprogram of the module, and a private specific of a public generic among them is re-exported under the generic's name, which is the only one the module lets out. The first cut mapped only the subprograms with a plan; t_in_k_module and the banded solvers failed to build on the specifics' names. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cne4hrGgYqhTMzVgzJtXYd
… generic The record handed to the plain wrapper for the <module>_flat adapter dropped the generic table, so a specific re-exported under its generic's name was used and called under its own -- which the adapter does not let out. The table survives for the chosen specifics. CLUBB's t_in_k_module (4 subprograms), tridiag_lu_solvers (3) and penta_lu_solvers (2) go through bit-exact on it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cne4hrGgYqhTMzVgzJtXYd
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cne4hrGgYqhTMzVgzJtXYd
chenyueqi
left a comment
There was a problem hiding this comment.
Review of #25 against two questions: generic or CLUBB-specific, and any change to an existing rule.
Genericity: pass. All five items are Fortran-language or engine-internal rules. _wm/_wn/_we<d>_<n> are the numpy emitter's own WHERE-mask temporaries (statements.py, already on main); the discard regex simply lagged the emitter, and the new test checks the negatives. CLUBB appears only in docstrings. (The KINDS_64 domain-name list this branch carries comes from #22; commented there.)
Existing rules: no test modified or deleted, all six test files are additions. lb:ub sizing agrees with the translation's existing lower-bound handling (rules/indexing.py _origin, subprograms.py local sizing, all untouched). public_via flows to every consumer of public (modules.py, numpy_anchor.py, flatten.py, bitexact.py), which is the right semantics but wider than the description says. Two gaps inline: the bound fold is partial for symmetric -nd:nd bounds and nothing refuses the leftover, and _array_valued falls back to the scalar spelling when rank cannot be settled (main's behavior, but the description does not say the new rule is not fail-closed). folded_bounds is recorded in the facts JSON only; a line in reading-the-evidence.md would make it findable.
| if bound is None: | ||
| continue | ||
| name = str(bound).strip().lower() | ||
| if name in values: |
There was a problem hiding this comment.
Only a bare name folds, so dimension(-nd:nd, ...) becomes {"lb": "- nd", "ub": "3"}; the new test's lhs assertion documents exactly this and its comment says the leftover is "the wrapper's to refuse", but nothing refuses it: f2py.py::_extent spells - nd:3 into the wrapper verbatim (gfortran undeclared-symbol build failure, not an engine refusal) and bitexact.py harvests nd from the lb text as a free extent to redraw. -n:n is common Fortran (stencils, band matrices). Not a regression against main, but the description's "folded to the value" is half true for symmetric bounds. Suggest folding a negated name too, or refusing when one bound of an axis folded and the other did not.
| Unsettled ranks fall back to the scalar spelling, as before.""" | ||
| try: | ||
| return self.semantics.rank(node) > 0 | ||
| except Exception: # rank refuses what it cannot settle |
There was a problem hiding this comment.
Swallowing Unanalyzable here emits Python's scalar not/and/or for an operand whose rank the engine could not settle, and semantics.rank returns 0 for an undeclared Name without raising, so a whole-array logical use-imported from another module never reaches this branch at all and is spelled not x. At runtime that raises for len>1 arrays (loud) but not np.array([True]) is silently False. This is main's behavior, so the PR strictly improves coverage, but the rule is not fail-closed and the description does not say so; the docstring's "as before" is the only mention.
|
The places where this branch turns a refusal on main into a silent fallback are recorded apart from the review, in #32 (rows 5-6), so each is ruled on rather than merged by omission. A row there closes when the rule refuses again, records what it assumed in the evidence, or takes the assumption from the extension's configuration. |
Stacked on #22. Closes #17, #18, #19, #24. Found by the CLUBB extension's tier-0 units.
weights_zt2zm(ngrdcol, nzm, t_above:t_below)) is folded to the value in the interface record, recorded underfolded_bounds. The wrapper compiled with an undeclared name before; the sampler had no table for it.lb:ubkeeps its extent on both sides: the flat adapter and the plain wrapper declarelb:ub, the bit-exact gate sizesub - lb + 1.lhs(-2:2, ngrdcol, ndim)has five rows, not two.public_via); the flat adapter uses, calls and re-exports the generic, and the adapter module's own wrappers call it through the generic too, the only name the module lets out._wnand_we<d>_<n>join_wmon the read/write check's discard list.~,&,|) outside a WHERE too:any( .not. l_ok )over a logical array was emitted as Python's scalarnot.Full suite green. CLUBB tier 0 goes from 8/14 to 12/14 bit-exact on this branch (mean_adv, tridiag_lu_solvers, penta_lu_solvers, bicgstab_solvers; t_in_k_module gains its three generic specifics, new_pdf eight of nine subprograms). The two left are #20, complex values.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Cne4hrGgYqhTMzVgzJtXYd