From: "mame (Yusuke Endoh)" Date: 2022-03-22T05:31:29+00:00 Subject: [ruby-core:108013] [Ruby master Bug#18625] ruby2_keywords does not unmark the hash if the receiving method has a *rest parameter Issue #18625 has been updated by mame (Yusuke Endoh). As far as I understand, this change will break the following code: ``` def target(a:) p a end # after the patch, ruby2_keywords is requried here def bar(*args) target(*args) end ruby2_keywords def foo(*args) bar(*args) end p foo(a: 42) #=> 42 in Ruby 3.1 #=> ArgumentError after the patch ``` After the patch, we need to add `ruby2_keywords` to the definition of `bar`. According to our previous experience, rails is using this kind of multi-stage delegation pattern so much. Maybe we need to ask the core developers of rails to check and support the change. cc/ @kamipo ---------------------------------------- Bug #18625: ruby2_keywords does not unmark the hash if the receiving method has a *rest parameter https://bugs.ruby-lang.org/issues/18625#change-96970 * Author: Eregon (Benoit Daloze) * Status: Open * Priority: Normal * Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- The code below shows the inconsistency. In all cases the `marked` Hash is copied at call sites using `some_call(*args)`, however for the case of `splat` it keeps the ruby2_keywords flag to true, and not false as expected. This can be observed in user code and will hurt migration from `ruby2_keywords` to other ways of delegation (`(...)` and `(*args, **kwargs)`). I believe this is another manifestation of #16466. ```ruby ruby2_keywords def foo(*args) args end def single(arg) arg end def splat(*args) args.last end def kwargs(**kw) kw end h = { a: 1 } args = foo(**h) marked = args.last Hash.ruby2_keywords_hash?(marked) # => true after_usage = single(*args) after_usage == h # => true after_usage.equal?(marked) # => false p Hash.ruby2_keywords_hash?(after_usage) # => false after_usage = splat(*args) after_usage == h # => true after_usage.equal?(marked) # => false p Hash.ruby2_keywords_hash?(after_usage) # => true, BUG, should be false after_usage = kwargs(*args) after_usage == h # => true after_usage.equal?(marked) # => false p Hash.ruby2_keywords_hash?(after_usage) # => false Hash.ruby2_keywords_hash?(marked) # => true ``` I'm implementing Ruby 3 kwargs in TruffleRuby and this came up as an inconsistency in specs. In TruffleRuby it's also basically not possible to implement this behavior, because at a splat call site where we check for a last Hash argument marked as ruby2_keywords, we have no idea of which method will be called yet, and so cannot differentiate behavior based on that. cc @jeremyevans0 @mame -- https://bugs.ruby-lang.org/ Unsubscribe: