From ac0268f54cc71b1284fd458e1930ea17d0f8b09b Mon Sep 17 00:00:00 2001 From: Yubin Kim Date: Sat, 1 Aug 2026 15:53:46 +0900 Subject: [PATCH] Rebase range iterator __reduce__ to match CPython range_iterator.__reduce__ (and longrange_iterator) returned the original range plus the current index as pickle state; CPython returns the range rebased to the current position with a None state. Rebase start by index * step (clamped to the length) and emit None. __setstate__ is kept so pickles carrying an integer state still load. Assisted-by: Claude Code:claude-opus-4-8 --- crates/vm/src/builtins/range.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/vm/src/builtins/range.rs b/crates/vm/src/builtins/range.rs index 415d34fdb05..e219c1506fd 100644 --- a/crates/vm/src/builtins/range.rs +++ b/crates/vm/src/builtins/range.rs @@ -716,13 +716,17 @@ fn range_iter_reduce( vm: &VirtualMachine, ) -> PyTupleRef { let iter = builtins_iter(vm); + // CPython pickles the remaining range with a None state. next() increments + // the index unconditionally, so clamp it to length before rebasing start. + let index = BigInt::from(index).min(length.clone()); let stop = start.clone() + length * step.clone(); + let start = start + index * step.clone(); let range = PyRange { start: PyInt::from(start).into_ref(&vm.ctx), stop: PyInt::from(stop).into_ref(&vm.ctx), step: PyInt::from(step).into_ref(&vm.ctx), }; - vm.new_tuple((iter, (range,), index)) + vm.new_tuple((iter, (range,), vm.ctx.none())) } // Silently clips state (i.e index) in range [0, usize::MAX].