From a18819e65fa2dd3135909df81534937dadafb6ab Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Tue, 12 Dec 2023 16:48:26 -0800 Subject: [PATCH] Fix op asgn method calls passing mutable keyword splats When passing the keyword splat to [], it cannot be mutable, because mutating the keyword splat inside [] would result in changes to the keyword splat passed to []=. --- compile.c | 2 +- test/ruby/test_call.rb | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/compile.c b/compile.c index ae127e59c9..048a30c203 100644 --- a/compile.c +++ b/compile.c @@ -8908,7 +8908,7 @@ compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node } ADD_INSN1(ret, node, dupn, INT2FIX(dup_argn)); flag |= asgnflag; - ADD_SEND_R(ret, node, idAREF, argc, NULL, INT2FIX(flag), keywords); + ADD_SEND_R(ret, node, idAREF, argc, NULL, INT2FIX(flag & ~VM_CALL_KW_SPLAT_MUT), keywords); if (id == idOROP || id == idANDOP) { /* a[x] ||= y or a[x] &&= y diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb index f17d5848b9..5993506b5f 100644 --- a/test/ruby/test_call.rb +++ b/test/ruby/test_call.rb @@ -281,6 +281,27 @@ class TestCall < Test::Unit::TestCase assert_equal([:to_a, :to_hash, :to_proc, :to_proc], ary) end + def test_call_op_asgn_keywords_mutable + h = Class.new do + attr_reader :get, :set + def v; yield; [*@get, *@set] end + def [](*a, **b, &c) + @get = [a.dup, b.dup] + a << :splat_modified + b[:kw_splat_modified] = true + @set = [] + 3 + end + def []=(*a, **b) @set = [a, b] end + end.new + + a = [] + kw = {} + b = lambda{} + + assert_equal([[2], {b: 5}, [2, 4], {b: 5}], h.v{h[*a, 2, b: 5, **kw] += 1}) + end + def test_call_splat_order bug12860 = '[ruby-core:77701] [Bug# 12860]' ary = [1, 2]