Bug #20012
closedFix keyword splat passing as regular argument
Description
Since Ruby 3.0, Ruby has passed a keyword splat as a regular argument in the case of a call to a Ruby method where the method does not accept keyword arguments, if the method call does not contain an argument splat:
def self.f(obj) obj end
def self.fs(*obj) obj[0] end
h = {a: 1}
f(**h).equal?(h) # Before: true; After: false
fs(**h).equal?(h) # Before: true; After: false
a = []
f(*a, **h).equal?(h) # Before and After: false
fs(*a, **h).equal?(h) # Before and After: false
The fact that the behavior differs when passing an empty argument splat makes it obvious that something is not working the way it is intended. The fact that the hash is copied for C methods also makes it obvious. Ruby 2 always copied the keyword splat hash, and I think that is the expected behavior in Ruby 3.
This bug is because of a missed check in setup_parameters_complex. If the keyword splat passed is not mutable, then it points to an existing object and not a new object, and therefore it must be copied.
I did not bisect to find the commit that introduced the problem, but this seems likely to be something I introduced during keyword argument separation. I'm surprised that this has not been filed as a bug sooner. Maybe it just rarely comes up in practice, and I only discovered it recently. Possibly, other developers that may have discovered this may have decided against submitting this as an issue, as we have had specs for two years that give the impression that the broken behavior was expected.
I've submitted a pull request to fix this: https://github.com/ruby/ruby/pull/8970
Updated by matz (Yukihiro Matsumoto) 12 months ago
I am in favor of this change, but at the same time concerned about compatibility. I'd like to make sure there won't be any major problems before adopting it.
Matz.
Updated by jeremyevans0 (Jeremy Evans) 12 months ago
matz (Yukihiro Matsumoto) wrote in #note-1:
I am in favor of this change, but at the same time concerned about compatibility. I'd like to make sure there won't be any major problems before adopting it.
Would you prefer we merge it soon and have the fix in Ruby 3.3, or should we wait until after Ruby 3.3 and put the fix in Ruby 3.4, which will allow for more testing time before release?
Updated by mame (Yusuke Endoh) 11 months ago
@jeremyevans0 (Jeremy Evans) In today's dev meeting, @matz (Yukihiro Matsumoto) said it should be fixed soon, so let's merge it. If someone complains it, we may reconsider.
Updated by jeremyevans0 (Jeremy Evans) 11 months ago
- Status changed from Open to Closed