checker: infer a type parameter through a trait bound (WEB F3) #101

Closed
opened 2026-10-05 21:07:20 +00:00 by orion · 13 comments
Collaborator

Context

render(counter) finds Message from Counter: Live<CounterMessage>.

Source

Acceptance criteria

  • The inference
  • Checker tests

Dependencies

None known.

Moved from the planning lists of the docs; the design record stays the specification.

## Context `render(counter)` finds `Message` from `Counter: Live<CounterMessage>`. ## Source - [WEB.md F3](https://git.torb.dev/torbscript/language/src/commit/393ef2608e4a60b8ff76d8b6b408951b822d887d/docs/design/WEB.md#L925) ## Acceptance criteria - [ ] The inference - [ ] Checker tests ## Dependencies None known. _Moved from the planning lists of the docs; the design record stays the specification._
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop). Implements WEB.md F3 (checker inference through a trait bound); no dependencies, no collision with a design record. Touches compiler/src (checker), so it is an opus/high implementer item. next: refine.

[loop:torbscript] intake: in scope (author orion, label loop). Implements WEB.md F3 (checker inference through a trait bound); no dependencies, no collision with a design record. Touches `compiler/src` (checker), so it is an opus/high implementer item. next: refine.
Author
Collaborator

[loop:torbscript] refine: still queued behind the intake order. next: waiting (ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z

[loop:torbscript] refine: still queued behind the intake order. next: waiting (ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261024 until 2026-10-24T03:30Z

[loop:torbscript] claim refine by session:loop-torbscript-20261024 until 2026-10-24T03:30Z
Author
Collaborator

[loop:torbscript] decided by the loop: the inference of F3 is already on main, so this issue shrinks to its remainder.

Findings at main (bc504c24):

  • Commit 6faeccd0 (feat(checker): infer a type parameter only a bound mentions from the trait's one implementation) implements the F3 inference: solveFromImplementation in compiler/src/semantics/checker/call.trb, documented as rule 8 in docs/language/generics/inference.md, with five checker tests in compiler/tests/inference.test.trb (group "A parameter only a bound mentions": one implementation solves it, a bound-typed parameter solves it, two implementations stay "Cannot infer", the expected type decides, a type without the trait says so). I confirmed it with the installed torb check (0.1.0): render(counter) with Counter with Live<CounterMessage> checks, a type implementing Live twice reports "Cannot infer Message of render", render(3) reports "Int64 does not implement Live<_>".
  • What is left of F3 in docs/design/WEB.md: the row still reads as open, problem 4 of section 1 still says the type arguments must be written out, and the second half of the row, "the panic of problem 1 fixed", has no test. I could not reproduce the Key does not exist panic (a call whose argument type stayed unsolved after "Cannot infer", for example counter.update(shown[0]) after two failed render(counter) calls on a type with two Live implementations): torb check answers with ordinary diagnostics. It is therefore either fixed by an earlier change or needs the exact probe-2 source; the first step of the work is to settle that with a test.

Scope of this issue from here (supersedes the acceptance list in the body; one PR, one or two Conventional Commits, no seed impact: no syntax change, no native, no driver flag, no std name looked up by string):

  1. Add a checker test in compiler/tests/inference.test.trb (or the matching checker test file) for the problem-1 shape: a "Cannot infer" diagnostic followed by a call whose argument type was never inferred must give diagnostics only, never a checker panic. If it does panic, fix the checker (the Index.at/expect("Key does not exist") lookup on a map of types that has no entry for the unsolved argument) and keep the test as the regression test; if it does not, the test stands as the proof.
  2. Update docs/design/WEB.md: mark F3 as (done, torbscript/language#101) in the section 8 table, and rewrite problem 4 of section 1 (and the probe-2 row of the table above it, which says the explicit type arguments are required) as closed, pointing at rule 8 in docs/language/generics/inference.md. Regenerate skills/ if the docs checks ask for it.
  3. Acceptance: the new test passes in forge CI, sh tools/gates.sh a is green in the PR's CI, tools/check-commits.sh is clean; the PR closes #101.
    Worker: checker (compiler/src) work only if the panic reproduces, so sonnet/high with a local quick torb check of the probe first; if it does not reproduce it is tests and docs (sonnet/medium). Not an epic, no further steps.
[loop:torbscript] decided by the loop: the inference of F3 is already on `main`, so this issue shrinks to its remainder. Findings at `main` (bc504c24): - Commit 6faeccd0 (`feat(checker): infer a type parameter only a bound mentions from the trait's one implementation`) implements the F3 inference: `solveFromImplementation` in `compiler/src/semantics/checker/call.trb`, documented as rule 8 in `docs/language/generics/inference.md`, with five checker tests in `compiler/tests/inference.test.trb` (group "A parameter only a bound mentions": one implementation solves it, a bound-typed parameter solves it, two implementations stay "Cannot infer", the expected type decides, a type without the trait says so). I confirmed it with the installed `torb check` (0.1.0): `render(counter)` with `Counter with Live<CounterMessage>` checks, a type implementing `Live` twice reports "Cannot infer `Message` of `render`", `render(3)` reports "`Int64` does not implement `Live<_>`". - What is left of F3 in `docs/design/WEB.md`: the row still reads as open, problem 4 of section 1 still says the type arguments must be written out, and the second half of the row, "the panic of problem 1 fixed", has no test. I could not reproduce the `Key does not exist` panic (a call whose argument type stayed unsolved after "Cannot infer", for example `counter.update(shown[0])` after two failed `render(counter)` calls on a type with two `Live` implementations): `torb check` answers with ordinary diagnostics. It is therefore either fixed by an earlier change or needs the exact probe-2 source; the first step of the work is to settle that with a test. Scope of this issue from here (supersedes the acceptance list in the body; one PR, one or two Conventional Commits, no seed impact: no syntax change, no native, no driver flag, no std name looked up by string): 1. Add a checker test in `compiler/tests/inference.test.trb` (or the matching checker test file) for the problem-1 shape: a "Cannot infer" diagnostic followed by a call whose argument type was never inferred must give diagnostics only, never a checker panic. If it does panic, fix the checker (the `Index.at`/`expect("Key does not exist")` lookup on a map of types that has no entry for the unsolved argument) and keep the test as the regression test; if it does not, the test stands as the proof. 2. Update `docs/design/WEB.md`: mark F3 as `(done, torbscript/language#101)` in the section 8 table, and rewrite problem 4 of section 1 (and the probe-2 row of the table above it, which says the explicit type arguments are required) as closed, pointing at rule 8 in `docs/language/generics/inference.md`. Regenerate `skills/` if the docs checks ask for it. 3. Acceptance: the new test passes in forge CI, `sh tools/gates.sh a` is green in the PR's CI, `tools/check-commits.sh` is clean; the PR closes #101. Worker: checker (`compiler/src`) work only if the panic reproduces, so sonnet/high with a local quick `torb check` of the probe first; if it does not reproduce it is tests and docs (sonnet/medium). Not an epic, no further steps.
Author
Collaborator

[loop:torbscript] refine: ready, claim released, next: implement. Scope narrowed to a regression test for the problem-1 panic plus the WEB.md F3 and problem 4 updates (see the decision comment above). Worker usage: inline RE worker (sonnet 5.5), about 70k tokens, no sub-spawns.

[loop:torbscript] refine: ready, claim released, next: implement. Scope narrowed to a regression test for the problem-1 panic plus the WEB.md F3 and problem 4 updates (see the decision comment above). Worker usage: inline RE worker (sonnet 5.5), about 70k tokens, no sub-spawns.
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261024 until 2026-10-24T05:30Z

[loop:torbscript] claim implement by session:loop-torbscript-20261024 until 2026-10-24T05:30Z
Author
Collaborator

[loop:torbscript] implement: PR #450 open (regression test for the problem-1 shape plus the WEB.md F3, problem 1 and problem 4 updates), waiting for forge CI, next: review

[loop:torbscript] implement: PR #450 open (regression test for the problem-1 shape plus the WEB.md F3, problem 1 and problem 4 updates), waiting for forge CI, next: review
Author
Collaborator

[loop:torbscript] implement: checkpoint, PR #450 is open (head 6964ef3b); after about 22 min all four forge CI checks (gates, commit messages, what the change touches, pdisc) are still pending, queued behind other runs, so no result yet. The test is unverified until gates run (no torb binary on the worker host, no local bootstrap by contract). next: implement continues (read CI of #450, fix the test expectation if red), then review. Worker usage: one inline implementer (sonnet 5.5), about 60k tokens, no sub-spawns. Claim kept.

[loop:torbscript] implement: checkpoint, PR #450 is open (head 6964ef3b); after about 22 min all four forge CI checks (gates, commit messages, what the change touches, pdisc) are still `pending`, queued behind other runs, so no result yet. The test is unverified until gates run (no `torb` binary on the worker host, no local bootstrap by contract). next: implement continues (read CI of #450, fix the test expectation if red), then review. Worker usage: one inline implementer (sonnet 5.5), about 60k tokens, no sub-spawns. Claim kept.
Author
Collaborator

[loop:torbscript] release implement: worker ended at its checkpoint with PR #450 open and forge CI queued; no fresh worker until CI reports. Worker usage: about 60k tokens. next: waiting (CI running on PR #450)

[loop:torbscript] release implement: worker ended at its checkpoint with PR https://git.torb.dev/torbscript/language/pulls/450 open and forge CI queued; no fresh worker until CI reports. Worker usage: about 60k tokens. next: waiting (CI running on PR #450)
Author
Collaborator

[loop:torbscript] claim fix by session:loop-torbscript-20261024 until 2026-10-24T04:04:24Z (red CI on the PR)

[loop:torbscript] claim fix by session:loop-torbscript-20261024 until 2026-10-24T04:04:24Z (red CI on the PR)
Author
Collaborator

[loop:torbscript] fix: caused by this PR, not inherited. Run 1518 tier A (linux-x64) failed only in the format --check gate ("1 of 1429 files would change": compiler/tests/inference.test.trb, the file this PR extends); every other gate lane passed (compiler tests, std/example packages native and VM, language, packages, docs, lock) and the PR base bc504c24 is green on tier A, so it is not the red-main bootstrap of #386. Reproduced with the 20261023 nightly (torb format --check, in the repo): the new test wrote const found = problems(source) where the canon wants the command form problems source. Fixed in 1dae64d3 (style(checker): write the call of the new test as a command, one line, no logic change); torb format --check on the file is now clean. CI of 1dae64d3 is queued behind the single runner and had not started when this worker ended (tier A alone takes about 25 min), next: waiting (forge CI of 1dae64d3 on #450; a fix worker re-checks only if it fails again).

[loop:torbscript] fix: caused by this PR, not inherited. Run 1518 `tier A (linux-x64)` failed only in the `format --check` gate ("1 of 1429 files would change": `compiler/tests/inference.test.trb`, the file this PR extends); every other gate lane passed (compiler tests, std/example packages native and VM, language, packages, docs, lock) and the PR base `bc504c24` is green on tier A, so it is not the red-main bootstrap of #386. Reproduced with the 20261023 nightly (`torb format --check`, in the repo): the new test wrote `const found = problems(source)` where the canon wants the command form `problems source`. Fixed in `1dae64d3` (`style(checker): write the call of the new test as a command`, one line, no logic change); `torb format --check` on the file is now clean. CI of `1dae64d3` is queued behind the single runner and had not started when this worker ended (tier A alone takes about 25 min), next: waiting (forge CI of 1dae64d3 on https://git.torb.dev/torbscript/language/pulls/450; a fix worker re-checks only if it fails again).
Author
Collaborator

[loop:torbscript] claim fix by session:loop-torbscript-20261024 until 2026-10-24T04:36:27Z

[loop:torbscript] claim fix by session:loop-torbscript-20261024 until 2026-10-24T04:36:27Z
Author
Collaborator

[loop:torbscript] fix: ci / commit messages failed because 1dae64d3 used the type style, which is not a Conventional Commit type of the repo (tools/check-commits.sh); squashed both commits into one test(checker): ... commit 30afeadb (identical tree), sh tools/check-commits.sh origin/main passes, next: waiting (forge CI of 30afeadb)

[loop:torbscript] fix: `ci / commit messages` failed because 1dae64d3 used the type `style`, which is not a Conventional Commit type of the repo (tools/check-commits.sh); squashed both commits into one `test(checker): ...` commit 30afeadb (identical tree), `sh tools/check-commits.sh origin/main` passes, next: waiting (forge CI of 30afeadb)
argus closed this issue 2026-10-24 05:22:09 +00:00
Sign in to join this conversation.
No labels
loop
ready
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#101
No description provided.