Repository navigation
Builtin registry carries a signature, not a return type: BuiltinParam { name, ty } over all 132 rows - #9952
Merged
Conversation
added 5 commits
September 1, 2026 13:48
… { name, ty } and BuiltinSignature over all 132 rows
Types for the 20 registry names that std.algebra also declares are DERIVED from it
by lookup; get is disambiguated by the registry's own already-committed return type
rather than by a pick. Names are declared per row. Nothing consumes params yet --
infer_builtin_call_type still answers sig.returns, so decide_callable_candidates
still answers FuncSigUnresolved for a BuiltinCallable and no call site is judged.
…ng fabrication and concat's binary exemption The pairing fallback yields a marked type variable where a refusal belongs, and no consumer asserts its absence -- named as a fabricated substitution rather than left looking like a wall. concat stays binary because the algebra and the emitted runtime agree it is; what is missing is associativity, and Semigroup<T> is declared without its law.
…er names 17 (builtin, position, spelling) classes across 26 files: char_at i/index -> pos, string_contains haystack/needle/sub -> s/pattern, to_string n/i -> value, code_point ch/s -> c, is_xid_* ch -> cp, concat left/right -> a/b, replace pattern/delimiter/ replacement -> from/to. Nothing binds these today, so the labels were unchecked decoration and the corpus had accumulated five spellings for one parameter without anyone noticing. That is what the declaration closes. NO-OP PROVEN BY EXECUTION: required-regen emits the whole corpus and reports drift only on compiler_tests.rs, which predates this branch. The three src/v1 modules edited here have stage0 mirrors that DO drift when their .dag changes semantically -- 04_method.dag proved that on this same branch -- and they do not drift now, so the emitted bytes are identical and the labels bind nothing, measured rather than assumed. The codemod skips string literals: .dag files carry emitted source text as data, and a first pass without that filter rewrote a parameter name inside runtime_rust.dag's Rust signature while its body still used the old one. 393 matches were inside string bodies.
…nd undeclarable associativity
One row, not two, for the naming fork -- the interpreter's diagnostic literals and
runtime_rust.dag's emitted signatures share one capability, and splitting them would let
each look closable while the capability that closes both stayed dead.
The associativity stall is what concat_binary_signature_exempt_from_arg_binding waits on:
Magma<T> is { op: fn(T, T) -> T } and Semigroup<T> is { op: fn(T, T) -> T }, structurally
identical, so the type whose entire content is the associativity law carries no law. Its
population is UncountedNotEnumerable rather than a list, because nothing can currently ask
which operations depend on associativity.
Both rows pass every_live_stall_is_below_its_ceiling and _names_a_next_rung_trigger.
A wrong monomorphic signature that looks specific is worse than params: [], which at least announces that it knows nothing -- the rule that decided three questions here: no record-naming NamedTemplate, concat stays binary, types derived where std.algebra declares them. Regen confirms the annotation is erased: no emission drift.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Grounds the v1 builtin registry: it carried a return type per name and now carries a signature.
decide_callable_candidatesansweredFuncSigUnresolvedfor everyBuiltinCallableprecisely becauseparams: []"would lie about the call's shape to every consumer of a sig" — this is the change that makes that arm answerable. The arm is not flipped here. Nothing consumesparamsyet; that lands next, with a call-site diff that should be empty because this PR conforms them.What changed
src/v1/04_method.dag:All 132 rows converted, no remainder. That is a §3 requirement rather than tidiness: a registry where some rows declare parameters and some do not cannot distinguish a nullary builtin from an undeclared one, which is the state this closes. 45 rows say
params: []and mean it.infer_builtin_call_typestill answerssig.returns, so every existing consumer sees exactly what it saw.Provenance is not uniform, and one word "declared" over all of it would be rung inflation
std.algebraderived_signature(names:, method:, returns:)readsparam_typesout of the roster at construction. No choice in it.code_point(coverch),count(xsoveritems),is_xid_start/is_xid_continue/is_emoji_ident(cpoverch), themap_*family (m, resolving a fork insideruntime_rust.dag, which spells the map receivermin five functions andmapin one)measure() == measure().getforks, and the fork resolves without a judgement callgetis declared in three profiles with two signatures:(Self, Int) -> Element?infree_monoid_collection,(Self, Key) -> Value?in the two function profiles. A name-keyed registry cannot represent that. The collection arm is selected because the registry's own prior return type wasOptional<collection_element>, which matches that arm and not the other — a compatibility argument reading the structure being replaced, written as a declared choice so a later reader can reopen it against the algebra instead of finding a rule that looks derived.Measured before authoring the row: no call site reaches this row with a map receiver. Every free call passes a list and an index; method-position calls resolve through the receiver-keyed profile path, and
func_sig_from_global_bareshort-circuits every algebra method name before the registry. That a name-keyed registry cannot represent a receiver-polymorphic builtin is a §3 shape fact this change does not repair, and does not widen.Call sites conformed: 206 labels, 26 files, zero remaining
17
(builtin, position, spelling)classes:char_at i/index→pos;string_contains haystack/needle/sub→s/pattern;to_string n/i→value;code_point ch/s→c;is_xid_* ch→cp;concat left/right→a/b;replace pattern/delimiter/replacement→from/to. Re-measured after: 0.Builtin arguments bind positionally —
v1_interpreter.rsreaches every operand throughargs.iter().map(|(_, v)| v), the label is literally the wildcard — so these labels have never been judged by anything, which is how one parameter accumulated five spellings. That is what the declaration closes, and it is why the names are declared rather than read off the corpus.No-op proven by execution.
required-regenemits the whole corpus and drifts only oncompiler_tests.rs, which predates this branch. The discriminator: the threesrc/v1modules edited here have stage0 mirrors that do drift when their.dagchanges semantically —04_method.dagproved that on this same branch — and they do not drift now.The codemod near-miss, recorded because it nearly shipped the defect it was fixing. The first pass matched call sites anywhere in the file, and
.dagfiles carry emitted source text as data. It rewrotepub fn string_contains(s: &str, sub: String)→(s: &str, pattern: String)insideruntime_rust.dagwhile the body still saidsub— emitted Rust that would not compile, from a pass whose purpose was making names agree. Redone with a string mask: 393 matches were inside string bodies, and the count fell from 216 to 206.Two declared drops and two stalls, because this change creates classes as well as closing one
gunbc.rung_drop builtin_signature_arity_pairing_fabricates— when a row declares more names thanstd.algebradeclares templates, the pairing yieldsAlgebraTypeVariable { id: "unpaired_declared_parameter" }instead of refusing: a value standing where a refusal belongs. A.dagdatainitializer has no refusal channel, so the arms were a fabricated value or a silently shorter list, and a marked fabrication is the more honest of the two. No consumer asserts the marker's absence, so it is mitigatable, not a wall — an unchecked marker is an inert lens. Population closed at the 20 rows that pair at all. Trigger names the capability (construction-time refusal in adatainitializer); a witness over the marker would raise the rung but not retire the row, because the invalid state stays writable.gunbc.rung_drop concat_binary_signature_exempt_from_arg_binding—concatis declared binary and is binary correctly:std.algebrasays[ReceiverSelf, ReceiverSelf]andruntime_rust.dagemitspub fn concat<T: V2Concat>(a: T, b: T) -> T. The algebra and the emitted runtime agree; only the corpus calls it at 64 arities (12 sites at 1, 8297 at 2, 507 at 3, 200 at 4, 178 at 5, tail to 262).concat(a, b, c)is a fold, and what licenses folding it is associativity.gunbc.guarantee_rung_drop algebra_operation_associativity_undeclarable_stall— the grounding that drop waits on.Magma<T>is{ op: fn(T, T) -> T }andSemigroup<T>is{ op: fn(T, T) -> T }: structurally identical, differing by a blank line where the law would sit. The type whose entire content is the associativity law carries no law. Population isUncountedNotEnumerable, because the class is notconcat— it is every operation whose surface arity exceeds its declared arity, and nothing can currently ask which those are. §5 wall-after-grounding, not a ratchet.gunbc.guarantee_rung_drop builtin_parameter_name_forked_across_hand_authored_sites_stall— once names are declared, the interpreter's diagnostic literals ("substring start","char_at pos") andruntime_rust.dag's emitted signatures become two more hand-authored answers to a question that finally has an authority. One row, not two: the capability is identical for both sites, and splitting it would let each look closable while the capability that closes both stayed dead.The rule the rows are authored under
A wrong monomorphic signature that looks specific is worse than
params: [], which at least announces that it knows nothing. It decided three questions here, not one:NamedTemplatewas not extended to name a record. Measured: the hand-builtFilesystemReadResulthas 1 field;NamedTemplate { name: "FilesystemReadResult" }throughinstantiate_algebra_typeyields 0, with no diagnostic. The arm accepts any string and fabricates a fieldless nominal — safe today only because its four live instantiations happen to be genuinely fieldless.concatstays binary rather than being declared signature-less: the signature is expressible and true, and erasing a fact that holds is a different failure from asserting one that does not.std.algebradeclares them, rather than re-authored beside the authority that already answers.A note on how the disagreements got there
A surface that binds nothing accumulates disagreements silently. Four independent namings exist for these parameters — the algebra (none), the interpreter's diagnostics, the emitted runtime, and the call sites — and no two agree. The act of making the surface bind is what converts those from invisible to blocking. That is the real deliverable here, more than any single row.