@@ -276,130 +276,168 @@ impl<'tcx> DecodedFormatArgs<'tcx> {
276276 insts. reverse ( ) ;
277277 Some ( insts)
278278 } ;
279- let fmt_args_new_call_insts = try_rev_take ( 3 ) . ok_or_else ( || {
280- FormatArgsNotRecognized ( "fmt::Arguments::new call: ran out of instructions" . into ( ) )
281- } ) ?;
279+ // Newer rustc can pass the `fmt::Arguments::new_*` result directly to
280+ // panic entry points (single trailing call), while older versions go
281+ // through a local temporary (`Call -> Store -> Load`).
282+ let fmt_args_new_call_inst_count = if matches ! (
283+ try_rev_take( -3 ) . as_deref( ) ,
284+ Some ( & [ Inst :: Call ( _, _, _) , Inst :: Store ( _, _) , Inst :: Load ( _, _) ] )
285+ ) {
286+ 3
287+ } else if matches ! (
288+ try_rev_take( -1 ) . as_deref( ) ,
289+ Some ( & [ Inst :: Call ( call_ret_id, _, _) ] ) if call_ret_id == format_args_id
290+ ) {
291+ 1
292+ } else {
293+ // HACK(eddyb) gather context for new call patterns before bailing.
294+ let mut insts = SmallVec :: < [ Inst < Word > ; 32 ] > :: new ( ) ;
295+ while let Some ( extra_inst) = try_rev_take ( 1 ) {
296+ insts. extend ( extra_inst) ;
297+ if insts. len ( ) >= 32 {
298+ break ;
299+ }
300+ }
301+ insts. reverse ( ) ;
302+
303+ return Err ( if insts. is_empty ( ) {
304+ FormatArgsNotRecognized ( "fmt::Arguments::new call: ran out of instructions" . into ( ) )
305+ } else {
306+ FormatArgsNotRecognized ( format ! ( "fmt::Arguments::new call sequence ({insts:?})" , ) )
307+ } ) ;
308+ } ;
309+ let fmt_args_new_call_insts = try_rev_take ( fmt_args_new_call_inst_count) . unwrap ( ) ;
310+ let ( call_args, pieces_len, rt_args_count) = match fmt_args_new_call_insts[ ..] {
311+ [
312+ Inst :: Call ( call_ret_id, callee_id, ref call_args) ,
313+ Inst :: Store ( st_dst_id, st_val_id) ,
314+ Inst :: Load ( ld_val_id, ld_src_id) ,
315+ ] if call_ret_id == st_val_id
316+ && st_dst_id == ld_src_id
317+ && ld_val_id == format_args_id =>
318+ {
319+ require_local_var ( st_dst_id, "fmt::Arguments::new destination" ) ?;
320+
321+ let Some ( & ( pieces_len, rt_args_count) ) =
322+ cx. fmt_args_new_fn_ids . borrow ( ) . get ( & callee_id)
323+ else {
324+ return Err ( FormatArgsNotRecognized (
325+ "fmt::Arguments::new callee not registered" . into ( ) ,
326+ ) ) ;
327+ } ;
328+
329+ ( call_args, pieces_len, rt_args_count)
330+ }
331+ [ Inst :: Call ( call_ret_id, callee_id, ref call_args) ]
332+ if call_ret_id == format_args_id =>
333+ {
334+ let Some ( & ( pieces_len, rt_args_count) ) =
335+ cx. fmt_args_new_fn_ids . borrow ( ) . get ( & callee_id)
336+ else {
337+ return Err ( FormatArgsNotRecognized (
338+ "fmt::Arguments::new callee not registered" . into ( ) ,
339+ ) ) ;
340+ } ;
341+
342+ ( call_args, pieces_len, rt_args_count)
343+ }
344+ _ => {
345+ // HACK(eddyb) this gathers more context before reporting.
346+ let mut insts = fmt_args_new_call_insts;
347+ insts. reverse ( ) ;
348+ while let Some ( extra_inst) = try_rev_take ( 1 ) {
349+ insts. extend ( extra_inst) ;
350+ if insts. len ( ) >= 32 {
351+ break ;
352+ }
353+ }
354+ insts. reverse ( ) ;
355+
356+ return Err ( FormatArgsNotRecognized ( format ! (
357+ "fmt::Arguments::new call sequence ({insts:?})" ,
358+ ) ) ) ;
359+ }
360+ } ;
282361 let ( ( pieces_slice_ptr_id, pieces_len) , ( rt_args_slice_ptr_id, rt_args_count) ) =
283- match fmt_args_new_call_insts[ ..] {
362+ match call_args[ ..] {
363+ // `<core::fmt::Arguments>::new_v1_formatted`
364+ //
365+ // HACK(eddyb) this isn't fully supported,
366+ // as that would require digging into unstable
367+ // internals of `core::fmt::rt::Placeholder`s,
368+ // but the whole call still needs to be removed,
369+ // and both const str pieces and runtime args
370+ // can still be printed (even if in jankier way).
284371 [
285- Inst :: Call ( call_ret_id, callee_id, ref call_args) ,
286- Inst :: Store ( st_dst_id, st_val_id) ,
287- Inst :: Load ( ld_val_id, ld_src_id) ,
288- ] if call_ret_id == st_val_id
289- && st_dst_id == ld_src_id
290- && ld_val_id == format_args_id =>
291- {
292- require_local_var ( st_dst_id, "fmt::Arguments::new destination" ) ?;
293-
294- let Some ( & ( pieces_len, rt_args_count) ) =
295- cx. fmt_args_new_fn_ids . borrow ( ) . get ( & callee_id)
296- else {
297- return Err ( FormatArgsNotRecognized (
298- "fmt::Arguments::new callee not registered" . into ( ) ,
299- ) ) ;
300- } ;
372+ pieces_slice_ptr_id,
373+ pieces_len_id,
374+ rt_args_slice_ptr_id,
375+ rt_args_len_id,
376+ fmt_placeholders_slice_ptr_id,
377+ fmt_placeholders_len_id,
378+ ] if ( pieces_len, rt_args_count) == ( !0 , !0 ) => {
379+ let [ pieces_len, rt_args_len, fmt_placeholders_len] =
380+ match [ pieces_len_id, rt_args_len_id, fmt_placeholders_len_id]
381+ . map ( const_u32_as_usize)
382+ {
383+ [ Some ( a) , Some ( b) , Some ( c) ] => [ a, b, c] ,
384+ _ => {
385+ return Err ( FormatArgsNotRecognized (
386+ "fmt::Arguments::new_v1_formatted \
387+ with dynamic lengths"
388+ . into ( ) ,
389+ ) ) ;
390+ }
391+ } ;
301392
302- match call_args[ ..] {
303- // `<core::fmt::Arguments>::new_v1_formatted`
304- //
305- // HACK(eddyb) this isn't fully supported,
306- // as that would require digging into unstable
307- // internals of `core::fmt::rt::Placeholder`s,
308- // but the whole call still needs to be removed,
309- // and both const str pieces and runtime args
310- // can still be printed (even if in jankier way).
311- [
312- pieces_slice_ptr_id,
313- pieces_len_id,
314- rt_args_slice_ptr_id,
315- rt_args_len_id,
316- fmt_placeholders_slice_ptr_id,
317- fmt_placeholders_len_id,
318- ] if ( pieces_len, rt_args_count) == ( !0 , !0 ) => {
319- let [ pieces_len, rt_args_len, fmt_placeholders_len] =
320- match [ pieces_len_id, rt_args_len_id, fmt_placeholders_len_id]
321- . map ( const_u32_as_usize)
322- {
323- [ Some ( a) , Some ( b) , Some ( c) ] => [ a, b, c] ,
324- _ => {
325- return Err ( FormatArgsNotRecognized (
326- "fmt::Arguments::new_v1_formatted \
327- with dynamic lengths"
328- . into ( ) ,
329- ) ) ;
330- }
331- } ;
332-
333- let prepare_args_insts = try_rev_take ( 2 ) . ok_or_else ( || {
334- FormatArgsNotRecognized (
335- "fmt::Arguments::new_v1_formatted call: ran out of instructions" . into ( ) ,
336- )
337- } ) ?;
338- let ( rt_args_slice_ptr_id, _fmt_placeholders_slice_ptr_id) =
339- match prepare_args_insts[ ..] {
340- [
341- Inst :: Bitcast ( rt_args_cast_out_id, rt_args_cast_in_id) ,
342- Inst :: Bitcast (
343- placeholders_cast_out_id,
344- placeholders_cast_in_id,
345- ) ,
346- ] if rt_args_cast_out_id == rt_args_slice_ptr_id
347- && placeholders_cast_out_id
348- == fmt_placeholders_slice_ptr_id =>
349- {
350- ( rt_args_cast_in_id, placeholders_cast_in_id)
351- }
352- _ => {
353- let mut insts = prepare_args_insts;
354- insts. extend ( fmt_args_new_call_insts) ;
355- return Err ( FormatArgsNotRecognized ( format ! (
356- "fmt::Arguments::new_v1_formatted call sequence ({insts:?})" ,
357- ) ) ) ;
358- }
359- } ;
360-
361- decoded_format_args. has_unknown_fmt_placeholder_to_args_mapping =
362- Some ( fmt_placeholders_len) ;
363-
364- (
365- ( pieces_slice_ptr_id, pieces_len) ,
366- ( Some ( rt_args_slice_ptr_id) , rt_args_len) ,
367- )
368- }
393+ let prepare_args_insts = try_rev_take ( 2 ) . ok_or_else ( || {
394+ FormatArgsNotRecognized (
395+ "fmt::Arguments::new_v1_formatted call: ran out of instructions" . into ( ) ,
396+ )
397+ } ) ?;
398+ let ( rt_args_slice_ptr_id, _fmt_placeholders_slice_ptr_id) =
399+ match prepare_args_insts[ ..] {
400+ [
401+ Inst :: Bitcast ( rt_args_cast_out_id, rt_args_cast_in_id) ,
402+ Inst :: Bitcast ( placeholders_cast_out_id, placeholders_cast_in_id) ,
403+ ] if rt_args_cast_out_id == rt_args_slice_ptr_id
404+ && placeholders_cast_out_id == fmt_placeholders_slice_ptr_id =>
405+ {
406+ ( rt_args_cast_in_id, placeholders_cast_in_id)
407+ }
408+ _ => {
409+ let mut insts = prepare_args_insts;
410+ insts. extend ( fmt_args_new_call_insts) ;
411+ return Err ( FormatArgsNotRecognized ( format ! (
412+ "fmt::Arguments::new_v1_formatted call sequence ({insts:?})" ,
413+ ) ) ) ;
414+ }
415+ } ;
369416
370- // `<core::fmt::Arguments>::new_v1`
371- [ pieces_slice_ptr_id, rt_args_slice_ptr_id] => (
372- ( pieces_slice_ptr_id, pieces_len) ,
373- ( Some ( rt_args_slice_ptr_id) , rt_args_count) ,
374- ) ,
417+ decoded_format_args. has_unknown_fmt_placeholder_to_args_mapping =
418+ Some ( fmt_placeholders_len) ;
375419
376- // `<core::fmt::Arguments>::new_const`
377- [ pieces_slice_ptr_id] if rt_args_count == 0 => {
378- ( ( pieces_slice_ptr_id, pieces_len) , ( None , rt_args_count) )
379- }
420+ (
421+ ( pieces_slice_ptr_id, pieces_len) ,
422+ ( Some ( rt_args_slice_ptr_id) , rt_args_len) ,
423+ )
424+ }
380425
381- _ => {
382- return Err ( FormatArgsNotRecognized (
383- "fmt::Arguments::new call args" . into ( ) ,
384- ) ) ;
385- }
386- }
426+ // `<core::fmt::Arguments>::new_v1`
427+ [ pieces_slice_ptr_id, rt_args_slice_ptr_id] => (
428+ ( pieces_slice_ptr_id, pieces_len) ,
429+ ( Some ( rt_args_slice_ptr_id) , rt_args_count) ,
430+ ) ,
431+
432+ // `<core::fmt::Arguments>::new_const`
433+ [ pieces_slice_ptr_id] if rt_args_count == 0 => {
434+ ( ( pieces_slice_ptr_id, pieces_len) , ( None , rt_args_count) )
387435 }
388- _ => {
389- // HACK(eddyb) this gathers more context before reporting.
390- let mut insts = fmt_args_new_call_insts;
391- insts. reverse ( ) ;
392- while let Some ( extra_inst) = try_rev_take ( 1 ) {
393- insts. extend ( extra_inst) ;
394- if insts. len ( ) >= 32 {
395- break ;
396- }
397- }
398- insts. reverse ( ) ;
399436
400- return Err ( FormatArgsNotRecognized ( format ! (
401- "fmt::Arguments::new call sequence ({insts:?})" ,
402- ) ) ) ;
437+ _ => {
438+ return Err ( FormatArgsNotRecognized (
439+ "fmt::Arguments::new call args" . into ( ) ,
440+ ) ) ;
403441 }
404442 } ;
405443
@@ -772,7 +810,9 @@ impl<'a, 'tcx> CodegenPanic<'a, 'tcx> for FormatArgsResult<'tcx> {
772810 match self {
773811 Ok ( e) => e. codegen_panic ( builder, result_type) ,
774812 Err ( FormatArgsNotRecognized ( step) ) => {
775- if let Some ( current_span) = builder. current_span {
813+ if let Some ( current_span) = builder. current_span
814+ && !builder. tcx . sess . opts . unstable_opts . ui_testing
815+ {
776816 // HACK(eddyb) Cargo silences warnings in dependencies.
777817 let force_warn = |span, msg| -> rustc_errors:: Diag < ' _ , ( ) > {
778818 rustc_errors:: Diag :: new (
0 commit comments