<feed xmlns='http://www.w3.org/2005/Atom'>
<title>ruby.git/test/ruby/test_call.rb, branch v4.0.4</title>
<subtitle>The Ruby Programming Language</subtitle>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/'/>
<entry>
<title>Fix allocationless anonymous splat keyword argument check</title>
<updated>2025-12-09T20:44:50+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2025-12-03T01:34:36+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=6409715212d22699bd2751a363b050a5d8b94b83'/>
<id>6409715212d22699bd2751a363b050a5d8b94b83</id>
<content type='text'>
Previously, if an argument splat and keywords are provided by
the caller, it did not check whether the method/proc accepted
keywords before avoiding the allocation. This is incorrect,
because if the method/proc does not accept keywords, the
keywords passed by the caller are added as a positional
argument, so there must be an allocation to avoid mutating
the positional splat argument.

Add a check that if the caller passes keywords, the
method/proc must accept keywords in order to optimize.
If the caller passes a keyword splat, either the
method/proc must accept keywords, or the keyword splat must
be empty in order to optimize.

If keywords are explicitly disallowed via `**nil`, the
optimization should be skipped, because the array is mutated
before the ArgumentError exception is raised.

In addition to a test for the correct behavior, add an
allocation test for a method that accepts an anonymous splat
without keywords.

