From 4921845b61c6a1b539dd8a65fb265024b8e40523 Mon Sep 17 00:00:00 2001 From: Nick Dower Date: Fri, 16 May 2025 05:18:28 +0200 Subject: [PATCH] [Bug #21313] Handle `it` in rescue and ensure blocks. The following is crashing for me: ```shell ruby --yjit --yjit-call-threshold=1 -e '1.tap { raise rescue p it }' ruby: YJIT has panicked. More info to follow... thread '' panicked at ./yjit/src/codegen.rs:2402:14: ... ``` It seems `it` sometimes points to the wrong value: ```shell ruby -e '1.tap { raise rescue p it }' false ruby -e '1.tap { begin; raise; ensure; p it; end } rescue nil' false ``` But only when `$!` is set: ```shell ruby -e '1.tap { begin; nil; ensure; p it; end }' 1 ruby -e '1.tap { begin; nil; rescue; ensure; p it; end }' 1 ruby -e '1.tap { begin; raise; rescue; ensure; p it; end }' 1 ``` --- prism_compile.c | 14 +++++++++++++- test/ruby/test_syntax.rb | 8 ++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/prism_compile.c b/prism_compile.c index e655688401..63893c5184 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -9640,7 +9640,19 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, // -> { it } // ^^ if (!popped) { - PUSH_GETLOCAL(ret, location, scope_node->local_table_for_iseq_size, 0); + pm_scope_node_t *current_scope_node = scope_node; + int level = 0; + + while (current_scope_node) { + if (current_scope_node->parameters && PM_NODE_TYPE_P(current_scope_node->parameters, PM_IT_PARAMETERS_NODE)) { + PUSH_GETLOCAL(ret, location, current_scope_node->local_table_for_iseq_size, level); + return; + } + + current_scope_node = current_scope_node->previous; + level++; + } + rb_bug("Local `it` does not exist"); } return; diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index 62f1d99bdc..f24929092b 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -1946,6 +1946,14 @@ eom end assert_valid_syntax('proc {def foo(_);end;it}') assert_syntax_error('p { [it **2] }', /unexpected \*\*/) + assert_equal(1, eval('1.then { raise rescue it }')) + assert_equal(2, eval('1.then { 2.then { raise rescue it } }')) + assert_equal(3, eval('3.then { begin; raise; rescue; it; end }')) + assert_equal(4, eval('4.tap { begin; raise ; rescue; raise rescue it; end; }')) + assert_equal(5, eval('a = 0; 5.then { begin; nil; ensure; a = it; end }; a')) + assert_equal(6, eval('a = 0; 6.then { begin; nil; rescue; ensure; a = it; end }; a')) + assert_equal(7, eval('a = 0; 7.then { begin; raise; ensure; a = it; end } rescue a')) + assert_equal(8, eval('a = 0; 8.then { begin; raise; rescue; ensure; a = it; end }; a')) end def test_value_expr_in_condition