diff options
| author | Daniel Colson <danieljamescolson@gmail.com> | 2025-05-21 22:00:12 -0400 |
|---|---|---|
| committer | Yusuke Endoh <mame@ruby-lang.org> | 2025-05-22 12:25:55 +0900 |
| commit | 056497319658cbefe22351c6ec5c9fa6e4df72bd (patch) | |
| tree | de3d1719f09a4fb867803d0dc63541d6c43327de /test/ruby | |
| parent | 7154b4208be5fbc314ba2aac765c6f1530e2ada6 (diff) | |
[Bug #21357] Fix crash in Hash#merge with block
Prior to https://github.com/ruby/ruby/commit/49b306ecb9e2e9e06e0b1590bacc5f4b38169c3c
the `optional_arg` passed from `rb_hash_update_block_i` to `tbl_update`
was a hash value (i.e. a VALUE). After that commit it changed to an
`update_call_args`.
If the block sets or changes the value, `tbl_update_modify` will set the
`arg.value` back to an actual value and we won't crash. But in the case
where the block returns the original value we end up calling
`RB_OBJ_WRITTEN` with the `update_call_args` which is not expected and
may crash.
`arg.value` appears to only be used to pass to `RB_OBJ_WRITTEN` (others
who need the `update_call_args` get it from `arg.arg`), so I don't think
it needs to be set to anything upfront. And `tbl_update_modify` will set
the `arg.value` in the cases we need the write barrier.
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/13404
Diffstat (limited to 'test/ruby')
| -rw-r--r-- | test/ruby/test_hash.rb | 5 |
1 files changed, 5 insertions, 0 deletions
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index e75cdfe7f9..7b8cf1c6c4 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -2355,6 +2355,11 @@ class TestHashOnly < Test::Unit::TestCase end end + def test_bug_21357 + h = {x: []}.merge(x: nil) { |_k, v1, _v2| v1 } + assert_equal({x: []}, h) + end + def test_any_hash_fixable 20.times do assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") |
