Repository navigation
A String Range's % answers an Enumerator that inspects as %(n) - #8468
Conversation
`("a".."e") % 2` inspected as `#<Enumerator: "a".."e":step(2)>` where CRuby
prints `%(2)`. desugar_str_range_methods renamed every `range % n` on a
String Range to `step` because the arithmetic emitter had no arm for it,
which left the builtin-op row for `%` (whose label is `%(n)`) unreachable.
The blockless `%` keeps its name now, and the arithmetic emitter hands a
String Range to that row, ahead of the poly-operand arm, which took a stride
of boxed type (any Integer variable under --int-overflow=promote) for
Integer arithmetic. The block form, `range % n { }`, is still renamed: it is
the step walk, and has no row of its own.
A Float stride (`r % 1.5`, `r.step(0.5)`) was truncated to an Integer, so the
Enumerator inspected as `step(1)` and walked every member. Both rows now call
sp_srange_step_enum, which takes the stride boxed: an Integer stride
materializes the members as before, and a Float one answers an Enumerator
labeled `%(1.5)` whose walk raises CRuby's TypeError and whose size is nil.
sp_enum_inspect prints the receiver of a generator Enumerator that recorded
one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gate: green tree bed77af5c031 master 0fc287c (darwin-arm64 clang-21.0.0) tests 6844/0
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk | ⚪ Minimal · up to
|
Probed at master
8aeee4bb5, macOS, Apple clang 21.p r % 2#<Enumerator: "a".."e":%(2)>#<Enumerator: "a".."e":step(2)>p r.step(2)#<Enumerator: "a".."e":step(2)>p r % 1.5#<Enumerator: "a".."e":%(1.5)>#<Enumerator: "a".."e":step(1)>(r % 1.5).to_aTypeError(no implicit conversion of Float into String)["a", "b", "c", "d", "e"]stepwith a Float (r.step(1.5)) answersstep(1)and the members of a stride of 1, the same way: the row read the stride as an Integer, truncating it. CRuby builds the Enumerator without looking at the stride and raises when its walk adds the stride to a String.desugar_str_range_methods(src/analyze.c) renamed everyrange % non a String Range tostep, because the arithmetic emitter had no arm for it. That left the builtin-op row for%(whose label is%(n)) unreachable.stepEnumerator fix, whose row text carries both labels.The fix. The blockless
%keeps its name, andemit_array_arith_call(src/codegen_call.c) hands a String Range to the row ahead of its numeric arms, which would read a Float stride as Float arithmetic. The poly-operand arm ofsrc/codegen_call_operator.cskips a String Range (as it does a Time): under--int-overflow=promotea stride variable is boxed, and the arm tookrange % nfor Integer arithmetic. The block form,range % n { }, is still renamed tostep: it is the step walk and has no row of its own.A Float stride. Both rows (
src/builtin_ops.c) callsp_srange_step_enum(lib/spinel_rt.h), which takes the stride boxed, so a Float held in a boxed slot is seen as well as a Float-typed one. An Integer stride materializes the stepped members as the rows did. A Float stride answers an Enumerator labeled with the call as written (%(1.5),step(0.5)) whose walk is a generator raising CRuby's TypeError, soto_a,next,first,eachand the rest raise it, and its size is nil.sp_enum_inspectprints the receiver of a generator Enumerator whose creator recorded one, where it printed a Generator placeholder for every generator; that is a change to an existing line oflib/spinel_rt.h, whose natural home is that check, not a helper beside it. The block forms,r.step(1.5) { }andr.%(1.5) { }, raise the TypeError at the call, as CRuby does.The corpus runs come from the local
make gatebelow (this branch merged with master0fc287c2f, macOS arm64, clang 21); the corpus-wide C diff was not run.One existing runtime line changes.
sp_enum_inspect(lib/spinel_rt.h) printed a placeholder for any generator Enumerator (if (e->gen || e->gen_label)); it now prints the receiver a generator Enumerator recorded, so the new step and%Enumerators inspect as CRuby's#<Enumerator: "a".."e":%(1.5)>. Adding a second inspect path for the same object would duplicate the function; the existing branch is where the receiver belongs.Tests.
test/srange_percent_label.rbcovers inspect, an exclusive Range, a variable stride,map,first,next, the block forms ((r % 2).each,r.%(2) { },r.step(2) { }), a Range passed through a method, and a Float stride (literal, variable and boxed) through inspect,size,to_a,next,first,each,each_with_indexand the block forms. It is marked# spinel: shareand# spinel: gc-stress; it fails on master and passes in both builds, plain, with--int-overflow=promote, and underSPINEL_GC_STRESS=1and2, on macOS arm64 and with gcc-O1on Linux. In that gatemake testpasses (6844 pass, 0 fail, 0 error) and the corpus with sharing on has 6843 pass, 1 known failure (already listed intest/share/known-failures.txt) and 0 new failures.Not covered, also on master:
r % nandr.step(n)on an Integer or Float Range answer an Array where CRuby answers an ArithmeticSequence (((1..10).%(3))).("a".."e") % 0,step(-1)) raises ArgumentError when called; CRuby answers an Enumerator whose walk yields only the first member (and loops forstep(0) { }).r.%(2, &blk)raises NoMethodError forstep; a String Range held in a boxed slot (z = [r, 1][0]; z % 2) raises NoMethodError for%; the%andstepof an endless String Range (("a"..) % 3) print as a Generator.r + x,r - xandr * xon a String Range with a boxed operand raise TypeError naming an Array where CRuby raises NoMethodError for the Range.make gate(on this branch merged with current master)Local
make gateon this branch merged with master0fc287c2f(macOS arm64, clang 21); the head commit carries its Gate trailer..expectedfiles that match CRuby 4.0 run with--enable-frozen-string-literal# spinel: int64🤖 Generated with Claude Code
Summary by CodeRabbit
%andstepoperations now return enumerators that yield every nth member, with support for runtime-provided strides.TypeError.ArgumentError.