merge revision(s) 1d94a9e1a4351e01f851dad250ba97dad859ee70: [Backport #21447]

Fix handling of PM_CONSTANT_PATH_NODE node in keyword arguments with ARGS_SPLAT

	This was handled correctly in parse.y (NODE_COLON2), but not in
	prism. This wasn't caught earlier, because I only added tests for
	the optimized case and not the unoptimized case. Add tests for
	the unoptimized case.

	In code terms:

	```ruby
	m(*a, kw: lvar::X)     # Does not require allocation for *a
	m(*a, kw: method()::X) # Requires allocation for *a
	```

	This commit fixes the second case when prism is used.
This commit is contained in:
Takashi Kokubun 2025-07-14 14:29:12 -07:00
parent fee92000fe
commit f0f97886fc
3 changed files with 14 additions and 4 deletions

View File

@ -1855,7 +1855,6 @@ pm_setup_args_dup_rest_p(const pm_node_t *node)
switch (PM_NODE_TYPE(node)) {
case PM_BACK_REFERENCE_READ_NODE:
case PM_CLASS_VARIABLE_READ_NODE:
case PM_CONSTANT_PATH_NODE:
case PM_CONSTANT_READ_NODE:
case PM_FALSE_NODE:
case PM_FLOAT_NODE:
@ -1874,6 +1873,13 @@ pm_setup_args_dup_rest_p(const pm_node_t *node)
case PM_SYMBOL_NODE:
case PM_TRUE_NODE:
return false;
case PM_CONSTANT_PATH_NODE: {
const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
if (cast->parent != NULL) {
return pm_setup_args_dup_rest_p(cast->parent);
}
return false;
}
case PM_IMPLICIT_NODE:
return pm_setup_args_dup_rest_p(((const pm_implicit_node_t *) node)->value);
default:

View File

@ -742,13 +742,15 @@ class TestAllocation < Test::Unit::TestCase
def test_no_array_allocation_with_splat_and_nonstatic_keywords
check_allocations(<<~RUBY)
def self.keyword(a: nil, b: nil#{block}); end
def self.Object; Object end
check_allocations(0, 1, "keyword(*empty_array, a: empty_array#{block})") # LVAR
check_allocations(0, 1, "->{keyword(*empty_array, a: empty_array#{block})}.call") # DVAR
check_allocations(0, 1, "$x = empty_array; keyword(*empty_array, a: $x#{block})") # GVAR
check_allocations(0, 1, "@x = empty_array; keyword(*empty_array, a: @x#{block})") # IVAR
check_allocations(0, 1, "self.class.const_set(:X, empty_array); keyword(*empty_array, a: X#{block})") # CONST
check_allocations(0, 1, "keyword(*empty_array, a: Object::X#{block})") # COLON2
check_allocations(0, 1, "keyword(*empty_array, a: Object::X#{block})") # COLON2 - safe
check_allocations(1, 1, "keyword(*empty_array, a: Object()::X#{block})") # COLON2 - unsafe
check_allocations(0, 1, "keyword(*empty_array, a: ::X#{block})") # COLON3
check_allocations(0, 1, "T = self; #{'B = block' unless block.empty?}; class Object; @@x = X; T.keyword(*X, a: @@x#{', &B' unless block.empty?}) end") # CVAR
check_allocations(0, 1, "keyword(*empty_array, a: empty_array, b: 1#{block})") # INTEGER
@ -807,13 +809,15 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(<<~RUBY)
keyword = keyword = proc{ |a: nil, b: nil #{block}| }
def self.Object; Object end
check_allocations(0, 1, "keyword.(*empty_array, a: empty_array#{block})") # LVAR
check_allocations(0, 1, "->{keyword.(*empty_array, a: empty_array#{block})}.call") # DVAR
check_allocations(0, 1, "$x = empty_array; keyword.(*empty_array, a: $x#{block})") # GVAR
check_allocations(0, 1, "@x = empty_array; keyword.(*empty_array, a: @x#{block})") # IVAR
check_allocations(0, 1, "self.class.const_set(:X, empty_array); keyword.(*empty_array, a: X#{block})") # CONST
check_allocations(0, 1, "keyword.(*empty_array, a: Object::X#{block})") # COLON2
check_allocations(0, 1, "keyword.(*empty_array, a: Object::X#{block})") # COLON2 - safe
check_allocations(1, 1, "keyword.(*empty_array, a: Object()::X#{block})") # COLON2 - unsafe
check_allocations(0, 1, "keyword.(*empty_array, a: ::X#{block})") # COLON3
check_allocations(0, 1, "T = keyword; #{'B = block' unless block.empty?}; class Object; @@x = X; T.(*X, a: @@x#{', &B' unless block.empty?}) end") # CVAR
check_allocations(0, 1, "keyword.(*empty_array, a: empty_array, b: 1#{block})") # INTEGER

View File

@ -11,7 +11,7 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 4
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
#define RUBY_PATCHLEVEL 45
#define RUBY_PATCHLEVEL 46
#include "ruby/version.h"
#include "ruby/internal/abi.h"