[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 '<unnamed>' 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
```
This commit is contained in:
Nick Dower 2025-05-16 05:18:28 +02:00 committed by Alan Wu
parent 097d742a1e
commit 4921845b61
Notes: git 2025-05-16 15:17:35 +00:00
2 changed files with 21 additions and 1 deletions

View File

@ -9640,7 +9640,19 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
// -> { it } // -> { it }
// ^^ // ^^
if (!popped) { 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; return;

View File

@ -1946,6 +1946,14 @@ eom
end end
assert_valid_syntax('proc {def foo(_);end;it}') assert_valid_syntax('proc {def foo(_);end;it}')
assert_syntax_error('p { [it **2] }', /unexpected \*\*/) 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 end
def test_value_expr_in_condition def test_value_expr_in_condition