Avoid opt_aset_with optimization inside multiple assignment

Previously, since the opt_aset_with optimization was introduced,
use of the opt_aset_with optimization inside multiple assignment
would result in a segfault or incorrect instructions.

Fixes [Bug #21012]

Co-authored-by: Nobuyoshi Nakada <nobu.nakada@gmail.com>
This commit is contained in:
Jeremy Evans 2025-01-08 08:49:51 -08:00 committed by GitHub
parent e728170043
commit e0d600ec19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
Notes: git 2025-01-08 16:50:14 +00:00
Merged: https://github.com/ruby/ruby/pull/12528

Merged-By: jeremyevans <code@jeremyevans.net>
3 changed files with 16 additions and 1 deletions

View File

@ -10240,7 +10240,8 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
/* optimization shortcut
* obj["literal"] = value -> opt_aset_with(obj, "literal", value)
*/
if (mid == idASET && !private_recv_p(node) && RNODE_ATTRASGN(node)->nd_args &&
if (!ISEQ_COMPILE_DATA(iseq)->in_masgn &&
mid == idASET && !private_recv_p(node) && RNODE_ATTRASGN(node)->nd_args &&
nd_type_p(RNODE_ATTRASGN(node)->nd_args, NODE_LIST) && RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->as.nd_alen == 2 &&
(nd_type_p(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head, NODE_STR) || nd_type_p(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head, NODE_FILE)) &&
ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
@ -10793,7 +10794,10 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
case NODE_MASGN:{
bool prev_in_masgn = ISEQ_COMPILE_DATA(iseq)->in_masgn;
ISEQ_COMPILE_DATA(iseq)->in_masgn = true;
compile_massign(iseq, ret, node, popped);
ISEQ_COMPILE_DATA(iseq)->in_masgn = prev_in_masgn;
break;
}

1
iseq.h
View File

@ -119,6 +119,7 @@ struct iseq_compile_data {
struct iseq_compile_data_storage *storage_current;
} insn;
bool in_rescue;
bool in_masgn;
int loopval_popped; /* used by NODE_BREAK */
int last_line;
int label_no;

View File

@ -248,6 +248,16 @@ class TestAssignment < Test::Unit::TestCase
a,b,*c = *[*[1,2]]; assert_equal([1,2,[]], [a,b,c])
end
def test_massign_optimized_literal_bug_21012
a = []
def a.[]=(*args)
push args
end
a["a", "b"], = 1
a["a", 10], = 2
assert_equal [["a", "b", 1], ["a", 10, 2]], a
end
def test_assign_rescue
a = raise rescue 2; assert_equal(2, a)
a, b = raise rescue [3,4]; assert_equal([3, 4], [a, b])