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

ruby-changes:57971

From: Jeremy <ko1@a...>
Date: Fri, 27 Sep 2019 11:25:14 +0900 (JST)
Subject: [ruby-changes:57971] 660c7e050f (master): Fix more keyword separation issues

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

From 660c7e050f6cb050fd5618f812129c211af29810 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Thu, 26 Sep 2019 17:25:54 -0700
Subject: Fix more keyword separation issues

This fixes instance_exec and similar methods. It also fixes
Enumerator::Yielder#yield, rb_yield_block, and a couple of cases
with Proc#{<<,>>}.

This support requires the addition of rb_yield_values_kw, similar to
rb_yield_values2, for passing the keyword flag.

Unlike earlier attempts at this, this does not modify the rb_block_call_func
type or add a separate function type.  The functions of type
rb_block_call_func are called by Ruby with a separate VM frame, and we can
get the keyword flag information from the VM frame flags, so it doesn't need
to be passed as a function argument.

These changes require the following VM functions accept a keyword flag:

* vm_yield_with_cref
* vm_yield
* vm_yield_with_block

diff --git a/enumerator.c b/enumerator.c
index 6332219..9eb530c 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -1320,7 +1320,7 @@ yielder_yield(VALUE obj, VALUE args) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L1320
 {
     struct yielder *ptr = yielder_ptr(obj);
 
-    return rb_proc_call(ptr->proc, args);
+    return rb_proc_call_kw(ptr->proc, args, RB_PASS_CALLED_KEYWORDS);
 }
 
 /* :nodoc: */
@@ -1357,7 +1357,7 @@ yielder_to_proc(VALUE obj) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L1357
 static VALUE
 yielder_yield_i(RB_BLOCK_CALL_FUNC_ARGLIST(obj, memo))
 {
-    return rb_yield_values2(argc, argv);
+    return rb_yield_values_kw(argc, argv, RB_PASS_CALLED_KEYWORDS);
 }
 
 static VALUE
diff --git a/ext/-test-/iter/yield.c b/ext/-test-/iter/yield.c
index 3cd408a..0f6f3e8 100644
--- a/ext/-test-/iter/yield.c
+++ b/ext/-test-/iter/yield.c
@@ -4,7 +4,7 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/ext/-test-/iter/yield.c#L4
 yield_block(int argc, VALUE *argv, VALUE self)
 {
     rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
-    return rb_block_call(self, rb_to_id(argv[0]), argc-1, argv+1, rb_yield_block, 0);
+    return rb_block_call_kw(self, rb_to_id(argv[0]), argc-1, argv+1, rb_yield_block, 0, RB_PASS_CALLED_KEYWORDS);
 }
 
 void
diff --git a/hash.c b/hash.c
index 8b84a14..d00a09d 100644
--- a/hash.c
+++ b/hash.c
@@ -4471,7 +4471,7 @@ rb_hash_gt(VALUE hash, VALUE other) https://github.com/ruby/ruby/blob/trunk/hash.c#L4471
 }
 
 static VALUE
-hash_proc_call(VALUE key, VALUE hash, int argc, const VALUE *argv, VALUE passed_proc)
+hash_proc_call(RB_BLOCK_CALL_FUNC_ARGLIST(key, hash))
 {
     rb_check_arity(argc, 1, 1);
     return rb_hash_aref(hash, *argv);
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index 18901bb..ca238dd 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -1970,8 +1970,9 @@ VALUE rb_each(VALUE); https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1970
 VALUE rb_yield(VALUE);
 VALUE rb_yield_values(int n, ...);
 VALUE rb_yield_values2(int n, const VALUE *argv);
+VALUE rb_yield_values_kw(int n, const VALUE *argv, int kw_splat);
 VALUE rb_yield_splat(VALUE);
-VALUE rb_yield_block(VALUE, VALUE, int, const VALUE *, VALUE); /* rb_block_call_func */
+VALUE rb_yield_block(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)); /* rb_block_call_func */
 #define RB_NO_KEYWORDS 0
 #define RB_PASS_KEYWORDS 1
 #define RB_PASS_EMPTY_KEYWORDS 2
diff --git a/proc.c b/proc.c
index 7c82cc6..91e9f98 100644
--- a/proc.c
+++ b/proc.c
@@ -2895,7 +2895,7 @@ mlambda(VALUE method) https://github.com/ruby/ruby/blob/trunk/proc.c#L2895
 static VALUE
 bmcall(RB_BLOCK_CALL_FUNC_ARGLIST(args, method))
 {
-    return rb_method_call_with_block(argc, argv, method, blockarg);
+    return rb_method_call_with_block_kw(argc, argv, method, blockarg, RB_PASS_CALLED_KEYWORDS);
 }
 
 VALUE
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index da502ac..5992434 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -1,6 +1,7 @@ https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L1
 # frozen_string_literal: false
 require 'test/unit'
 require '-test-/rb_call_super_kw'
+require '-test-/iter'
 
 class TestKeywordArguments < Test::Unit::TestCase
   def f1(str: "foo", num: 424242)
@@ -918,6 +919,83 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L919
     assert_equal([1, h3], g.new(&f).each(a: 1, **h2))
   end
 
