[Bug #19021] Fix safe call w/ conditional assign

As of fbaac837cfba23a9d34dc7ee144d7940248222a2, when we were performing
a safe call (`o&.x=`) with a conditional assign (`||= 1`) and discarding
the result the stack would end up in a bad state due to a missing pop.

This commit fixes that by adjusting the target label of the branchnil to
be before a pop in that case (as was previously done in the
non-conditional assignment case).
This commit is contained in:
John Hawthorn 2022-09-25 19:54:49 -07:00
parent e3cc1a6cae
commit b361bdc200
Notes: git 2022-09-26 12:45:16 +09:00
2 changed files with 14 additions and 11 deletions

View File

@ -8728,10 +8728,6 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
}
ADD_LABEL(ret, lfin);
ADD_INSN(ret, node, pop);
if (lskip) {
ADD_LABEL(ret, lskip);
}
}
else {
CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
@ -8741,13 +8737,13 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
ADD_INSN1(ret, node, topn, INT2FIX(1));
}
ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
if (lskip && popped) {
ADD_LABEL(ret, lskip);
}
ADD_INSN(ret, node, pop);
if (lskip && !popped) {
ADD_LABEL(ret, lskip);
}
}
if (lskip && popped) {
ADD_LABEL(ret, lskip);
}
ADD_INSN(ret, node, pop);
if (lskip && !popped) {
ADD_LABEL(ret, lskip);
}
return COMPILE_OK;
}

View File

@ -47,12 +47,19 @@ class TestCall < Test::Unit::TestCase
assert_equal(5, o.y)
o&.z ||= 6
assert_equal(6, o.z)
o&.z &&= 7
assert_equal(7, o.z)
o = nil
assert_nil(o&.x)
assert_nothing_raised(NoMethodError) {o&.x = raise}
assert_nothing_raised(NoMethodError) {o&.x = raise; nil}
assert_nothing_raised(NoMethodError) {o&.x *= raise}
assert_nothing_raised(NoMethodError) {o&.x *= raise; nil}
assert_nothing_raised(NoMethodError) {o&.x ||= raise}
assert_nothing_raised(NoMethodError) {o&.x ||= raise; nil}
assert_nothing_raised(NoMethodError) {o&.x &&= raise}
assert_nothing_raised(NoMethodError) {o&.x &&= raise; nil}
end
def test_safe_call_evaluate_arguments_only_method_call_is_made