From: eregontp@... Date: 2020-01-22T10:46:45+00:00 Subject: [ruby-core:96970] [Ruby master Bug#16519] pp [Hash.ruby2_keywords_hash({})] shows `[nil]` Issue #16519 has been updated by Eregon (Benoit Daloze). > To paraphrase @Eregon , if a ruby committer heavily involved in the process cannot figure out even why a problem occurs, let alone the solution, what chance does a standard ruby programmer have? For the record, by "I don't know why it goes wrong." I meant "This is after midnight and I have no time to investigate it further today". But yes, I'm surprised a ruby2_keywords flagged Hash is just removed and "lost". I think we might want to fix that. I think the actual issue here might be that we remove empty keyword arguments, which is also inconsistent with non-empty ones when they are given to a method not accepting keyword arguments: ```ruby def target(*args) args end # target does not accept keyword arguments, it shouldn't matter if we pass a last positional Hash or use keyword arguments syntax. # But actually it does in 2.7.0 and master! h = {} p target(**{a: 1}) # => [{:a=>1}] p target(**h) # => [] in 2.7.0 and master (inconsistent with above), [{}] in 2.6 and ruby2_keywords by default p target(h) # => [{}] ``` We need to remove empty keyword arguments for one reason: to make `*args, **kwargs` delegation work with a target method not taking kwargs: ```ruby def target(a, b = nil) [a, b] end def delegate(*args, **kwargs) target(*args, **kwargs) end p delegate(1) # => [1, {}] in 2.6.5, [1, nil] in 2.7.0, master and ruby2_keywords by default ``` Note that if `target` does accept keyword arguments, it would not change anything whether we pass or not an empty keyword Hash in Ruby 3.0+. Here are two ideas to make `*args, **kwargs` delegation work, but not remove empty keyword hashes when passed to a method not accepting keyword arguments: * When the keyword Hash is created out of nothing, i.e., by `def m(**hash_created_by_double_splat)` and `m` was not passed any keyword argument, we could remember that, and when calling another method with `foo(**hash_created_by_double_splat)` remove that Hash, since the user never intended it. For other cases, keep pass the keyword Hash just like for non-empty keyword hashes, since the user passed keywords explicitly. * Maybe ruby2_keywords-flagged Hash should never be removed. I need to think more about that case and experiment with it. @jeremyevans0 @mame What do you think of that? ---------------------------------------- Bug #16519: pp [Hash.ruby2_keywords_hash({})] shows `[nil]` https://bugs.ruby-lang.org/issues/16519#change-83994 * Author: Eregon (Benoit Daloze) * Status: Open * Priority: Normal * Assignee: * Target version: * ruby -v: ruby 2.8.0dev (2020-01-21T13:45:10Z master 5798d35ff6) [x86_64-linux] * Backport: 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: REQUIRED ---------------------------------------- This happens on `master`: ``` $ ruby -ve 'ruby2_keywords def flag(*a); a.last; end; pp [flag(**{})]' ruby 2.8.0dev (2020-01-21T13:45:10Z master 5798d35ff6) [x86_64-linux] [nil] ``` Of course it should be `[{}]`, as it is for `pp [{}]`. On 2.7.0 it warns (should be fixed, it's valid to `pp` a flagged Hash): ``` $ ruby -ve 'ruby2_keywords def flag(*a); a.last; end; pp [flag(**{})]' ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux] [/home/eregon/.rubies/ruby-2.7.0/lib/ruby/2.7.0/pp.rb:226: warning: Passing the keyword argument as the last hash parameter is deprecated /home/eregon/.rubies/ruby-2.7.0/lib/ruby/2.7.0/pp.rb:334: warning: The called method is defined here {}] ``` The warning being in the middle of the output is a fun fact here. Lines it refers to (still the same on current master): https://github.com/ruby/ruby/blob/v2_7_0/lib/pp.rb#L226 https://github.com/ruby/ruby/blob/v2_7_0/lib/pp.rb#L334 This is very confusing as it can happen during `test-all` and then show output such as: ``` <[{:a=>1}]> expected but was <[{:a=>1}, nil]>. ``` when the reality is (can be verified with `p` before the `assert_equal`): ``` <[{:a=>1}]> expected but was <[{:a=>1}, {}]>. ``` -- https://bugs.ruby-lang.org/ Unsubscribe: