aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2020-01-24 12:13:41 -0800
committerNARUSE, Yui <naruse@airemix.jp>2020-03-15 20:52:43 +0900
commit3efbd527a86203406664a01126784a10eb136405 (patch)
tree6cd56c2a0b75b3b3c552f85483860351fd72be97
parentc858a10ce87033beec63516fe6407b50c6539177 (diff)
downloadruby-3efbd527a86203406664a01126784a10eb136405.tar.gz
Do not autosplat when calling proc with empty keyword splat
With the removal of the splatted argument when using an empty keyword splat, the autosplat code considered an empty keyword splat the same as no argument at all. However, that results in autosplat behavior changing dependent on the content of the splatted hash, which is not what anyone would expect or want. This change always skips an autosplat if keywords were provided. Fixes [Bug #16560] (cherry picked from commit c1d8829ef515ee51fadeadd7dd022b5c47a71cdd)
-rw-r--r--test/ruby/test_proc.rb20
-rw-r--r--vm_args.c3
2 files changed, 22 insertions, 1 deletions
diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb
index c6572ec1ba..3f0d599a04 100644
--- a/test/ruby/test_proc.rb
+++ b/test/ruby/test_proc.rb
@@ -1087,6 +1087,26 @@ class TestProc < Test::Unit::TestCase
assert_equal([1,2,[3],4,5], r, "[ruby-core:19485]")
end
+ def test_proc_autosplat
+ def self.a(arg, kw)
+ yield arg
+ yield arg, **kw
+ yield arg, kw
+ end
+
+ arr = []
+ a([1,2,3], {}) do |arg1, arg2=0|
+ arr << [arg1, arg2]
+ end
+ assert_equal([[1, 2], [[1, 2, 3], 0], [[1, 2, 3], {}]], arr)
+
+ arr = []
+ a([1,2,3], a: 1) do |arg1, arg2=0|
+ arr << [arg1, arg2]
+ end
+ assert_equal([[1, 2], [[1, 2, 3], {a: 1}], [[1, 2, 3], {a: 1}]], arr)
+ end
+
def test_parameters
assert_equal([], proc {}.parameters)
assert_equal([], proc {||}.parameters)
diff --git a/vm_args.c b/vm_args.c
index 1efb84a0a3..cf0328d4b7 100644
--- a/vm_args.c
+++ b/vm_args.c
@@ -725,10 +725,10 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
{
const int min_argc = iseq->body->param.lead_num + iseq->body->param.post_num;
const int max_argc = (iseq->body->param.flags.has_rest == FALSE) ? min_argc + iseq->body->param.opt_num : UNLIMITED_ARGUMENTS;
- int opt_pc = 0;
int given_argc;
int kw_splat = FALSE;
unsigned int kw_flag = ci->flag & (VM_CALL_KWARG | VM_CALL_KW_SPLAT);
+ int opt_pc = 0, allow_autosplat = !kw_flag;
struct args_info args_body, *args;
VALUE keyword_hash = Qnil;
VALUE * const orig_sp = ec->cfp->sp;
@@ -879,6 +879,7 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
break; /* do nothing special */
case arg_setup_block:
if (given_argc == 1 &&
+ allow_autosplat &&
(min_argc > 0 || iseq->body->param.opt_num > 1 ||
iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) &&
!iseq->body->param.flags.ambiguous_param0 &&