Skip to content

feat(ts): translate start.. as an iterator, stepping as std's does past its type's maximum - #320

Merged
kixelated merged 5 commits into
mainfrom
quest/m3/ts-range-from-iter
Oct 31, 2026
Merged

kixelated merged 5 commits into
mainfrom
quest/m3/ts-range-from-iter

Conversation

@kixelated

@kixelated kixelated commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

An unbounded start.. was a plain { start } object, so wherever an adaptor or consumer wants an rt.Iter it failed tsc: firsts.iter().zip(0..) became rt.iterZip(rt.iterSlice(firsts), { start: 0 }) (keys.ts(1881,77): error TS2353: ... 'start' does not exist in type 'Iter<u8>'). The same happened for chain, flatten/flat_map, extend, collect/from_iter, and for a start.. as its own receiver ((0..).take(3), r.next(), for i in a..). A char range of any kind also stepped straight into the surrogates.

Approach

  • start.. is now rt.RangeFrom<T>, an rt.Iter class, the same way rt.Range is. That fixes every position at once, because the value already is an iterator. It carries its type's Step. next and nth compute the next start before handing back the item, as std's RangeFrom does. Stepping past the type's last value panics with std's debug message: "attempt to add with overflow" for integers, "overflow in Step::forward" for char. sizeHint is (usize::MAX, None), so Vec::extend(250u8..) and Vec::from_iter(250u8..) panic with "capacity overflow" up front, matching std's TrustedLen path.
  • New rt.charStep skips the surrogate gap in both directions. rt.Range, rt.RangeInclusive, and rt.RangeFrom of char take it, and steps_between subtracts the gap.
  • One helper, library::range_step, picks the Step for every place a range is built: rvalues, constants, and RangeInclusive::new.
  • keys::arrays in the lang fixture is back to the natural firsts.iter().zip(0..).
  • New differential case iters::range_froms runs against native wasm32 (a debug build, like the fixture), plus 2,560 named inputs in caller.ts. It covers:
    • every integer width and char, starting 0 to 3 below the maximum;
    • the range on either side of a zip, read by next and by for_each. std's TrustedLen zip fold reads one item fewer than next does when the range comes first;
    • u64 past 2^53 and u128 past 2^64;
    • Range, RangeInclusive, and RangeFrom of char across the surrogates and up to char::MAX;
    • chain, extend, flatten/flat_map of start.., a..b, and a..=b;
    • BTreeMap collected from zip(0..), Vec::from_iter, String::from_iter, step_by, nth, by_ref, and for with break;
    • consumers that overflow.
  • Mutation checks against the same inputs. Removing the overflow panic gives 919 differences, removing the surrogate skip gives 8, disabling the TrustedLen zip fold gives 107 in range_froms alone, and leaving charStep unsaturated gives 19.

Impact

  • Breaking change to the output: a start.. is typed rt.RangeFrom<T>, not { start: T }. A caller passing one to a translated function builds new rt.RangeFrom(start, rt.<int>Step) (MIGRATE.md). Bindings that take one still read start, because the class instance still has that field.
  • Breaking change to the output: rt.Range and rt.RangeInclusive of char take rt.charStep.
  • New runtime exports: rt.RangeFrom and rt.charStep. rt.charStep saturates at char::MAX and '\0', as std's char::forward_overflowing and backward_overflowing do, so an exhausted char range's bounds read as std's.
  • Consumer: moq-net's translation still type-checks (just ts moq). Its output uses no start.., so it is unchanged. The lang fixture's keys::arrays is the consumer that found the bug.

Alternatives

  • Keep { start } and wrap it at each iterator position. Rejected: receivers ((0..).take(3), r.next(), &mut borrows) would each need their own wrapping, plus a write-back to the original range.
  • Follow a crate's overflow-checks = false and wrap the way a release build does. Not modeled: translation always lowers std's overflow asserts as panics, as for every other rustc_inherit_overflow_checks function, and the fixture is built in debug.

