@@ -1898,31 +1898,37 @@ fn can_direct_send(function: &mut Function, block: BlockId, iseq: *const rb_iseq
18981898 return false ;
18991899 }
19001900
1901- // Because we exclude e.g. post parameters above, they are also excluded from the sum below.
1901+ // Because we exclude e.g. post parameters above, they are also excluded from the checks below.
19021902 let lead_num = params. lead_num ;
19031903 let opt_num = params. opt_num ;
19041904 let keyword = params. keyword ;
19051905 let kw_req_num = if keyword. is_null ( ) { 0 } else { unsafe { ( * keyword) . required_num } } ;
19061906 let kw_total_num = if keyword. is_null ( ) { 0 } else { unsafe { ( * keyword) . num } } ;
1907- // Minimum args: all required positional + all required keywords
1908- let min_argc = lead_num + kw_req_num;
1909- // Maximum args: all positional (required + optional) + all keywords (required + optional)
1910- let max_argc = lead_num + opt_num + kw_total_num;
1907+ let kwarg = unsafe { rb_vm_ci_kwarg ( ci) } ;
1908+ let caller_kw_count = if kwarg. is_null ( ) { 0 } else { ( unsafe { get_cikw_keyword_len ( kwarg) } ) as usize } ;
1909+ let caller_positional = match args. len ( ) . checked_sub ( caller_kw_count) {
1910+ Some ( count) => count,
1911+ None => {
1912+ function. set_dynamic_send_reason ( send_insn, ArgcParamMismatch ) ;
1913+ return false ;
1914+ }
1915+ } ;
19111916
1912- can_send = c_int:: try_from ( args . len ( ) )
1917+ let positional_ok = c_int:: try_from ( caller_positional )
19131918 . as_ref ( )
1914- . map ( |argc| ( min_argc ..=max_argc ) . contains ( argc) )
1919+ . map ( |argc| ( lead_num ..=lead_num + opt_num ) . contains ( argc) )
19151920 . unwrap_or ( false ) ;
1916- if !can_send {
1921+ let keyword_ok = c_int:: try_from ( caller_kw_count)
1922+ . as_ref ( )
1923+ . map ( |argc| ( kw_req_num..=kw_total_num) . contains ( argc) )
1924+ . unwrap_or ( false ) ;
1925+ if !positional_ok || !keyword_ok {
19171926 function. set_dynamic_send_reason ( send_insn, ArgcParamMismatch ) ;
19181927 return false
19191928 }
19201929
19211930 // asm.ccall() doesn't support 6+ args. Compute the final argc after keyword setup:
19221931 // final_argc = caller's positional args + callee's total keywords (all kw slots are filled).
1923- let kwarg = unsafe { rb_vm_ci_kwarg ( ci) } ;
1924- let caller_kw_count = if kwarg. is_null ( ) { 0 } else { ( unsafe { get_cikw_keyword_len ( kwarg) } ) as usize } ;
1925- let caller_positional = args. len ( ) - caller_kw_count;
19261932 // Right now, the JIT entrypoint accepts the block as an param
19271933 // We may remove it, remove the block_arg addition to match
19281934 // See: https://github.com/ruby/ruby/pull/15911#discussion_r2710544982
@@ -1933,15 +1939,6 @@ fn can_direct_send(function: &mut Function, block: BlockId, iseq: *const rb_iseq
19331939 return false ;
19341940 }
19351941
1936- // Caller passing a hash as a positional arg but callee expects keyword-only args will
1937- // raise ArgumentError at runtime. Fall back to VM dispatch to handle this correctly.
1938- if kwarg. is_null ( ) && !keyword. is_null ( ) {
1939- if lead_num == 0 && opt_num == 0 && caller_positional > 0 {
1940- function. set_dynamic_send_reason ( send_insn, ArgcParamMismatch ) ;
1941- return false ;
1942- }
1943- }
1944-
19451942 can_send
19461943}
19471944
0 commit comments