<feed xmlns='http://www.w3.org/2005/Atom'>
<title>ruby.git/test/ruby/test_keyword.rb, branch v3_4_9</title>
<subtitle>The Ruby Programming Language</subtitle>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/'/>
<entry>
<title>Do not respect ruby2_keywords on method/proc with post arguments</title>
<updated>2025-08-27T21:29:42+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2025-05-30T05:32:09+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=fd036dbc3f39e6bdce735edf9ca187a690fe2079'/>
<id>fd036dbc3f39e6bdce735edf9ca187a690fe2079</id>
<content type='text'>
Previously, ruby2_keywords could be used on a method or proc with
post arguments, but I don't think the behavior is desired:

```ruby
def a(*c, **kw) [c, kw] end
def b(*a, b) a(*a, b) end
ruby2_keywords(:b)

b({foo: 1}, bar: 1)
```

This changes ruby2_keywords to emit a warning and not set the
flag on a method/proc with post arguments.

While here, fix the ruby2_keywords specs for warnings, since they
weren't testing what they should be testing.  They all warned
because the method didn't accept a rest argument, not because it
accepted a keyword or keyword rest argument.

[Backport #21402]
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Previously, ruby2_keywords could be used on a method or proc with
post arguments, but I don't think the behavior is desired:

```ruby
def a(*c, **kw) [c, kw] end
def b(*a, b) a(*a, b) end
ruby2_keywords(:b)

b({foo: 1}, bar: 1)
```

This changes ruby2_keywords to emit a warning and not set the
flag on a method/proc with post arguments.

While here, fix the ruby2_keywords specs for warnings, since they
weren't testing what they should be testing.  They all warned
because the method didn't accept a rest argument, not because it
accepted a keyword or keyword rest argument.

[Backport #21402]
</pre>
</div>
</content>
</entry>
<entry>
<title>ruby2_keywords warnings: Quote non-UTF8 method names fully</title>
<updated>2024-12-19T15:32:14+00:00</updated>
<author>
<name>Alan Wu</name>
<email>XrXr@users.noreply.github.com</email>
</author>
<published>2024-12-19T15:32:14+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=ce849d565bf6aae8e0179fffb04eb1f665f17347'/>
<id>ce849d565bf6aae8e0179fffb04eb1f665f17347</id>
<content type='text'>
It used to quote only part of the method name because NUL byte in
the method terminates the C string:

```
(irb)&gt; "abcdef".encode("UTF-16LE").bytes
=&gt; [97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0]
```

```
expected: /abcdef/
actual: warning: Skipping set of ruby2_keywords flag for a (method not defined in Ruby)\n".
```</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
It used to quote only part of the method name because NUL byte in
the method terminates the C string:

```
(irb)&gt; "abcdef".encode("UTF-16LE").bytes
=&gt; [97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0]
```

```
expected: /abcdef/
actual: warning: Skipping set of ruby2_keywords flag for a (method not defined in Ruby)\n".
```</pre>
</div>
</content>
</entry>
<entry>
<title>Update ruby test for colon-style hash inspect</title>
<updated>2024-10-03T09:47:09+00:00</updated>
<author>
<name>tompng</name>
<email>tomoyapenguin@gmail.com</email>
</author>
<published>2024-06-07T15:23:12+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=7237af75d2aa700454fbaba9bda7a9283413d903'/>
<id>7237af75d2aa700454fbaba9bda7a9283413d903</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Prevent warnings: the block passed to ... may be ignored</title>
<updated>2024-09-15T01:06:11+00:00</updated>
<author>
<name>Yusuke Endoh</name>
<email>mame@ruby-lang.org</email>
</author>
<published>2024-09-15T01:06:11+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=532af89e3b5b78dd3a6fe29c6cc64ad1b073afe2'/>
<id>532af89e3b5b78dd3a6fe29c6cc64ad1b073afe2</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Avoid hash allocation for certain proc calls</title>
<updated>2024-08-20T02:00:37+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2024-08-20T02:00:37+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=ea7ceff82cec98d0c419e9807dcb33dcc08b56fa'/>
<id>ea7ceff82cec98d0c419e9807dcb33dcc08b56fa</id>
<content type='text'>
Previously, proc calls such as:

```ruby
proc{|| }.(**empty_hash)
proc{|b: 1| }.(**r2k_array_with_empty_hash)
```

both allocated hashes unnecessarily, due to two separate code paths.

The first call goes through CALLER_SETUP_ARG/vm_caller_setup_keyword_hash,
and is simple to fix by not duping an empty keyword hash that will be
dropped.

The second case is more involved, in setup_parameters_complex, but is
fixed the exact same way as when the ruby2_keywords hash is not empty,
by flattening the rest array to the VM stack, ignoring the last
element (the empty keyword splat).  Add a flatten_rest_array static
function to handle this case.

Update test_allocation.rb to automatically convert the method call
allocation tests to proc allocation tests, at least for the calls
that can be converted.  With the code changes, all proc call
allocation tests pass, showing that proc calls and method calls
now allocate the same number of objects.

I've audited the allocation tests, and I believe that all of the low
hanging fruit has been collected.  All remaining allocations are
either caller side:

* Positional splat + post argument
* Multiple positional splats
* Literal keywords + keyword splat
* Multiple keyword splats

Or callee side:

* Positional splat parameter
* Keyword splat parameter
* Keyword to positional argument conversion for methods that don't accept keywords
* ruby2_keywords method called with keywords

Reapplies abc04e898b627ab37fa9dd5e330f239768778d8b, which was reverted at
d56470a27c5a8a2e7aee7a76cea445c2d29c0c59, with the addition of a bug fix and
test.

Fixes [Bug #20679]</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Previously, proc calls such as:

```ruby
proc{|| }.(**empty_hash)
proc{|b: 1| }.(**r2k_array_with_empty_hash)
```

both allocated hashes unnecessarily, due to two separate code paths.

The first call goes through CALLER_SETUP_ARG/vm_caller_setup_keyword_hash,
and is simple to fix by not duping an empty keyword hash that will be
dropped.

The second case is more involved, in setup_parameters_complex, but is
fixed the exact same way as when the ruby2_keywords hash is not empty,
by flattening the rest array to the VM stack, ignoring the last
element (the empty keyword splat).  Add a flatten_rest_array static
function to handle this case.

Update test_allocation.rb to automatically convert the method call
allocation tests to proc allocation tests, at least for the calls
that can be converted.  With the code changes, all proc call
allocation tests pass, showing that proc calls and method calls
now allocate the same number of objects.

I've audited the allocation tests, and I believe that all of the low
hanging fruit has been collected.  All remaining allocations are
either caller side:

* Positional splat + post argument
* Multiple positional splats
* Literal keywords + keyword splat
* Multiple keyword splats

Or callee side:

* Positional splat parameter
* Keyword splat parameter
* Keyword to positional argument conversion for methods that don't accept keywords
* ruby2_keywords method called with keywords

Reapplies abc04e898b627ab37fa9dd5e330f239768778d8b, which was reverted at
d56470a27c5a8a2e7aee7a76cea445c2d29c0c59, with the addition of a bug fix and
test.

Fixes [Bug #20679]</pre>
</div>
</content>
</entry>
<entry>
<title>clear `kw_flag` if given hash is nil</title>
<updated>2024-06-13T04:52:39+00:00</updated>
<author>
<name>Koichi Sasada</name>
<email>ko1@atdot.net</email>
</author>
<published>2024-06-13T02:48:48+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=fc33559c40e08e4ae0a98821a679abddc4bb247c'/>
<id>fc33559c40e08e4ae0a98821a679abddc4bb247c</id>
<content type='text'>
https://bugs.ruby-lang.org/issues/20570 is caused I missed to
clear the `kw_flag` even if `keyword_hash` is nil.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
https://bugs.ruby-lang.org/issues/20570 is caused I missed to
clear the `kw_flag` even if `keyword_hash` is nil.
</pre>
</div>
</content>
</entry>
<entry>
<title>Do not apply anon_rest optimization when passed array uses keyword-flagged hash</title>
<updated>2024-03-22T23:54:07+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2024-03-22T22:29:13+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=2dbcc123f4f605b51a3698d38ccd53ba6ef482ac'/>
<id>2dbcc123f4f605b51a3698d38ccd53ba6ef482ac</id>
<content type='text'>
The optimization sets args-&gt;rest_dupped to avoid allocating an array,
but this is not safe if the splat array ends in a keyword flagged
hash.  Unset args-&gt;rest_dupped in this case.

Fixes [Bug #20388]
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The optimization sets args-&gt;rest_dupped to avoid allocating an array,
but this is not safe if the splat array ends in a keyword flagged
hash.  Unset args-&gt;rest_dupped in this case.

Fixes [Bug #20388]
</pre>
</div>
</content>
</entry>
<entry>
<title>Correctly set anon_kwrest flag for def f(b: 1, **)</title>
<updated>2024-03-01T20:36:19+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2024-02-01T20:28:32+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=99384bac28e10895623ecbc73fc09fd7d41fc897'/>
<id>99384bac28e10895623ecbc73fc09fd7d41fc897</id>
<content type='text'>
In cases where a method accepts both keywords and an anonymous
keyword splat, the method was not marked as taking an anonymous
keyword splat.  Fix that in the compiler.

Doing that broke handling of nil keyword splats in yjit, so
update yjit to handle that.

Add a test to check that calling a method that accepts both
a keyword argument and an anonymous keyword splat does not
modify a passed keyword splat hash.

Move the anon_kwrest check from setup_parameters_complex to
ignore_keyword_hash_p, and only use it if the keyword hash
is already a hash. This should speed things up slightly as
it avoids a check previously used for all callers of
setup_parameters_complex.

Co-authored-by: Nobuyoshi Nakada &lt;nobu@ruby-lang.org&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
In cases where a method accepts both keywords and an anonymous
keyword splat, the method was not marked as taking an anonymous
keyword splat.  Fix that in the compiler.

Doing that broke handling of nil keyword splats in yjit, so
update yjit to handle that.

Add a test to check that calling a method that accepts both
a keyword argument and an anonymous keyword splat does not
modify a passed keyword splat hash.

Move the anon_kwrest check from setup_parameters_complex to
ignore_keyword_hash_p, and only use it if the keyword hash
is already a hash. This should speed things up slightly as
it avoids a check previously used for all callers of
setup_parameters_complex.

Co-authored-by: Nobuyoshi Nakada &lt;nobu@ruby-lang.org&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Allow `foo(**nil, &amp;block_arg)`</title>
<updated>2024-02-12T18:02:50+00:00</updated>
<author>
<name>Alan Wu</name>
<email>XrXr@users.noreply.github.com</email>
</author>
<published>2024-02-09T00:10:51+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=e878bbd641f255b5d8f9e99b84ff8082a5b7fdaf'/>
<id>e878bbd641f255b5d8f9e99b84ff8082a5b7fdaf</id>
<content type='text'>
Previously, `**nil` by itself worked, but if you add a block argument,
it raised a conversion error. The presence of the block argument
shouldn't change how keyword splat works.

See: &lt;https://bugs.ruby-lang.org/issues/20064&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Previously, `**nil` by itself worked, but if you add a block argument,
it raised a conversion error. The presence of the block argument
shouldn't change how keyword splat works.

See: &lt;https://bugs.ruby-lang.org/issues/20064&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Fix crash when passing large keyword splat to method accepting keywords and keyword splat</title>
<updated>2024-02-12T06:48:38+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2024-01-31T01:15:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=c20e819e8b04e84a4103ca9a036022543459c213'/>
<id>c20e819e8b04e84a4103ca9a036022543459c213</id>
<content type='text'>
The following code previously caused a crash:

```ruby
h = {}
1000000.times{|i| h[i.to_s.to_sym] = i}
def f(kw: 1, **kws) end
f(**h)
```

Inside a thread or fiber, the size of the keyword splat could be much smaller
and still cause a crash.

I found this issue while optimizing method calling by reducing implicit
allocations.  Given the following code:

```ruby
def f(kw: , **kws) end
kw = {kw: 1}
f(**kw)
```

The `f(**kw)` call previously allocated two hashes callee side instead of a
single hash.  This is because `setup_parameters_complex` would extract the
keywords from the keyword splat hash to the C stack, to attempt to mirror
the case when literal keywords are passed without a keyword splat.  Then,
`make_rest_kw_hash` would build a new hash based on the extracted keywords
that weren't used for literal keywords.

Switch the implementation so that if a keyword splat is passed, literal keywords
are deleted from the keyword splat hash (or a copy of the hash if the hash is
not mutable).

In addition to avoiding the crash, this new approach is much more
efficient in all cases.  With the included benchmark:

```
                                1
            miniruby:   5247879.9 i/s
     miniruby-before:   2474050.2 i/s - 2.12x  slower

                        1_mutable
            miniruby:   1797036.5 i/s
     miniruby-before:   1239543.3 i/s - 1.45x  slower

                               10
            miniruby:   1094750.1 i/s
     miniruby-before:    365529.6 i/s - 2.99x  slower

                       10_mutable
            miniruby:    407781.7 i/s
     miniruby-before:    225364.0 i/s - 1.81x  slower

                              100
            miniruby:    100992.3 i/s
     miniruby-before:     32703.6 i/s - 3.09x  slower

                      100_mutable
            miniruby:     40092.3 i/s
     miniruby-before:     21266.9 i/s - 1.89x  slower

                             1000
            miniruby:     21694.2 i/s
     miniruby-before:      4949.8 i/s - 4.38x  slower

                     1000_mutable
            miniruby:      5819.5 i/s
     miniruby-before:      2995.0 i/s - 1.94x  slower
```
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The following code previously caused a crash:

```ruby
h = {}
1000000.times{|i| h[i.to_s.to_sym] = i}
def f(kw: 1, **kws) end
f(**h)
```

Inside a thread or fiber, the size of the keyword splat could be much smaller
and still cause a crash.

I found this issue while optimizing method calling by reducing implicit
allocations.  Given the following code:

```ruby
def f(kw: , **kws) end
kw = {kw: 1}
f(**kw)
```

The `f(**kw)` call previously allocated two hashes callee side instead of a
single hash.  This is because `setup_parameters_complex` would extract the
keywords from the keyword splat hash to the C stack, to attempt to mirror
the case when literal keywords are passed without a keyword splat.  Then,
`make_rest_kw_hash` would build a new hash based on the extracted keywords
that weren't used for literal keywords.

Switch the implementation so that if a keyword splat is passed, literal keywords
are deleted from the keyword splat hash (or a copy of the hash if the hash is
not mutable).

In addition to avoiding the crash, this new approach is much more
efficient in all cases.  With the included benchmark:

```
                                1
            miniruby:   5247879.9 i/s
     miniruby-before:   2474050.2 i/s - 2.12x  slower

                        1_mutable
            miniruby:   1797036.5 i/s
     miniruby-before:   1239543.3 i/s - 1.45x  slower

                               10
            miniruby:   1094750.1 i/s
     miniruby-before:    365529.6 i/s - 2.99x  slower

                       10_mutable
            miniruby:    407781.7 i/s
     miniruby-before:    225364.0 i/s - 1.81x  slower

                              100
            miniruby:    100992.3 i/s
     miniruby-before:     32703.6 i/s - 3.09x  slower

                      100_mutable
            miniruby:     40092.3 i/s
     miniruby-before:     21266.9 i/s - 1.89x  slower

                             1000
            miniruby:     21694.2 i/s
     miniruby-before:      4949.8 i/s - 4.38x  slower

                     1000_mutable
            miniruby:      5819.5 i/s
     miniruby-before:      2995.0 i/s - 1.94x  slower
```
</pre>
</div>
</content>
</entry>
</feed>
