Avoid array allocation for *nil, by not calling nil.to_a
The following method call:
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:
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.
Avoid array allocation for *nil, by not calling nil.to_a
The following method call:
A method call such as
a(*nil)
previously allocated an array, becauseit calls
nil.to_a
, but I have determined this array allocation isunnecessary. The instructions in this case are:
The method call uses
ARGS_SPLAT
withoutARGS_SPLAT_MUT
, so thereturned 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 optimizationin this case is to have
splatarray false
push a shared empty frozenarray, 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 behaviorof
*nil
not callingnil.to_a
is more consistent with how**nil
does not call
nil.to_hash
. Also, so much Ruby code would break ifnil.to_a
returned something different from the empty hash, that it'sdifficult 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
callednil.to_a
in some cases and not others, so this changes other cases to not
call
nil.to_a
:For
[*nil]
, this usessplatarray true
, which now allocates anew array for a
nil
argument without callingnil.to_a
.For
[1, *nil]
, this usesconcattoarray
, which now returnsthe 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]