Choices made without asking

  • Iterator::eq, ne, lt, le, gt, ge, cmp, and partial_cmp refuse for every iterator today, not only ranges. They went into a follow-up quest instead of this PR.
  • RangeFrom's Clone, ==, and contains, and is_empty/contains of a char range, were unsupported before this PR and still are. Clone is folded into the existing ts-iter-clone quest.

Follow-ups

Review

Codex gpt-6.1-sol at medium effort, via quest review --base origin/main. Reviewed head: f553320.

Local checks: just ci inside nix develop passed on f553320. just ts moq passed on 0540c60, and this fix touches only char stepping, which moq-net's output doesn't use.

🤖 Generated with Claude Code

(written by Claude Opus 5.5)

kixelated and others added 5 commits October 30, 2026 18:22
…st its type's maximum

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	ts/fixtures/lang/expected/harness.ts
#	ts/fixtures/lang/expected/keys.ts
#	ts/fixtures/lang/src/harness.rs
…her end

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title Unbounded ranges as iterators adaptors take feat(ts): translate start.. as an iterator, stepping as std's does past its type's maximum Oct 31, 2026
@kixelated
kixelated marked this pull request as ready for review October 31, 2026 02:35
@coderabbitai

coderabbitai Bot commented Oct 31, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 17 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b059e3c2-15a7-4e1a-8dde-d1c16bf9bbcf
Commits

Reviewing files that changed from the base of the PR and between 52a8900 and f553320.

Files selected for processing: 28
  • quest/m3/README.md
  • quest/m3/ts-iter-clone.md
  • quest/m3/ts-iter-compare.md
  • quest/m3/ts-range-from-iter.md
  • ts/MIGRATE.md
  • ts/fixtures/bind/expected/harness.ts
  • ts/fixtures/lang/caller.ts
  • ts/fixtures/lang/expected/collections.ts
  • ts/fixtures/lang/expected/core/option.ts
  • ts/fixtures/lang/expected/fmt.ts
  • ts/fixtures/lang/expected/harness.ts
  • ts/fixtures/lang/expected/hints.ts
  • ts/fixtures/lang/expected/iters.ts
  • ts/fixtures/lang/expected/keys.ts
  • ts/fixtures/lang/expected/slices.ts
  • ts/fixtures/lang/expected/strs.ts
  • ts/fixtures/lang/expected/wrappers.ts
  • ts/fixtures/lang/src/harness.rs
  • ts/fixtures/lang/src/iters.rs
  • ts/fixtures/lang/src/keys.rs
  • ts/fixtures/task/expected/drops/inside.ts
  • ts/runtime/rt/int.ts
  • ts/runtime/rt/iter.ts
  • ts/src/lower/consts.rs
  • ts/src/lower/library.rs
  • ts/src/lower/mir/mod.rs
  • ts/src/lower/mir/rvalue.rs
  • ts/src/lower/mod.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@kixelated
kixelated merged commit f2288c0 into main Oct 31, 2026
2 checks passed
@kixelated
kixelated deleted the quest/m3/ts-range-from-iter branch October 31, 2026 02:35
kixelated added a commit that referenced this pull request Oct 31, 2026
#320 made start.. an rt.RangeFrom iterator while this branch removed
rt.rangeClone, so RangeFrom gets its own clone() and the iterator
clone mapping takes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated added a commit that referenced this pull request Oct 31, 2026
* chore(quest): claim quest/m3/ts-iter-clone.md

Quest-Claim: 36120e2558fbb4d9

* feat(ts): clone std's iterators and adaptors

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(ts): clone a start.. as the other ranges, after merging main

#320 made start.. an rt.RangeFrom iterator while this branch removed
rt.rangeClone, so RangeFrom gets its own clone() and the iterator
clone mapping takes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Luke Curley <432854+kixelated@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant