Project

General

Profile

Actions

Bug #19864

closed

Ruby 3.2 Changed Behavior With One Sided Ranges

Added by Aesthetikx (John DeSilva) about 1 year ago. Updated about 1 year ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:114641]

Description

Thank you for taking the time to read my issue. I know there has been some previous discussion here https://bugs.ruby-lang.org/issues/19533 regarding a similar issue, although I think this is different. I apologize if this has already been addressed.

Prior to Ruby 3.2, you could use a beginless or endless range, and use === (or a case statement) to determine if a given date matched that range. For example, (..today) === yesterday would have returned true, and (tomorrow..) === today would have returned false. Please see my attached file for a more concrete example.

Starting with Ruby 3.2, this results in "`===': cannot determine inclusion in beginless/endless ranges (TypeError)".

I can imagine that there is difficulty and ambiguity with these infinite ranges and non numeric objects, however I do feel that these examples with dates should work, especially since (..Date.today).cover?(Date.today) still works as expected.


Files

range_test.rb (928 Bytes) range_test.rb Aesthetikx (John DeSilva), 09/05/2023 09:32 PM
range_test_2.rb (411 Bytes) range_test_2.rb jgomo3 (Jesús Gómez), 09/06/2023 12:17 PM
range_test_3.rb (162 Bytes) range_test_3.rb jgomo3 (Jesús Gómez), 09/06/2023 12:17 PM

Updated by jgomo3 (Jesús Gómez) about 1 year ago

I tested the idea with whole numbers, and it doesn't fail.

I tested the idea with dates not in the extremes, and it DOES fail.

Attached 2 examples.

The first one using numbers:

(..0) === 0

Which works fine both in Ruby 3.1 and 3.2

The second one using different dates. In particular, checking if "yesterday" is in the range of "..today":

require 'date'

today = Date.today
yesterday = today - 1
(..today) === yesterday

And it fails in 3.2, but works fine in 3.1.

Updated by zverok (Victor Shepelev) about 1 year ago

Seems to be a bug indeed.

Originates from this commit, which fixed behavior for semi-open Ranges #include? but also added semi-openness check to range_string_cover_internal.

cc @jeremyevans0 (Jeremy Evans)

Actions #3

Updated by zverok (Victor Shepelev) about 1 year ago

  • Backport changed from 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN to 3.0: DONTNEED, 3.1: DONTNEED, 3.2: REQUIRED

Updated by jeremyevans0 (Jeremy Evans) about 1 year ago

This appears to fix it:

diff --git a/range.c b/range.c
index 62e957e622..4b2e2460c7 100644
--- a/range.c
+++ b/range.c
@@ -1818,6 +1818,7 @@ range_string_cover_internal(VALUE range, VALUE val)
             return r_cover_p(range, beg, end, val);
         }
         if (NIL_P(beg)) {
+unbounded_begin:;
             VALUE r = rb_funcall(val, id_cmp, 1, end);
             if (NIL_P(r)) return Qfalse;
             if (RANGE_EXCL(range)) {
@@ -1826,12 +1827,20 @@ range_string_cover_internal(VALUE range, VALUE val)
             return RBOOL(rb_cmpint(r, val, end) <= 0);
         }
         else if (NIL_P(end)) {
+unbounded_end:;
             VALUE r = rb_funcall(beg, id_cmp, 1, val);
             if (NIL_P(r)) return Qfalse;
             return RBOOL(rb_cmpint(r, beg, val) <= 0);
         }
     }
 
+    if (!NIL_P(beg) && NIL_P(end)) {
+        goto unbounded_end;
+    }
+    if (NIL_P(beg) && !NIL_P(end)) {
+        goto unbounded_begin;
+    }
+
     return range_include_fallback(beg, end, val);
 }
 

I'll try to add tests and submit a pull request within a week.

Updated by jeremyevans0 (Jeremy Evans) about 1 year ago

I've submitted a pull request for the diff posted earlier (with tests): https://github.com/ruby/ruby/pull/8458

Actions #6

Updated by jeremyevans (Jeremy Evans) about 1 year ago

  • Status changed from Open to Closed

Applied in changeset git|25711683e86271385e8abe09a9c03782000e48db.


Fix regression when testing inclusion in unbounded ranges

Caused by 04a92a6764bf678919cf4b68a27496a39d6b886a. This treats
unbounded ranges of arbitrary objects the same as how unbounded
string ranges are treated:

(..x) === y # (y <=> x) <= 0
(...x) === y # (y <=> x) < 0
(x..) === y # (x <=> y) <= 0

Fixes [Bug #19864]

Updated by nagachika (Tomoyuki Chikanaga) about 1 year ago

  • Backport changed from 3.0: DONTNEED, 3.1: DONTNEED, 3.2: REQUIRED to 3.0: DONTNEED, 3.1: DONTNEED, 3.2: DONE

ruby_3_2 217ef2bf89b3861e83c2e2a3a633c019f0731de6 merged revision(s) 25711683e86271385e8abe09a9c03782000e48db.

Actions

Also available in: Atom PDF

Like2
Like0Like0Like0Like2Like0Like1Like0