Repository navigation
Range#first answers its counted form and refuses a beginless range - #117
Open
jiro-by-nori[bot] wants to merge 1 commit into
Open
jiro-by-nori[bot] wants to merge 1 commit into
jiro-by-nori[bot] wants to merge 1 commit into
Conversation
The bare form is `#begin`, and a beginless range has no first element: CRuby raises `RangeError` there, before it even scans the arguments. The counted form is `Enumerable#first(n)`, so an over-large count is the whole range and an endless range still answers; the count follows `NUM2LONG` (a negative count is `rb_ary_new2`'s ArgumentError, one beyond a machine word its RangeError). A non-Integer count, or a range with non-Integer endpoints, gates with a named reason instead of guessing CRuby's per-class `each` TypeError. Co-authored-by: Nori <contact@tilework.tech>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #114.
Authored by Jiro, an autonomous agent (Nori sessions) on behalf of @sam-xif.
What was wrong
Range#firstwas a native builtin with arity 0, registered next toRange#beginand sitting in
zeroArgBids. Two of CRuby's three shapes were therefore wrong,in opposite directions:
(1..5).first(2)ArgumentError: wrong number of arguments (given 1, expected 0)[1, 2](1...5).first(9)ArgumentError (given 1, expected 0)[1, 2, 3, 4](1..).first(2)ArgumentError (given 1, expected 0)[1, 2](1..5).first(1, 2)ArgumentError (given 2, expected 0)ArgumentError (given 2, expected 1)(nil..5).firstnilRangeError: cannot get the first element of beginless range(nil..5).first(2)ArgumentError (given 1, expected 0)RangeErrorThe counted forms are a wrong
ArgumentError— inside the family the validatorclaims to exclude — and the bare form on a beginless range is a wrong answer,
not a gate.
Range#lasthad the same defect (#110);Array#first(n)andArray#last(n)already follow CRuby.The fix
Range#firstis now a prelude method inruby-lean/prelude/prelude.rb, the sametreatment
#size/#each/#cover?get and the same shape the#lastfix takes:#begin, after the beginless check;Enumerable#first(n): it stops at the end of the range,so an over-large
nis the whole range and an endless range simply yieldsnelements;
that is the order in
rb_range_first— so a beginless range raises the sameRangeErrorfor every argument shape;NUM2LONGandrb_ary_new2: a negative count isArgumentError: negative array size (or size too big), and a count beyond amachine word either way is
RangeError: bignum too big to convert into 'long';with a named
Unsupportedrather than guessing CRuby's per-classeachTypeError(can't iterate from Float,no implicit conversion of String into Integer, …). Those are the only shapes this PR declines.Range#beginkeeps its own arity:(1..5).begin(0)is stillArgumentError (given 1, expected 0), and the regression case pins it.Test plan
Ran on Debian with Lean
v4.32.2, CRuby4.0.5and the pinned Sorbet gems.make check—make check: ALL CHECKS PASSED:make lint,gen-check,lean,books(every proof book, the checker included),metatheory(axiomaudit),
soundness(SOUNDNESS CHECK PASSED),comparator(
Your solution is okay!),desugar-unit,emitter-unit,desugar-test,desugar-coverage,difftest-test,conformance,feature-loading,book-checks(72/72),docs.make check-not-weakened—CHECKS NOT WEAKENED.make conformance— 1287 ran, 1087 agree / 0 disagree / 194 unsupported,exactly the recorded baseline (
difftest/coverage-baseline.jsonis unmoved).The regressions and tier-3 corpora agree.
difftest/corpus/regressions/range-first-counted.rb(statusfixed,28 labelled cases) is reported
heldbyuv run difftest run --tier regressions --sut lean, which went from 220 to221 held cases with 0 failures.
bin/ruby-lean --compare difftest/corpus/regressions/range-first-counted.rb—
AGREEafter the change. Onorigin/main's prelude the same commandreports
DIFFER, and the model's stdout is the wrong-arityArgumentErroron every counted case plusbeginless: nil.endless, descending, empty, single-element and negative ranges;
nof 0, 5, 6and 9; bignum counts) all print byte-identical output to CRuby, and the seven
gated shapes print the named
Unsupportedreason (exit 3) instead of ananswer.
Not in this PR
Range#last's counted form is #110 (its own PR). Two defects found whileprobing
Range#firstare filed separately rather than scope-crept in:#115 (
Range#countis aliased to#size, so a block is silently ignored and theitem argument is rejected) and #116 (
Enumerable#indexis invented, soRange#indexanswers where CRuby raisesNoMethodError).