diff options
| author | Takashi Kokubun <takashikkbn@gmail.com> | 2026-05-11 14:01:20 -0700 |
|---|---|---|
| committer | Takashi Kokubun <takashikkbn@gmail.com> | 2026-05-11 14:01:20 -0700 |
| commit | a9e4608b5225d665ec9c9353a8350f5b940c20fe (patch) | |
| tree | f54624ed2259f0947b4a1fbe3cf6725fab8fae92 | |
| parent | e52562edc0c26a905bec575215f6ac4e7fcdd59f (diff) | |
merge revision(s) d077df24a2256d760cc534b242b28822d4ef6376: [Backport #22002]
[Bug #22002] Never pop when compiling branch predicate
The value is always needed. Now prism emits the same instructions as parse.y for the added test case.
`defined?`/`flip-flop` caused `argument stack underflow`, `and`/`or` segfaulted during runtime.
| -rw-r--r-- | prism_compile.c | 29 | ||||
| -rw-r--r-- | test/ruby/test_syntax.rb | 18 |
2 files changed, 31 insertions, 16 deletions
diff --git a/prism_compile.c b/prism_compile.c index 34212b0228..55419d916d 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -871,10 +871,10 @@ pm_code_location(const pm_scope_node_t *scope_node, const pm_node_t *node) static void pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond, - LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node); + LABEL *then_label, LABEL *else_label, pm_scope_node_t *scope_node); static void -pm_compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_node_t *cond, LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node) +pm_compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_node_t *cond, LABEL *then_label, LABEL *else_label, pm_scope_node_t *scope_node) { const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, cond); @@ -884,17 +884,14 @@ pm_compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_node_t *cond, LAB if (!then_label) then_label = label; else if (!else_label) else_label = label; - pm_compile_branch_condition(iseq, seq, cond, then_label, else_label, popped, scope_node); + pm_compile_branch_condition(iseq, seq, cond, then_label, else_label, scope_node); if (LIST_INSN_SIZE_ONE(seq)) { INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(seq)); if (insn->insn_id == BIN(jump) && (LABEL *)(insn->operands[0]) == label) return; } - if (!label->refcnt) { - if (popped) PUSH_INSN(ret, location, putnil); - } - else { + if (label->refcnt) { PUSH_LABEL(seq, label); } @@ -966,7 +963,7 @@ pm_compile_flip_flop(const pm_flip_flop_node_t *flip_flop_node, LABEL *else_labe static void pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition); static void -pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond, LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node) +pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond, LABEL *then_label, LABEL *else_label, pm_scope_node_t *scope_node) { const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, cond); @@ -974,14 +971,14 @@ again: switch (PM_NODE_TYPE(cond)) { case PM_AND_NODE: { const pm_and_node_t *cast = (const pm_and_node_t *) cond; - pm_compile_logical(iseq, ret, cast->left, NULL, else_label, popped, scope_node); + pm_compile_logical(iseq, ret, cast->left, NULL, else_label, scope_node); cond = cast->right; goto again; } case PM_OR_NODE: { const pm_or_node_t *cast = (const pm_or_node_t *) cond; - pm_compile_logical(iseq, ret, cast->left, then_label, NULL, popped, scope_node); + pm_compile_logical(iseq, ret, cast->left, then_label, NULL, scope_node); cond = cast->right; goto again; @@ -1002,11 +999,11 @@ again: PUSH_INSNL(ret, location, jump, then_label); return; case PM_FLIP_FLOP_NODE: - pm_compile_flip_flop((const pm_flip_flop_node_t *) cond, else_label, then_label, iseq, location.line, ret, popped, scope_node); + pm_compile_flip_flop((const pm_flip_flop_node_t *) cond, else_label, then_label, iseq, location.line, ret, false, scope_node); return; case PM_DEFINED_NODE: { const pm_defined_node_t *cast = (const pm_defined_node_t *) cond; - pm_compile_defined_expr(iseq, cast->value, &location, ret, popped, scope_node, true); + pm_compile_defined_expr(iseq, cast->value, &location, ret, false, scope_node, true); break; } default: { @@ -1050,7 +1047,7 @@ pm_compile_conditional(rb_iseq_t *iseq, const pm_node_location_t *node_location, LABEL *end_label = NULL; DECL_ANCHOR(cond_seq); - pm_compile_branch_condition(iseq, cond_seq, predicate, then_label, else_label, false, scope_node); + pm_compile_branch_condition(iseq, cond_seq, predicate, then_label, else_label, scope_node); PUSH_SEQ(ret, cond_seq); rb_code_location_t conditional_location = { 0 }; @@ -1198,10 +1195,10 @@ pm_compile_loop(rb_iseq_t *iseq, const pm_node_location_t *node_location, pm_nod PUSH_LABEL(ret, next_label); if (type == PM_WHILE_NODE) { - pm_compile_branch_condition(iseq, ret, predicate, redo_label, end_label, popped, scope_node); + pm_compile_branch_condition(iseq, ret, predicate, redo_label, end_label, scope_node); } else if (type == PM_UNTIL_NODE) { - pm_compile_branch_condition(iseq, ret, predicate, end_label, redo_label, popped, scope_node); + pm_compile_branch_condition(iseq, ret, predicate, end_label, redo_label, scope_node); } PUSH_LABEL(ret, end_label); @@ -7597,7 +7594,7 @@ pm_compile_case_node(rb_iseq_t *iseq, const pm_case_node_t *cast, const pm_node_ } else { LABEL *next_label = NEW_LABEL(pm_node_line_number(parser, condition)); - pm_compile_branch_condition(iseq, cond_seq, condition, label, next_label, false, scope_node); + pm_compile_branch_condition(iseq, cond_seq, condition, label, next_label, scope_node); PUSH_LABEL(cond_seq, next_label); } } diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index 94a2e03940..1dfeecacf3 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -1862,6 +1862,24 @@ eom assert_valid_syntax('while class Foo a = tap do end; end; break; end') end + def test_while_until_conditional_bug_22002 + @foo = 123 until defined?(@foo) + assert_equal(123, @foo) + + @bar = 456 while @bar==nil..true + assert_equal(456, @bar) + + while false and @baz + @baz = 789 + end + assert_equal(nil, @baz) + + until true || @baz + @baz = 789 + end + assert_equal(nil, @baz) + end + def test_command_with_cmd_brace_block assert_valid_syntax('obj.foo (1) {}') assert_valid_syntax('obj::foo (1) {}') |
