You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A bare `let v = Ok(x)` / `Err(x)` / `None` leaves a type parameter open (the
Result error type, the Result ok type, or the Option value type). The checker
accepted it, the VM ran it, but it failed to lower to valid Rust
(E0282: type annotations needed) — surfacing as a backend RS1101 instead of a
frontend RSScript error, violating the "RSScript owns semantics" contract.
The analyzer is string-based (it leaks literal `E`/`T` placeholders and never
resolves them from usage), so it cannot distinguish the un-lowerable case from a
valid constrained-by-use one — EXCEPT by one sound signal: whether the binding is
ever used. Unused => the placeholder can never be pinned => guaranteed E0282 =>
sound to reject. Used => a later use may pin it (e.g. core-properties'
`let input = Ok(value); match Result.map(input) { Err(e) => <e as String> }`),
so it is left alone. The "not annotated" test is `type_name == value_type_name`
(the HIR sets them equal exactly when the user wrote no annotation).
New RS0034 (+ explanation). Found by the rss-testgen generative differential.
Regression: a fail fixture plus a soundness test asserting it fires on unused
Ok/Err/None and never on Some / annotated / constrained-by-use bindings. The
full suite (1011) stays green — no existing program is newly rejected.
A complete fix (reject only when *no* downstream use pins the param, for used
bindings too) needs real free-type-variable inference, which the string-based
analyzer lacks; that is a tracked follow-up. The remaining used-but-unpinned
cases still fail at the backend with a clean source-mapped RS1101.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: TODO.md
-138Lines changed: 0 additions & 138 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,141 +8,3 @@ _Every item says **why** — if you can't fill in a why, it doesn't belong here.
8
8
-**Stream / lazy Iter** = the deferred performance path. Blueprint: a lazy iterator is `struct Iter[X] { next: () -> Option<X> }`, combinators wrap the next-closure → lazy fusion, zero intermediates. _Why parked / why noted:_ it's the "Stream for performance" we split off from the eager Pipeline; record the design so it's ready, **with the review-first caveats**: it's mutate-on-read / consume-once (hidden-state hazard), and prefer explicit loops over callback-iteration (even mature lazy-iterator libraries are moving away from it).
9
9
- Keep the `try_` surface **small** (don't generate try_ variants; let pipeline's `FalliblePipeline` cover the common case). _Why:_ error-polymorphism (inferring fallibility from the callback) would collapse the `try_` surface, but RSS pays explicit fallibility with duplication, so the price must stay bounded (Article II).
10
10
-`read` omittable-default decision (§2A.3, recorded); pattern extras (`|`, range, `as`/`@`); **map-pattern via a `get(Self,K)->Option<V>` protocol** (defers protocol-dispatched patterns — much heavier semantics than struct/sum, and the interpreter doesn't need it); shared Iter protocol to collapse ×collections. _Why parked:_ not on the dogfooding critical path; revisit when a real program demands them.
11
-
12
-
## tinygrad port feedback
13
-
14
-
These items came from the tinygrad RSScript/modern-c port. They are not abstract
15
-
language wish-list items: each one removes a current source-port workaround or
// A bare `Ok`/`Err`/`None` with no user annotation (the HIR sets
1198
+
// `type_name == value_type_name` exactly when the user did not annotate —
1199
+
// an annotation would override `type_name` with a placeholder-free type),
1200
+
// bound to a name that is never used, so nothing pins the open parameter.
1201
+
HirStmt::Let{
1202
+
name,
1203
+
value:Some(value),
1204
+
type_name,
1205
+
value_type_name,
1206
+
span,
1207
+
..
1208
+
}if type_name == value_type_name
1209
+
&& !uses.contains(name)
1210
+
&& open_variant_constructor(value) =>
1211
+
{
1212
+
analyzer.diagnostics.push(
1213
+
Diagnostic::error(
1214
+
code::UNINFERABLE_BINDING_TYPE,
1215
+
format!("the type of `{name}` cannot be inferred."),
1216
+
span.clone(),
1217
+
"uninferable binding type",
1218
+
)
1219
+
.with_cause(
1220
+
"A bare `Ok(...)`, `Err(...)`, or `None` leaves a type parameter open, and this binding is never used, so nothing can constrain it — the type is ambiguous and would not lower to valid Rust.",
1221
+
)
1222
+
.with_fix(
1223
+
"annotate_binding_type",
1224
+
"Add a type annotation (e.g. `let v: Result<Int, String> = ...`) or remove the unused binding.",
explanation:"Parameters must have explicit types so call effects, freshness, and resource rules can be checked against a stable signature.",
356
357
},
358
+
DiagnosticExplanation{
359
+
code: code::UNINFERABLE_BINDING_TYPE,
360
+
title:"binding type cannot be inferred",
361
+
explanation:"A `let` binding whose value is a bare `Ok(...)`, `Err(...)`, or `None` leaves a type parameter open (the `Result` error type, the `Result` ok type, or the `Option` value type). RSScript normally resolves that parameter from how the binding is later used; when the binding is never used, nothing can constrain it, so the type is genuinely ambiguous and the program would not lower to valid Rust. The checker reports this in RSScript — instead of letting it surface as a backend `type annotations needed` error — and the fix is to add a type annotation (e.g. `let v: Result<Int, String> = Ok(value)`) or to remove the unused binding.",
0 commit comments