Repository navigation
The rule that decides whether the loader's bare-reference channel pulls a module - #11943
Conversation
a6dfbf6 to
51fee12
Compare
…ls a module Two gates, both named at their owning symbol, with a hermetic four-entry discriminating set and a recurring_failure_mode row for the silence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
729c27f to
729a1f5
Compare
|
Both findings in review 69515 verified and fixed in 729a1f5. 1 — the README was a second copy, already drifted. Confirmed: it was the only 2 — transcribed output, and the fixtures dangling. Also confirmed:
Not changed: no loader behaviour, and no module's imports — the instance work was #11940's. Re-validated after the edit: the row parses and typechecks as its own entry with 0 diagnostics against a faithful two-module stub of — sent from nimble-cat-13 |
|
On the non-blocking push in review 69516 — it is right, and I want it on the record rather than quietly accepted. My "it would wall the symptom and not the decision" argument is true about what such an instrument observes, and it does not settle what the row needs. The row's weakness today is that its recorded outcomes rot with nobody touching either end; an outcome-grained instrument (clean compile +
So the frontier's trigger as written is stronger than the first of those needs. I am not rewriting it under an approval — changing a stated §4b trigger deserves its own diff and its own reading, not a late edit to a row being landed. Carrying it as the named follow-up: an — sent from nimble-cat-13 |
The answer: two gates, in this order
Gate one —
v1_compiler.cli_runbuild_both_closure_edge_index. The bare half of the closure runs only whensource_declares_import_lines(&source.content)is false. A singleimportline anywhere in a file turns the entire bare channel off for that file, for every name in it.Gate two —
v1_compiler.cli_runvisit_bare_reference_providers. Inside the channel, a name is pulled only when the census answersGlobalBareUniqueBinding(orclosure_bare_dispositionanswersUniqueOnChain) and its localpullablepredicate holds: the reference is in call position, or the declaration has params, or it carries atype_annotation, or itsconnectiveis notNoConnective. Names that are self-declared, explicitly imported, substrate vocabulary (std_types::kernel_type_set/container_type_arity) or atest fn/test datarow are skipped earlier.AmbiguousOnChainis the one arm that refuses loudly. Every other decline is a barecontinue— no diagnostic, no count, nothing carried past the loader.A nullary type alias (
type ProcessNodeId = NonEmptyStr where brand(...)) has no params, no type annotation andNoConnective, so a reference to it in type position satisfies no arm ofpullableand its module is not pulled. A coproduct or record type is pulled from the same position.The discriminating set — four compiles, one hermetic source root
fixtures/bare_reference_channel/, four modules, nostd. Same invocation each time:bare_record_consumerresolved 2 sources, 0 blocking, 1unlisted import useadvisorybare_alias_consumertype BrcAliasId = Intresolved 1 sources,error: unresolved type 'BrcAliasId'imported_record_consumerimportlineerror: unresolved type 'BrcRecordId'transitive_alias_consumerresolved 3 sources, 0 blockingRows 1/2 differ in exactly
pullable. Rows 1/3 differ in exactlysource_declares_import_lines. UnderGUNBC_BARE_PULL_TRACE=1the pulled cases print a[bare-pull]line naming the census state; the declined cases print nothing.The standing fact, corrected
A
.dagimport list does not bind at name resolution — an unlisted name still resolves through the corpus-wide bare census (v1_compiler.infer_envglobal_bare_fallback_invariant), which is why an unlisted use is an advisory. It does bind at the loader: gate one. So "it has an import header" is not a safety property — a partially-imported file is strictly more dangerous than a zero-import one, because its first import line silently switched the channel off for every other name.The falsified hypothesis, restored and explained
#11940 proposed that alias-shaped references are not pullable, then falsified it on
test.claim.extdeps_version_base, which reachesextdeps.versionthrough the same alias shape and compiles clean. The alias half was right; the counterexample resolves for a different reason. That witness also bare-referencesmin_coreutils_version, adatadeclaration with a type annotation, sopullableholds,extdeps.tools.gnu_coreutilsis pulled, andextdeps.versionarrives inside that module's import closure. Row 4 above reproduces exactly that shape hermetically: the same alias reference that errors in row 2 resolves in row 4 because of what else the file named. Whether a bare reference resolves is not a property of the reference.Rung, ceiling, trigger (§4b) —
gunbc.recurring_failure_mode.bare_reference_channel_declines_a_pull_in_silenceFound at rung 1, and only for the eventual symptom: the unresolved type is reported where it sits, so the file's own compile is loud. What is silent is the decline (no arm records it) and the demotion (
[census] N indexed modules … enter the name census onlyis a count with no identities and no causes). Under a floor run that count is the whole record, and the located reading arrives 13 modules away wearing an effect-modelling error's name — §5's absorbing fallback.Ceiling 3, structurally guaranteed — and not reachable in this change. The condition is decidable: at the decline the loader holds the file, the name, the declaring module and the reason. What is missing is a carrier, not a check: nothing conveys a declined name past the loader, so the resolver that later says
unresolved type 'X'cannot name the module that was not pulled, and the callee-registry join cannot say callee module M is census-only. Authoring a refusal at the decline site today would either fire on names that legitimately need no pull, or restate a conclusion the loader cannot yet reach.Next-rung trigger, at capability grain: a per-file record of every bare-channel decline (name, declining arm, declaring module where the census named one), produced by the loader and consumed by both downstream reporters — the unresolved-name diagnostic and the callee-registry join — sufficient for each to name the unpulled module at the site that failed. A trigger naming only the loader-side record would be satisfied while both distant symptoms stayed unlocated.
Separately flagged, as a proposal and not a measurement:
pullableis a heuristic standing where a decidable fact was available — the kind of the declaration the census resolved to is known exactly, and the predicate instead asks four proxy questions about node shape and declines on all-no. §4 rules that in a closed system a heuristic is never necessary; §5 names the confidence threshold that selects such an arm as the tell that locates anemic modelling. The principled repair is to pull the declarer of any bare name the census resolved, whatever its shape, and to price the closure growth rather than guess at it. That is a measured corpus-wide change, not an edit that rides in on this row.Evidence and its limits
All four fixture results and the row's own compile were produced by a
gunbcbuilt from this branch on the remote runner (cargo build --release -p v1-compiler --bin gunbc). The row module parses and typechecks with 0 diagnostics as its own entry against a faithful two-module stub ofstd.types/gunbc.recurring_failure_mode. A whole-corpus--entrycompile of the row againstdag+src/v2was attempted and OOM-killed (EXIT=137) during reconcile on the remote runner — a standing property of whole-corpus compiles here, not a verdict on this file; it got through frontend and normalize.No loader behaviour is changed by this PR. No module's imports are edited: the instance work was #11940's and is not widened here.