+  def test_Enumerator_Yielder_yield_kwsplat
+    kw = {}
+    h = {:a=>1}
+    h2 = {'a'=>1}
+    h3 = {'a'=>1, :a=>1}
+
+    g = Enumerator::Generator
+    f = -> { true }
+    assert_equal(true, g.new{|y| y.yield(**{})}.each(&f))
+    assert_equal(true, g.new{|y| y.yield(**kw)}.each(&f))
+    assert_raise(ArgumentError) { g.new{|y| y.yield(**h)}.each(&f) }
+    assert_raise(ArgumentError) { g.new{|y| y.yield(a: 1)}.each(&f) }
+    assert_raise(ArgumentError) { g.new{|y| y.yield(**h2)}.each(&f) }
+    assert_raise(ArgumentError) { g.new{|y| y.yield(**h3)}.each(&f) }
+
+    f = ->(a) { a }
+    assert_warn(/The keyword argument is passed as the last hash parameter/m) do
+      assert_equal(kw, g.new{|y| y.yield(**{})}.each(&f))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/m) do
+      assert_equal(kw, g.new{|y| y.yield(**kw)}.each(&f))
+    end
+    assert_equal(h, g.new{|y| y.yield(**h)}.each(&f))
+    assert_equal(h, g.new{|y| y.yield(a: 1)}.each(&f))
+    assert_equal(h2, g.new{|y| y.yield(**h2)}.each(&f))
+    assert_equal(h3, g.new{|y| y.yield(**h3)}.each(&f))
+    assert_equal(h3, g.new{|y| y.yield(a: 1, **h2)}.each(&f))
+
+    f = ->(**x) { x }
+    assert_equal(kw, g.new{|y| y.yield(**{})}.each(&f))
+    assert_equal(kw, g.new{|y| y.yield(**kw)}.each(&f))
+    assert_equal(h, g.new{|y| y.yield(**h)}.each(&f))
+    assert_equal(h, g.new{|y| y.yield(a: 1)}.each(&f))
+    assert_equal(h2, g.new{|y| y.yield(**h2)}.each(&f))
+    assert_equal(h3, g.new{|y| y.yield(**h3)}.each(&f))
+    assert_equal(h3, g.new{|y| y.yield(a: 1, **h2)}.each(&f))
+    assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
+      assert_equal(h, g.new{|y| y.yield(h)}.each(&f))
+    end
+    assert_raise(ArgumentError) { g.new{|y| y.yield(h2)}.each(&f) }
+    assert_warn(/The last argument is split into positional and keyword parameters.*for method/m) do
+      assert_raise(ArgumentError) { g.new{|y| y.yield(h3)}.each(&f) }
+    end
+
+    f = ->(a, **x) { [a,x] }
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([{}, {}], g.new{|y| y.yield(**{})}.each(&f))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([{}, {}], g.new{|y| y.yield(**kw)}.each(&f))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([h, {}], g.new{|y| y.yield(**h)}.each(&f))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([h, {}], g.new{|y| y.yield(a: 1)}.each(&f))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([h2, {}], g.new{|y| y.yield(**h2)}.each(&f))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([h3, {}], g.new{|y| y.yield(**h3)}.each(&f))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal([h3, {}], g.new{|y| y.yield(a: 1, **h2)}.each(&f))
+    end
+
+    f = ->(a=1, **x) { [a, x] }
+    assert_equal([1, kw], g.new{|y| y.yield(**{})}.each(&f))
+    assert_equal([1, kw], g.new{|y| y.yield(**kw)}.each(&f))
+    assert_equal([1, h], g.new{|y| y.yield(**h)}.each(&f))
+    assert_equal([1, h], g.new{|y| y.yield(a: 1)}.each(&f))
+    assert_equal([1, h2], g.new{|y| y.yield(**h2)}.each(&f))
+    assert_equal([1, h3], g.new{|y| y.yield(**h3)}.each(&f))
+    assert_equal([1, h3], g.new{|y| y.yield(a: 1, **h2)}.each(&f))
+  end
+
   def test_Class_new_kwsplat_call
     kw = {}
     h = {:a=>1}
@@ -3131,6 +3209,576 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L3209
     end
   end
 
+  def test_instance_exec_kwsplat
+    kw = {}
+    h = {:a=>1}
+    h2 = {'a'=>1}
+    h3 = {'a'=>1, :a=>1}
+
+    c = Object.new
+    m = ->(*args) { args }
+    assert_equal([], c.instance_exec(**{}, &m))
+    assert_equal([], c.instance_exec(**kw, &m))
+    assert_equal([h], c.instance_exec(**h, &m))
+    assert_equal([h], c.instance_exec(a: 1, &m))
+    assert_equal([h2], c.instance_exec(**h2, &m))
+    assert_equal([h3], c.instance_exec(**h3, &m))
+    assert_equal([h3], c.instance_exec(a: 1, **h2, &m))
+
+    m = ->() { nil }
+    assert_nil(c.instance_exec(**{}, &m))
+    assert_nil(c.instance_exec(**kw, &m))
+    assert_raise(ArgumentError) { c.instance_exec(**h, &m) }
+    assert_raise(ArgumentError) { c.instance_exec(a: 1, &m) }
+    assert_raise(ArgumentError) { c.instance_exec(**h2, &m) }
+    assert_raise(ArgumentError) { c.instance_exec(**h3, &m) }
+    assert_raise(ArgumentError) { c.instance_exec(a: 1, **h2, &m) }
+
+    m = ->(args) { args }
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal(kw, c.instance_exec(**{}, &m))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/) do
+      assert_equal(kw, c.instance_exec(**kw, &m))
+    end
+    assert_equal(kw, c.instance_exec(kw, **kw, &m))
+    assert_equal(h, c.instance_exec(**h, &m))
+    assert_equal(h, c.instance_exec(a: 1, &m))
+    assert_equal(h2, c.instance_exec(**h2, &m))
+    assert_equal(h3, c.instance_exec(**h3, &m))
+    assert_equal(h3, c.instance_exec(a: 1, **h2, &m))
+
+    m = ->(**args) { args }
+    assert_equal(kw, c.instance_exec(**{}, &m))
+    assert_equal(kw, c.instance_exec(**kw, &m))
+    assert_equal(h, c. (... truncated)

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

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