[前][次][番号順一覧][スレッド一覧]

ruby-changes:62120

From: Sam <ko1@a...>
Date: Sun, 5 Jul 2020 02:02:41 +0900 (JST)
Subject: [ruby-changes:62120] bf1a6771f3 (master): Fix non-numeric exclusive Range#minmax bug

https://git.ruby-lang.org/ruby.git/commit/?id=bf1a6771f3

From bf1a6771f305ea286a3ae575676924551c03e857 Mon Sep 17 00:00:00 2001
From: Sam Bostock <sam.bostock@s...>
Date: Fri, 3 Jul 2020 22:56:07 -0400
Subject: Fix non-numeric exclusive Range#minmax bug

The implementation of Range#minmax added in d5c60214c45 causes the
following incorrect behaviour:

  ('a'...'c').minmax => ["a", ["a", "b"]]

instead of

  ('a'...'c').minmax => ["a", "b"]

This is because the C implementation of Range#minmax (range_minmax)
directly delegates to the C implementation of Range#min (range_min) and
Range#max (range_max), without changing the execution context.

Range#max's C implementation (range_max), when given a non-numeric
exclusive range, delegates to super, which is meant to call
Enumerable#max. However, because range_max is called directly by
range_minmax, super calls Enumerable#minmax instead, causing the
incorrect nesting.

Perhaps it is possible to change the execution context in an optimized
manner, but the simplest solution seems to be to just explicitly
delegate from Range#minmax to Range#min and Range#max.

diff --git a/range.c b/range.c
index 1903b0c..65e863c 100644
--- a/range.c
+++ b/range.c
@@ -1266,7 +1266,10 @@ range_minmax(VALUE range) https://github.com/ruby/ruby/blob/trunk/range.c#L1266
     if (rb_block_given_p()) {
         return rb_call_super(0, NULL);
     }
-    return rb_assoc_new(range_min(0, NULL, range), range_max(0, NULL, range));
+    return rb_assoc_new(
+        rb_funcall(range, rb_intern("min"), 0),
+        rb_funcall(range, rb_intern("max"), 0)
+    );
 }
 
 int
diff --git a/test/ruby/test_range.rb b/test/ruby/test_range.rb
index b37dbbc..3953b3e 100644
--- a/test/ruby/test_range.rb
+++ b/test/ruby/test_range.rb
@@ -146,6 +146,9 @@ class TestRange < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_range.rb#L146
     assert_equal([nil, nil], (0...0).minmax)
 
     assert_equal([2, 1], (1..2).minmax{|a, b| b <=> a})
+
+    assert_equal(['a', 'c'], ('a'..'c').minmax)
+    assert_equal(['a', 'b'], ('a'...'c').minmax)
   end
 
   def test_initialize_twice
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]