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

ruby-changes:60336

From: Jeremy <ko1@a...>
Date: Mon, 9 Mar 2020 12:49:36 +0900 (JST)
Subject: [ruby-changes:60336] f4394bbca3 (master): Do not autosplat when calling procs that accept rest and keywords

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

From f4394bbca361c2bb500f586ba0bf1bef8b919910 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Fri, 27 Sep 2019 13:35:29 -0700
Subject: Do not autosplat when calling procs that accept rest and keywords

When providing a single array to a block that takes a splat, pass the
array as one argument of the splat instead of as the splat itself,
even if the block also accepts keyword arguments.  Previously, this
behavior was only used for blocks that did not accept keywords.

Implements [Feature#16166]

diff --git a/spec/ruby/language/block_spec.rb b/spec/ruby/language/block_spec.rb
index 1a8e790..9936600 100644
--- a/spec/ruby/language/block_spec.rb
+++ b/spec/ruby/language/block_spec.rb
@@ -76,18 +76,18 @@ describe "A block yielded a single" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/language/block_spec.rb#L76
         result.should == [1, 2, [3], {x: 9}, 2, {}]
       end
 
-      it "does not treat final Hash as keyword arguments" do
+      it "does not treat final Hash as keyword arguments and does not autosplat" do
         result = m(["a" => 1, a: 10]) { |a=nil, **b| [a, b] }
-        result.should == [{"a" => 1, a: 10}, {}]
+        result.should == [[{"a" => 1, a: 10}], {}]
       end
 
-      it "does not call #to_hash on final argument to get keyword arguments" do
+      it "does not call #to_hash on final argument to get keyword arguments and does not autosplat" do
         suppress_keyword_warning do
           obj = mock("coerce block keyword arguments")
           obj.should_not_receive(:to_hash)
 
           result = m([obj]) { |a=nil, **b| [a, b] }
-          result.should == [obj, {}]
+          result.should == [[obj], {}]
         end
       end
     end
@@ -113,12 +113,12 @@ describe "A block yielded a single" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/language/block_spec.rb#L113
     end
 
     ruby_version_is "2.8" do
-      it "does not call #to_hash on the argument when optional argument and keyword argument accepted" do
+      it "does not call #to_hash on the argument when optional argument and keyword argument accepted and does not autosplat" do
         obj = mock("coerce block keyword arguments")
         obj.should_not_receive(:to_hash)
 
         result = m([obj]) { |a=nil, **b| [a, b] }
-        result.should == [obj, {}]
+        result.should == [[obj], {}]
       end
     end
 
@@ -132,18 +132,27 @@ describe "A block yielded a single" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/language/block_spec.rb#L132
         end
       end
       ruby_version_is "2.8" do
-        it "does not separates non-symbol keys and symbol keys" do
+        it "does not separate non-symbol keys and symbol keys and does not autosplat" do
           suppress_keyword_warning do
             result = m(["a" => 10, b: 2]) { |a=nil, **b| [a, b] }
-            result.should == [{"a" => 10, b: 2}, {}]
+            result.should == [[{"a" => 10, b: 2}], {}]
           end
         end
       end
     end
 
-    it "does not treat hashes with string keys as keyword arguments" do
-      result = m(["a" => 10]) { |a = nil, **b| [a, b] }
-      result.should == [{"a" => 10}, {}]
+    ruby_version_is ""..."2.8" do
+      it "does not treat hashes with string keys as keyword arguments" do
+        result = m(["a" => 10]) { |a = nil, **b| [a, b] }
+        result.should == [{"a" => 10}, {}]
+      end
+    end
+
+    ruby_version_is "2.8" do
+      it "does not treat hashes with string keys as keyword arguments and does not autosplat" do
+        result = m(["a" => 10]) { |a = nil, **b| [a, b] }
+        result.should == [[{"a" => 10}], {}]
+      end
     end
 
     ruby_version_is ''...'2.8' do
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index db7d696..48d6a73 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -3284,15 +3284,20 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L3284
     bug7665 = '[ruby-core:51278]'
     bug8463 = '[ruby-core:55203] [Bug #8463]'
     a = [*%w[foo bar], {zzz: 42}]
-    expect = a + [{}]
-    assert_equal(expect, rest_keyrest(*a), bug7665)
+    splat_expect = a + [{}]
+    nonsplat_expect = [a, {}]
+    assert_equal(splat_expect, rest_keyrest(*a), bug7665)
+    assert_equal(nonsplat_expect, rest_keyrest(a), bug7665)
+
     pr = proc {|*args, **opt| next *args, opt}
-    assert_equal(expect, pr.call(*a), bug7665)
-    assert_equal(expect, pr.call(a), bug8463)
+    assert_equal(splat_expect, pr.call(*a), bug7665)
+    assert_equal(nonsplat_expect, pr.call(a), bug8463)
+
     pr = proc {|a, *b, **opt| next a, *b, opt}
-    assert_equal(expect, pr.call(a), bug8463)
+    assert_equal(splat_expect, pr.call(a), bug8463)
+
     pr = proc {|a, **opt| next a, opt}
-    assert_equal(expect.values_at(0, -1), pr.call(expect), bug8463)
+    assert_equal(splat_expect.values_at(0, -1), pr.call(splat_expect), bug8463)
   end
 
   def req_plus_keyword(x, **h)
@@ -3662,7 +3667,7 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L3667
 
   def test_nonsymbol_key
     result = m(["a" => 10]) { |a = nil, **b| [a, b] }
-    assert_equal([{"a" => 10}, {}], result)
+    assert_equal([[{"a" => 10}], {}], result)
   end
 
   def method_for_test_to_hash_call_during_setup_complex_parameters k1:, k2:, **rest_kw
diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb
index b00f42d..14b7938 100644
--- a/test/ruby/test_proc.rb
+++ b/test/ruby/test_proc.rb
@@ -784,6 +784,33 @@ class TestProc < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_proc.rb#L784
     assert_equal [[1, 2], Proc, :x], (pr.call(1, 2){|x| x})
   end
 
+  def test_proc_args_only_rest
+    pr = proc {|*c| c }
+    assert_equal [], pr.call()
+    assert_equal [1], pr.call(1)
+    assert_equal [[1]], pr.call([1])
+    assert_equal [1, 2], pr.call(1,2)
+    assert_equal [[1, 2]], pr.call([1,2])
+  end
+
+  def test_proc_args_rest_kw
+    pr = proc {|*c, a: 1| [c, a] }
+    assert_equal [[], 1], pr.call()
+    assert_equal [[1], 1], pr.call(1)
+    assert_equal [[[1]], 1], pr.call([1])
+    assert_equal [[1, 2], 1], pr.call(1,2)
+    assert_equal [[[1, 2]], 1], pr.call([1,2])
+  end
+
+  def test_proc_args_rest_kwsplat
+    pr = proc {|*c, **kw| [c, kw] }
+    assert_equal [[], {}], pr.call()
+    assert_equal [[1], {}], pr.call(1)
+    assert_equal [[[1]], {}], pr.call([1])
+    assert_equal [[1, 2], {}], pr.call(1,2)
+    assert_equal [[[1, 2]], {}], pr.call([1,2])
+  end
+
   def test_proc_args_pos_rest_post_block
     pr = proc {|a,b,*c,d,e,&f|
       [a, b, c, d, e, f.class, f&&f.call(:x)]
diff --git a/vm_args.c b/vm_args.c
index e6d4981..50bb023 100644
--- a/vm_args.c
+++ b/vm_args.c
@@ -568,14 +568,14 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co https://github.com/ruby/ruby/blob/trunk/vm_args.c#L568
 	rb_raise(rb_eArgError, "no keywords accepted");
     }
 
+
     switch (arg_setup_type) {
       case arg_setup_method:
 	break; /* do nothing special */
       case arg_setup_block:
         if (given_argc == (keyword_hash == Qnil ? 1 : 2) &&
             allow_autosplat &&
-	    (min_argc > 0 || iseq->body->param.opt_num > 1 ||
-	     iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) &&
+            (min_argc > 0 || iseq->body->param.opt_num > 1) &&
 	    !iseq->body->param.flags.ambiguous_param0 &&
 	    args_check_block_arg0(args)) {
 	    given_argc = RARRAY_LENINT(args->rest);
-- 
cgit v0.10.2


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

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