Fixes [Bug #21757]
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Previously, if an argument splat and keywords are provided by
the caller, it did not check whether the method/proc accepted
keywords before avoiding the allocation. This is incorrect,
because if the method/proc does not accept keywords, the
keywords passed by the caller are added as a positional
argument, so there must be an allocation to avoid mutating
the positional splat argument.

Add a check that if the caller passes keywords, the
method/proc must accept keywords in order to optimize.
If the caller passes a keyword splat, either the
method/proc must accept keywords, or the keyword splat must
be empty in order to optimize.

If keywords are explicitly disallowed via `**nil`, the
optimization should be skipped, because the array is mutated
before the ArgumentError exception is raised.

In addition to a test for the correct behavior, add an
allocation test for a method that accepts an anonymous splat
without keywords.

Fixes [Bug #21757]
</pre>
</div>
</content>
</entry>
<entry>
<title>Fix assertion failure with anonymous splats</title>
<updated>2025-04-03T02:31:05+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2025-04-02T16:27:47+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=29dafa5fc21343803127dda7d608f1f1f7908e7b'/>
<id>29dafa5fc21343803127dda7d608f1f1f7908e7b</id>
<content type='text'>
When calling a method that accepts an anonymous splat and literal
keywords without any arguments, an assertion failure was previously
raised. Set rest_index to 0 when setting rest to the frozen hash,
so the args_argc calculation is accurate.

While here, add more tests for methods with anonymous splats with
and without keywords and keyword splats to confirm behavior is
correct.

Also add a basic bootstrap test that would hit the previous assertion
failure.

Co-authored-by: Jean Boussier &lt;jean.boussier@gmail.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
When calling a method that accepts an anonymous splat and literal
keywords without any arguments, an assertion failure was previously
raised. Set rest_index to 0 when setting rest to the frozen hash,
so the args_argc calculation is accurate.

While here, add more tests for methods with anonymous splats with
and without keywords and keyword splats to confirm behavior is
correct.

Also add a basic bootstrap test that would hit the previous assertion
failure.

Co-authored-by: Jean Boussier &lt;jean.boussier@gmail.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Fix evaluation order issue in f(**h, &amp;h.delete(key))</title>
<updated>2024-09-18T19:46:07+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2024-09-18T19:46:07+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=29f2cb83fb72d970ee07004b2fc019fd31efd823'/>
<id>29f2cb83fb72d970ee07004b2fc019fd31efd823</id>
<content type='text'>
Previously, this would delete the key in `h` before keyword
splatting `h`.  This goes against how ruby handles `f(*a, &amp;a.pop)`
and similar expressions.

Fix this by having the compiler check whether the block pass
expression is safe.  If it is not safe, then dup the keyword
splatted hash before evaluating the block pass expression.

For expression: `h=nil; f(**h, &amp;h.delete(:key))`

VM instructions before:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 getlocal_WC_0                          h@0
0006 getlocal_WC_0                          h@0
0008 putobject                              :key
0010 opt_send_without_block                 &lt;calldata!mid:delete, argc:1, ARGS_SIMPLE&gt;
0012 splatkw
0013 send                                   &lt;calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT&gt;, nil
0016 leave
```

VM instructions after:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 putspecialobject                       1
0006 newhash                                0
0008 getlocal_WC_0                          h@0
0010 opt_send_without_block                 &lt;calldata!mid:core#hash_merge_kwd, argc:2, ARGS_SIMPLE&gt;
0012 getlocal_WC_0                          h@0
0014 putobject                              :key
0016 opt_send_without_block                 &lt;calldata!mid:delete, argc:1, ARGS_SIMPLE&gt;
0018 send                                   &lt;calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT|KW_SPLAT_MUT&gt;, nil
0021 leave
```

This is the same as 07d3bf4832532ae7446c9a6924d79aed60a7a9a5, except that
it removes unnecessary hash allocations when using the prism compiler.

Fixes [Bug #20640]</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Previously, this would delete the key in `h` before keyword
splatting `h`.  This goes against how ruby handles `f(*a, &amp;a.pop)`
and similar expressions.

Fix this by having the compiler check whether the block pass
expression is safe.  If it is not safe, then dup the keyword
splatted hash before evaluating the block pass expression.

For expression: `h=nil; f(**h, &amp;h.delete(:key))`

VM instructions before:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 getlocal_WC_0                          h@0
0006 getlocal_WC_0                          h@0
0008 putobject                              :key
0010 opt_send_without_block                 &lt;calldata!mid:delete, argc:1, ARGS_SIMPLE&gt;
0012 splatkw
0013 send                                   &lt;calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT&gt;, nil
0016 leave
```

VM instructions after:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 putspecialobject                       1
0006 newhash                                0
0008 getlocal_WC_0                          h@0
0010 opt_send_without_block                 &lt;calldata!mid:core#hash_merge_kwd, argc:2, ARGS_SIMPLE&gt;
0012 getlocal_WC_0                          h@0
0014 putobject                              :key
0016 opt_send_without_block                 &lt;calldata!mid:delete, argc:1, ARGS_SIMPLE&gt;
0018 send                                   &lt;calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT|KW_SPLAT_MUT&gt;, nil
0021 leave
```

This is the same as 07d3bf4832532ae7446c9a6924d79aed60a7a9a5, except that
it removes unnecessary hash allocations when using the prism compiler.

Fixes [Bug #20640]</pre>
</div>
</content>
</entry>
<entry>
<title>Revert "Fix evaluation order issue in f(**h, &amp;h.delete(key))"</title>
<updated>2024-09-18T18:26:10+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2024-09-18T18:26:10+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=9c12c39ed175c634dfa0e56619cb30a49e292043'/>
<id>9c12c39ed175c634dfa0e56619cb30a49e292043</id>
<content type='text'>
This reverts commit 07d3bf4832532ae7446c9a6924d79aed60a7a9a5.

No failures in the pull request CI, but there are now allocation
test failures.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
This reverts commit 07d3bf4832532ae7446c9a6924d79aed60a7a9a5.

No failures in the pull request CI, but there are now allocation
test failures.
</pre>
</div>
</content>
</entry>
<entry>
<title>Fix evaluation order issue in f(**h, &amp;h.delete(key))</title>
<updated>2024-09-18T18:18:29+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2024-09-18T18:18:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=07d3bf4832532ae7446c9a6924d79aed60a7a9a5'/>
<id>07d3bf4832532ae7446c9a6924d79aed60a7a9a5</id>
<content type='text'>
Previously, this would delete the key in h before keyword
splatting h.  This goes against how ruby handles f(*a, &amp;a.pop)
and similar expressions.

Fix this by having the compiler check whether the block pass
expression is safe.  If it is not safe, then dup the keyword
splatted hash before evaluating the block pass expression.

For expression: `h=nil; f(**h, &amp;h.delete(:key))`

VM instructions before:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 getlocal_WC_0                          h@0
0006 getlocal_WC_0                          h@0
0008 putobject                              :key
0010 opt_send_without_block                 &lt;calldata!mid:delete, argc:1, ARGS_SIMPLE&gt;
0012 splatkw
0013 send                                   &lt;calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT&gt;, nil
0016 leave
```

VM instructions after:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 putspecialobject                       1
0006 newhash                                0
0008 getlocal_WC_0                          h@0
0010 opt_send_without_block                 &lt;calldata!mid:core#hash_merge_kwd, argc:2, ARGS_SIMPLE&gt;
0012 getlocal_WC_0                          h@0
0014 putobject                              :key
0016 opt_send_without_block                 &lt;calldata!mid:delete, argc:1, ARGS_SIMPLE&gt;
0018 send                                   &lt;calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT|KW_SPLAT_MUT&gt;, nil
0021 leave
```

Fixes [Bug #20640]</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Previously, this would delete the key in h before keyword
splatting h.  This goes against how ruby handles f(*a, &amp;a.pop)
and similar expressions.

Fix this by having the compiler check whether the block pass
expression is safe.  If it is not safe, then dup the keyword
splatted hash before evaluating the block pass expression.

For expression: `h=nil; f(**h, &amp;h.delete(:key))`

VM instructions before:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 getlocal_WC_0                          h@0
0006 getlocal_WC_0                          h@0
0008 putobject                              :key
0010 opt_send_without_block                 &lt;calldata!mid:delete, argc:1, ARGS_SIMPLE&gt;
0012 splatkw
0013 send                                   &lt;calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT&gt;, nil
0016 leave
```

VM instructions after:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 putspecialobject                       1
0006 newhash                                0
0008 getlocal_WC_0                          h@0
0010 opt_send_without_block                 &lt;calldata!mid:core#hash_merge_kwd, argc:2, ARGS_SIMPLE&gt;
0012 getlocal_WC_0                          h@0
0014 putobject                              :key
0016 opt_send_without_block                 &lt;calldata!mid:delete, argc:1, ARGS_SIMPLE&gt;
0018 send                                   &lt;calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT|KW_SPLAT_MUT&gt;, nil
0021 leave
```

Fixes [Bug #20640]</pre>
</div>
</content>
</entry>
<entry>
<title>Prevent warnings "the block passed to ... may be ignored"</title>
<updated>2024-09-13T07:52:38+00:00</updated>
<author>
<name>Yusuke Endoh</name>
<email>mame@ruby-lang.org</email>
</author>
<published>2024-09-13T04:48:51+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=0f3dc2f958bd1447cc459bc4a4f39071a6a07a9c'/>
<id>0f3dc2f958bd1447cc459bc4a4f39071a6a07a9c</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Remove incorrect setting of KW_SPLAT_MUT flag</title>
<updated>2024-08-27T16:08:22+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2024-08-27T16:08:22+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=a3562c2a0abf1c2bdd1d50377b4f929580782594'/>
<id>a3562c2a0abf1c2bdd1d50377b4f929580782594</id>
<content type='text'>
Fixes [Bug #20701]

Co-authored-by: Pablo Herrero &lt;pablodherrero@gmail.com&gt;</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Fixes [Bug #20701]

Co-authored-by: Pablo Herrero &lt;pablodherrero@gmail.com&gt;</pre>
</div>
</content>
</entry>
<entry>
<title>Avoid unnecessary array allocations for f(arg, *arg, **arg, **arg), f(*arg, a: lvar), and other calls</title>
<updated>2024-07-19T05:17:21+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2024-07-11T23:16:40+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=3de20efc308cccc38bf9dacfffca6c626d039a06'/>
<id>3de20efc308cccc38bf9dacfffca6c626d039a06</id>
<content type='text'>
The `f(arg, *arg, **arg, **arg)` case was previously not optimized.
The optimizer didn't optimize this case because of the multiple
keyword splats, and the compiler didn't optimize it because the
`f(*arg, **arg, **arg)` optimization added in
0ee3960685e283d8e75149a8777eb0109d41509a didn't apply.

I found it difficult to apply this optimization without changing
the `setup_args_core` API, since by the time you get to the ARGSCAT
case, you don't know whether you were called recursively or directly,
so I'm not sure if it was possible to know at that point whether the
array allocation could be avoided.

This changes the dup_rest argument in `setup_args_core` from an int
to a pointer to int.  This allows us to track whether we have allocated
a caller side array for multiple splats or splat+post across
recursive calls. Check the pointed value (*dup_rest) to determine the
`splatarray` argument. If dup_rest is 1, then use `splatarray true`
(caller-side array allocation), then set *dup_rest back to 0, ensuring
only a single `splatarray true` per method call.

Before calling `setup_args_core`, check whether the array allocation
can be avoided safely using `splatarray false`.  Optimizable cases are:

```
// f(*arg)
SPLAT

// f(1, *arg)
ARGSCAT
 LIST

// f(*arg, **arg)
ARGSPUSH
 SPLAT
 HASH nd_brace=0

// f(1, *arg, **arg)
ARGSPUSH
  ARGSCAT
   LIST
  HASH nd_brace=0
```

If so, dup_rest is set to 0 instead of 1 to avoid the allocation.

After calling `setup_args_core`, check the flag. If the flag
includes `VM_CALL_ARGS_SPLAT`, and the pointed value has changed,
indicating `splatarray true` was used, then also set
`VM_CALL_ARGS_SPLAT_MUT` in the flag.

My initial attempt at this broke the `f(*ary, &amp;ary.pop)` test,
because we were not duplicating the ary in the splat even though
it was modified later (evaluation order issue). The initial attempt
would also break `f(*ary, **ary.pop)` or `f(*ary, kw: ary.pop)` cases
for the same reason. I added test cases for those evaluation
order issues.

Add setup_args_dup_rest_p static function that checks that a given
node is safe.  Call that on the block pass node to determine if
the block pass node is safe.  Also call it on each of the hash
key/value nodes to test that they are safe.  If any are not safe,
then set dup_rest = 1 so that `splatarray true` will be used to
avoid the evaluation order issue.

This new approach has the affect of optimizing most cases of
literal keywords after positional splats.  Previously, only
static keyword hashes after positional splats avoided array
allocation for the splat.  Now, most dynamic keyword hashes
after positional splats also avoid array allocation.

Add allocation tests for dynamic keyword keyword hashes after
positional splats.

setup_args_dup_rest_p is currently fairly conservative. It
could definitely be expanded to handle additional node types
to reduce allocations in additional cases.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The `f(arg, *arg, **arg, **arg)` case was previously not optimized.
The optimizer didn't optimize this case because of the multiple
keyword splats, and the compiler didn't optimize it because the
`f(*arg, **arg, **arg)` optimization added in
0ee3960685e283d8e75149a8777eb0109d41509a didn't apply.

I found it difficult to apply this optimization without changing
the `setup_args_core` API, since by the time you get to the ARGSCAT
case, you don't know whether you were called recursively or directly,
so I'm not sure if it was possible to know at that point whether the
array allocation could be avoided.

This changes the dup_rest argument in `setup_args_core` from an int
to a pointer to int.  This allows us to track whether we have allocated
a caller side array for multiple splats or splat+post across
recursive calls. Check the pointed value (*dup_rest) to determine the
`splatarray` argument. If dup_rest is 1, then use `splatarray true`
(caller-side array allocation), then set *dup_rest back to 0, ensuring
only a single `splatarray true` per method call.

Before calling `setup_args_core`, check whether the array allocation
can be avoided safely using `splatarray false`.  Optimizable cases are:

```
// f(*arg)
SPLAT

// f(1, *arg)
ARGSCAT
 LIST

// f(*arg, **arg)
ARGSPUSH
 SPLAT
 HASH nd_brace=0

// f(1, *arg, **arg)
ARGSPUSH
  ARGSCAT
   LIST
  HASH nd_brace=0
```

If so, dup_rest is set to 0 instead of 1 to avoid the allocation.

After calling `setup_args_core`, check the flag. If the flag
includes `VM_CALL_ARGS_SPLAT`, and the pointed value has changed,
indicating `splatarray true` was used, then also set
`VM_CALL_ARGS_SPLAT_MUT` in the flag.

My initial attempt at this broke the `f(*ary, &amp;ary.pop)` test,
because we were not duplicating the ary in the splat even though
it was modified later (evaluation order issue). The initial attempt
would also break `f(*ary, **ary.pop)` or `f(*ary, kw: ary.pop)` cases
for the same reason. I added test cases for those evaluation
order issues.

Add setup_args_dup_rest_p static function that checks that a given
node is safe.  Call that on the block pass node to determine if
the block pass node is safe.  Also call it on each of the hash
key/value nodes to test that they are safe.  If any are not safe,
then set dup_rest = 1 so that `splatarray true` will be used to
avoid the evaluation order issue.

This new approach has the affect of optimizing most cases of
literal keywords after positional splats.  Previously, only
static keyword hashes after positional splats avoided array
allocation for the splat.  Now, most dynamic keyword hashes
after positional splats also avoid array allocation.

Add allocation tests for dynamic keyword keyword hashes after
positional splats.

setup_args_dup_rest_p is currently fairly conservative. It
could definitely be expanded to handle additional node types
to reduce allocations in additional cases.
</pre>
</div>
</content>
</entry>
<entry>
<title>Make error messages clear blocks/keywords are disallowed in index assignment</title>
<updated>2024-05-31T15:22:40+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2024-05-31T15:22:40+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=89486c79bb59216dbbbdff69a2c81cb387f64c5f'/>
<id>89486c79bb59216dbbbdff69a2c81cb387f64c5f</id>
<content type='text'>
Blocks and keywords are allowed in regular index.

Also update NEWS to make this more clear.

Co-authored-by: Nobuyoshi Nakada &lt;nobu@ruby-lang.org&gt;</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Blocks and keywords are allowed in regular index.

Also update NEWS to make this more clear.

Co-authored-by: Nobuyoshi Nakada &lt;nobu@ruby-lang.org&gt;</pre>
</div>
</content>
</entry>
<entry>
<title>Prevent "assigned but unused variable" warnings</title>
<updated>2024-04-04T04:29:41+00:00</updated>
<author>
<name>Yusuke Endoh</name>
<email>mame@ruby-lang.org</email>
</author>
<published>2024-04-04T04:24:09+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=cb30c3d2b3385aaa2ac4f8fd2c0d674a15b300d8'/>
<id>cb30c3d2b3385aaa2ac4f8fd2c0d674a15b300d8</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
</feed>
