Issue #14183 has been updated by jeremyevans0 (Jeremy Evans).


I think the changes to support the keyword to last positional hash are minimal.  I'm testing a patch now that provides the following behavior:

```ruby
def a(a=1, b:2) [a, b] end;

a('a'=>1, :b=>3)
# 2.6: => [{"a"=>1}, 3]
# my branch: warning: The last argument for `a' (defined at t/t2.rb:1) is split into positional and keyword parameters
#            =>[{"a"=>1}, 3]

a('a'=>1, 'b'=>3)
# 2.6: => [{"a"=>1, "b"=>3}, 2]
# my branch: warning: The keyword argument for `a' (defined at t/t2.rb:1) is passed as the last hash parameter
#            => [{"a"=>1, "b"=>3}, 2]
```

The logic is: if the last argument is a keyword hash that does not contain all symbols, and the method accepts keyword arguments but not a keyword splat, split the keyword hash.  If the hash is split, emit the split warning.  If there are no elements in the keyword hash (all were transferred to the positional hash), then emit the keyword to last hash warning.

I need some more time to test this and make sure it doesn't emit false positive warnings, but I should be able to push it to my branch next week.

One thing I learned during this work was that the VM currently treats the following exactly the same:

```ruby
a('a'=>1, :b=>1)
a(**{'a'=>1, :b=>1})
```

Basically, if the keyword parameter contains a non-Symbol key, the entire keyword parameter is treated as a splatted hash (VM_CALL_KW_SPLAT), instead of being treated as a normal keyword call (VM_CALL_KWARG).

----------------------------------------
Feature #14183: "Real" keyword argument
https://bugs.ruby-lang.org/issues/14183#change-80812

* Author: mame (Yusuke Endoh)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: Next Major
----------------------------------------
In RubyWorld Conference 2017 and RubyConf 2017, Matz officially said that Ruby 3.0 will have "real" keyword arguments.  AFAIK there is no ticket about it, so I'm creating this (based on my understanding).

In Ruby 2, the keyword argument is a normal argument that is a Hash object (whose keys are all symbols) and is passed as the last argument.  This design is chosen because of compatibility, but it is fairly complex, and has been a source of many corner cases where the behavior is not intuitive.  (Some related tickets: #8040, #8316, #9898, #10856, #11236, #11967, #12104, #12717, #12821, #13336, #13647, #14130)

In Ruby 3, a keyword argument will be completely separated from normal arguments.  (Like a block parameter that is also completely separated from normal arguments.)
This change will break compatibility; if you want to pass or accept keyword argument, you always need to use bare `sym: val` or double-splat `**` syntax:

```
# The following calls pass keyword arguments
foo(..., key: val)
foo(..., **hsh)
foo(..., key: val, **hsh)

# The following calls pass **normal** arguments
foo(..., {key: val})
foo(..., hsh)
foo(..., {key: val, **hsh})

# The following method definitions accept keyword argument
def foo(..., key: val)
end
def foo(..., **hsh)
end

# The following method definitions accept **normal** argument
def foo(..., hsh)
end
```

In other words, the following programs WILL NOT work:

```
# This will cause an ArgumentError because the method foo does not accept keyword argument
def foo(a, b, c, hsh)
  p hsh[:key]
end
foo(1, 2, 3, key: 42)

# The following will work; you need to use keyword rest operator explicitly
def foo(a, b, c, **hsh)
  p hsh[:key]
end
foo(1, 2, 3, key: 42)

# This will cause an ArgumentError because the method call does not pass keyword argument
def foo(a, b, c, key: 1)
end
h = {key: 42}
foo(1, 2, 3, h)

# The following will work; you need to use keyword rest operator explicitly
def foo(a, b, c, key: 1)
end
h = {key: 42}
foo(1, 2, 3, **h)
```

I think here is a transition path:

* Ruby 2.6 (or 2.7?) will output a warning when a normal argument is interpreted as keyword argument, or vice versa.
* Ruby 3.0 will use the new semantics.

---Files--------------------------------
vm_args.diff (4.19 KB)
vm_args_v2.diff (4.18 KB)


-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>