diff options
| author | Alan Wu <XrXr@users.noreply.github.com> | 2023-07-04 11:07:18 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-07-04 15:07:18 +0000 |
| commit | 2f603bc4d750384d57513679ca9db2b4e2e3ec34 (patch) | |
| tree | 2e69f8469a052049361d8d8977c2de8db36f9ae6 | |
| parent | 07d5709feea0b4d4fe23e2f346383791c614f073 (diff) | |
ruby_3_2 backport for #8006 (#8008)
YJIT: Fix autosplat miscomp for blocks with optionals
When passing an array as the sole argument to `yield`, and the yieldee
takes more than 1 optional parameter, the array is expanded similar
to `*array` splat calls. This is called "autosplat" in
`setup_parameters_complex()`.
Previously, YJIT did not detect this autosplat condition. It passed the
array without expanding it, deviating from interpreter behavior.
Detect this conditon and refuse to compile it.
| -rw-r--r-- | bootstraptest/test_yjit.rb | 10 | ||||
| -rw-r--r-- | yjit/src/codegen.rs | 4 |
2 files changed, 13 insertions, 1 deletions
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 9d9143d265..29efba24b3 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -1,3 +1,13 @@ +# Regression test for yielding with autosplat to block with +# optional parameters. https://github.com/Shopify/yjit/issues/313 +assert_equal '[:a, :b, :a, :b]', %q{ + def yielder(arg) = yield(arg) + yield(arg) + + yielder([:a, :b]) do |c = :c, d = :d| + [c, d] + end +} + assert_equal 'true', %q{ # regression test for tracking type of locals for too long def local_setting_cmp(five) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 687205e10f..84748d8adf 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -6117,7 +6117,9 @@ fn gen_invokeblock( // Not supporting vm_callee_setup_block_arg_arg0_splat for now let comptime_captured = unsafe { ((comptime_handler.0 & !0x3) as *const rb_captured_block).as_ref().unwrap() }; let comptime_iseq = unsafe { *comptime_captured.code.iseq.as_ref() }; - if argc == 1 && unsafe { get_iseq_flags_has_lead(comptime_iseq) && !get_iseq_flags_ambiguous_param0(comptime_iseq) } { + if argc == 1 && unsafe { + (get_iseq_flags_has_lead(comptime_iseq) || get_iseq_body_param_opt_num(comptime_iseq) > 1) && + !get_iseq_flags_ambiguous_param0(comptime_iseq) } { gen_counter_incr!(asm, invokeblock_iseq_arg0_splat); return CantCompile; } |
