summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--spec/ruby/core/module/ruby2_keywords_spec.rb1
-rw-r--r--spec/ruby/language/hash_spec.rb18
-rw-r--r--spec/ruby/language/keyword_arguments_spec.rb15
-rw-r--r--test/ruby/test_keyword.rb26
-rw-r--r--vm_args.c4
5 files changed, 43 insertions, 21 deletions
diff --git a/spec/ruby/core/module/ruby2_keywords_spec.rb b/spec/ruby/core/module/ruby2_keywords_spec.rb
index dc16d712c7..a72612a670 100644
--- a/spec/ruby/core/module/ruby2_keywords_spec.rb
+++ b/spec/ruby/core/module/ruby2_keywords_spec.rb
@@ -22,7 +22,6 @@ describe "Module#ruby2_keywords" do
end
h = {a: 1}
- obj.regular(**h).should.equal?(h)
last = mark(**h).last
Hash.ruby2_keywords_hash?(last).should == true
diff --git a/spec/ruby/language/hash_spec.rb b/spec/ruby/language/hash_spec.rb
index 9e2b9bd4c5..6ac382c42c 100644
--- a/spec/ruby/language/hash_spec.rb
+++ b/spec/ruby/language/hash_spec.rb
@@ -220,15 +220,17 @@ describe "The ** operator" do
h.should == { one: 1, two: 2 }
end
- it "does not copy when calling a method taking a positional Hash" do
- def m(h)
- h.delete(:one); h
- end
+ ruby_bug "#20012", ""..."3.3" do
+ it "makes a copy when calling a method taking a positional Hash" do
+ def m(h)
+ h.delete(:one); h
+ end
- h = { one: 1, two: 2 }
- m(**h).should == { two: 2 }
- m(**h).should.equal?(h)
- h.should == { two: 2 }
+ h = { one: 1, two: 2 }
+ m(**h).should == { two: 2 }
+ m(**h).should_not.equal?(h)
+ h.should == { one: 1, two: 2 }
+ end
end
ruby_version_is "3.1" do
diff --git a/spec/ruby/language/keyword_arguments_spec.rb b/spec/ruby/language/keyword_arguments_spec.rb
index e2c816f622..ffb5b1fab0 100644
--- a/spec/ruby/language/keyword_arguments_spec.rb
+++ b/spec/ruby/language/keyword_arguments_spec.rb
@@ -87,13 +87,16 @@ describe "Keyword arguments" do
end
context "**" do
- it "does not copy a non-empty Hash for a method taking (*args)" do
- def m(*args)
- args[0]
- end
+ ruby_version_is "3.3" do
+ it "copies a non-empty Hash for a method taking (*args)" do
+ def m(*args)
+ args[0]
+ end
- h = {a: 1}
- m(**h).should.equal?(h)
+ h = {a: 1}
+ m(**h).should_not.equal?(h)
+ h.should == {a: 1}
+ end
end
it "copies the given Hash for a method taking (**kwargs)" do
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index ef594bd52e..9aca787dff 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -237,16 +237,16 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(true, Hash.ruby2_keywords_hash?(marked))
end
+ def assert_equal_not_same(kw, res)
+ assert_instance_of(Hash, res)
+ assert_equal(kw, res)
+ assert_not_same(kw, res)
+ end
+
def test_keyword_splat_new
kw = {}
h = {a: 1}
- def self.assert_equal_not_same(kw, res)
- assert_instance_of(Hash, res)
- assert_equal(kw, res)
- assert_not_same(kw, res)
- end
-
def self.yo(**kw) kw end
m = method(:yo)
assert_equal(false, yo(**{}).frozen?)
@@ -459,6 +459,20 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal_not_same h, yo(*a, **h)
end
+ def test_keyword_splat_to_non_keyword_method
+ h = {a: 1}.freeze
+
+ def self.yo(kw) kw end
+ assert_equal_not_same(h, yo(**h))
+ assert_equal_not_same(h, method(:yo).(**h))
+ assert_equal_not_same(h, :yo.to_proc.(self, **h))
+
+ def self.yoa(*kw) kw[0] end
+ assert_equal_not_same(h, yoa(**h))
+ assert_equal_not_same(h, method(:yoa).(**h))
+ assert_equal_not_same(h, :yoa.to_proc.(self, **h))
+ end
+
def test_regular_kwsplat
kw = {}
h = {:a=>1}
diff --git a/vm_args.c b/vm_args.c
index 3305b9bd33..92e141bf6e 100644
--- a/vm_args.c
+++ b/vm_args.c
@@ -609,6 +609,10 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
kw_flag &= ~(VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT);
}
else {
+ if (!(kw_flag & VM_CALL_KW_SPLAT_MUT)) {
+ converted_keyword_hash = rb_hash_dup(converted_keyword_hash);
+ }
+
if (last_arg != converted_keyword_hash) {
last_arg = converted_keyword_hash;
args->argv[args->argc-1] = last_arg;