diff options
author | Jeremy Evans <[email protected]> | 2025-01-18 01:40:05 -0800 |
---|---|---|
committer | Jeremy Evans <[email protected]> | 2025-03-27 11:17:40 -0700 |
commit | 67d1dd2ebd622c27d2ae0681c544d9f5d2f5349b (patch) | |
tree | 2705052203d04d21b31a61f7b1ebac01fb30a484 /bootstraptest/test_literal.rb | |
parent | 6ecfe643b5d8d64682c6f6bce5b27db5c007331d (diff) |
Avoid array allocation for *nil, by not calling nil.to_a
The following method call:
```ruby
a(*nil)
```
A method call such as `a(*nil)` previously allocated an array, because
it calls `nil.to_a`, but I have determined this array allocation is
unnecessary. The instructions in this case are:
```
0000 putself ( 1)[Li]
0001 putnil
0002 splatarray false
0004 opt_send_without_block <calldata!mid:a, argc:1, ARGS_SPLAT|FCALL>
0006 leave
```
The method call uses `ARGS_SPLAT` without `ARGS_SPLAT_MUT`, so the
returned array doesn't need to be mutable. I believe all cases where
`splatarray false` are used allow the returned object to be frozen,
since the `false` means to not duplicate the array. The optimization
in this case is to have `splatarray false` push a shared empty frozen
array, instead of calling `nil.to_a` to return a newly allocated array.
There is a slightly backwards incompatibility with this optimization,
in that `nil.to_a` is not called. However, I believe the new behavior
of `*nil` not calling `nil.to_a` is more consistent with how `**nil`
does not call `nil.to_hash`. Also, so much Ruby code would break if
`nil.to_a` returned something different from the empty hash, that it's
difficult to imagine anyone actually doing that in real code, though
we have a few tests/specs for that.
I think it would be bad for consistency if `*nil` called `nil.to_a`
in some cases and not others, so this changes other cases to not
call `nil.to_a`:
For `[*nil]`, this uses `splatarray true`, which now allocates a
new array for a `nil` argument without calling `nil.to_a`.
For `[1, *nil]`, this uses `concattoarray`, which now returns
the first array if the second array is `nil`.
This updates the allocation tests to check that the array allocations
are avoided where possible.
Implements [Feature #21047]
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/12597
Diffstat (limited to 'bootstraptest/test_literal.rb')
-rw-r--r-- | bootstraptest/test_literal.rb | 4 |
1 files changed, 2 insertions, 2 deletions
diff --git a/bootstraptest/test_literal.rb b/bootstraptest/test_literal.rb index 7295f7a148..39e6527027 100644 --- a/bootstraptest/test_literal.rb +++ b/bootstraptest/test_literal.rb @@ -117,8 +117,8 @@ assert_equal '1', 'a = [obj = Object.new]; a.size' assert_equal 'true', 'a = [obj = Object.new]; a[0] == obj' assert_equal '5', 'a = [1,2,3]; a[1] = 5; a[1]' assert_equal 'bar', '[*:foo];:bar' -assert_equal '[1, 2]', 'def nil.to_a; [2]; end; [1, *nil]' -assert_equal '[1, 2]', 'def nil.to_a; [1, 2]; end; [*nil]' +assert_equal '[]', 'def nil.to_a; [1, 2]; end; [*nil]' +assert_equal '[1]', 'def nil.to_a; [2]; end; [1, *nil]' assert_equal '[0, 1, {2 => 3}]', '[0, *[1], 2=>3]', "[ruby-dev:31592]" |