checker: Multiply<Scalar> and Multiply on one generic type are refused as overlapping #441
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Found by the scratch probe of the
std/tensordesign record (#438, step 1 of #82;docs/design/TENSOR.md, "Operators"). A generic type that implementsMultiply<Scalar>(its own scalar) andMultiply(itself) is refused as overlapping, although the two cannot be the same instance:Scalar = Tensor<Scalar>has no finite solution.Reproduction
torb check:error: `Tensor<Scalar>` already implements `Multiply<Tensor<Scalar>, Tensor<Scalar>>` ... Two implementations of one trait may not overlap: only different target types tell them apart. Declaring the second one on a concrete scalar (extend Tensor<Float64> with Multiply<Float64>besidewith Multiplyon the generic type) is accepted anda * 3.0anda * bboth run, so the rule can tell them apart there. Probed with the nightly of 2026-10-23.Goal
The overlap check unifies with an occurs check (a variable never equals a type that contains it), so
tensor * scalarandtensor * tensorcan be written once for every scalar.Acceptance criteria
a * 3.0anda * brun in both back ends.Multiply<Scalar>besideMultiply<Int>on one generic type) is still refused; compiler tests pin both.Seed
None expected (the checker accepts more programs; nothing in
std/depends on it).Out of scope
std/tensorslices: they spell the element-wise product of two tensors asscaled(by:)and do not wait for this.std/tensoron the CPU (step 1 of #82) #438[loop:torbscript] intake: in scope (author orion, label loop). claim refine by session:loop-torbscript-20261024 until 2026-10-24T00:00Z
[loop:torbscript] refine: ready, claim released, next: implement
Definition of ready
docs/design/TENSOR.mdsection 7 ("Operators", finding ontensor * tensor) andCONCEPT.md"Coherence" (blanket and overlap rule, "Disjointness is proved by different target heads, or by bounds ..."). It is the false positive filed there as #441; the rule itself ("two implementations of one trait may never overlap") does not change, only the unifier behind it gets exact.compiler/src/semantics/checker/implementation.trb):reportOverlapcallscouldUnifyonce on the targets and once on the trait applications.couldUnifyis stateless: everyParameterunifies with anything. For the repro, targets giveScalar = Scalarand the trait arguments giveScalar = Tensor<Scalar>, which only conflicts when the two answers share one substitution and an occurs check.std/depends on it. The seed refresh stays untouched.Acceptance criteria (replaces the two in the description, same intent, now testable)
couldUnifybecomes a real unification: one substitution (parameter -> type) is shared by the target pair and the trait pair of the two implementations, a parameter bound twice must agree, and a parameter never equals a type that contains it (occurs check).Variable,InvalidandDeferredkeep being open (no new false negatives, no cascade errors).type Tensor<Scalar: Numeric = Float> with Multiply<Scalar>plusextend<Scalar: Numeric> Tensor<Scalar> with Multiply<Tensor<Scalar>, Tensor<Scalar>>) is accepted bytorb check.tests/language/(with.expected) builds aTensor<Float>, runsa * 3.0anda * b, and passes withtorb runandtorb run --native(both back ends). If call resolution picks the wrong implementation or lowering fails for the generic pair, fixing that is part of this issue (same PR), not a new one.compiler/tests/implementations.test.trbpin: (a) the repro is accepted; (b) a real overlap is still refused with the unchanged message (Multiply<Scalar>besideMultiply<Int>on one generic type); (c) a parameter used twice is consistent (Wrapper<A, A>vsWrapper<Int, String>do not overlap,Wrapper<A, A>vsWrapper<Int, Int>do); (d) the two-blanket and bound-disjointness messages are unchanged.sh tools/gates.sh agreen in forge CI (not run locally); the existing overlap tests inimplementations.test.trbstay green unmodified.Commit plan
fix(checker): unify implementation targets with a shared substitution and an occurs check- the unifier, the tests of criterion 4, thetests/language/program of criterion 3.docs(design): record that tensor times tensor no longer overlaps scalar times tensor-docs/design/TENSOR.mdsection 7 (the "refused" probe row and the #441 bullets become "fixed by #441"; the revisit of*between two tensors stays a decision for the first-release numbers, not promised), one sentence inCONCEPT.mdCoherence ("... and a parameter never equals a type that contains it"), regeneratedskills/(docs skill).decided by the loop:
Wrapper<A, A>), and a pattern-specific exemption would keep the next false positive in place. Technical checker design, so no owner question.Scalar: NumericvsTensor<Scalar>). Why: the occurs check already separates the two andreportBlanketOverlap/ the bound-disjointness path stay as they are; mixing the two would widen the change beyond one PR.tensor * tensoras a language decision stays out. Why: TENSOR.md section 7 deliberately namesscaled(by:)andmatmul; this issue only removes the checker obstacle, and thestd/tensorslices do not wait for it (as the description says).3.0 * tensor(#440) is not part of this. Related, refined separately: #440No owner question: none of the contract's categories applies (internal checker design, no published contract broken, since the checker only accepts more).
Tokens: RE worker ~60k (sonnet, refine).
scalar * valuewith a userMultiplyon the number type lowers as the builtin product #440[loop:torbscript] claim implement by session:loop-torbscript-20261024 until 2026-10-24T04:30Z
[loop:torbscript] release implement: the implement worker hit its 30 minute run timeout without a result comment, after opening PR #448 (two commits: checker fix with shared substitution and occurs check, design doc note). Forge CI: bootstrap (linux-x64) green, gates, tier A/B and the remaining jobs still pending. Claim released. next: waiting (CI running on PR #448), then review/fix rounds
From<Scalar>on a generic type is refused as overlapping the blanketFrom<Never>#455From<Scalar>on a generic type is refused as overlapping the blanketFrom<Never>#455std/tensor,Tensor<Scalar>over aBuffer(step 2 of #82) #691std/tensoron the CPU #82