1use core::ops::ControlFlow;
2use std::borrow::Cow;
3
4use rustc_ast::visit::Visitor;
5use rustc_ast::*;
6use rustc_data_structures::fx::FxIndexMap;
7use rustc_hir as hir;
8use rustc_session::config::FmtDebug;
9use rustc_span::{Ident, Span, Symbol, kw, sym};
10
11use super::LoweringContext;
12
13impl<'hir> LoweringContext<'_, 'hir> {
14 pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> {
15 let allow_const = fmt.arguments.all_args().is_empty();
18 let mut fmt = Cow::Borrowed(fmt);
19 if self.tcx.sess.opts.unstable_opts.flatten_format_args {
20 fmt = flatten_format_args(fmt);
21 fmt = self.inline_literals(fmt);
22 }
23 expand_format_args(self, sp, &fmt, allow_const)
24 }
25
26 fn try_inline_lit(&self, lit: token::Lit) -> Option<Symbol> {
28 match LitKind::from_token_lit(lit) {
29 Ok(LitKind::Str(s, _)) => Some(s),
30 Ok(LitKind::Int(n, ty)) => {
31 match ty {
32 LitIntType::Unsuffixed => {
34 (n <= i32::MAX as u128).then_some(Symbol::intern(&n.to_string()))
35 }
36 LitIntType::Signed(int_ty) => {
37 let max_literal = self.int_ty_max(int_ty);
38 (n <= max_literal).then_some(Symbol::intern(&n.to_string()))
39 }
40 LitIntType::Unsigned(uint_ty) => {
41 let max_literal = self.uint_ty_max(uint_ty);
42 (n <= max_literal).then_some(Symbol::intern(&n.to_string()))
43 }
44 }
45 }
46 _ => None,
47 }
48 }
49
50 fn int_ty_max(&self, int_ty: IntTy) -> u128 {
52 match int_ty {
53 IntTy::Isize => self.tcx.data_layout.pointer_size.signed_int_max() as u128,
54 IntTy::I8 => i8::MAX as u128,
55 IntTy::I16 => i16::MAX as u128,
56 IntTy::I32 => i32::MAX as u128,
57 IntTy::I64 => i64::MAX as u128,
58 IntTy::I128 => i128::MAX as u128,
59 }
60 }
61
62 fn uint_ty_max(&self, uint_ty: UintTy) -> u128 {
64 match uint_ty {
65 UintTy::Usize => self.tcx.data_layout.pointer_size.unsigned_int_max(),
66 UintTy::U8 => u8::MAX as u128,
67 UintTy::U16 => u16::MAX as u128,
68 UintTy::U32 => u32::MAX as u128,
69 UintTy::U64 => u64::MAX as u128,
70 UintTy::U128 => u128::MAX as u128,
71 }
72 }
73
74 fn inline_literals<'fmt>(&self, mut fmt: Cow<'fmt, FormatArgs>) -> Cow<'fmt, FormatArgs> {
84 let mut was_inlined = vec![false; fmt.arguments.all_args().len()];
85 let mut inlined_anything = false;
86
87 for i in 0..fmt.template.len() {
88 let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] else { continue };
89 let Ok(arg_index) = placeholder.argument.index else { continue };
90
91 let mut literal = None;
92
93 if let FormatTrait::Display = placeholder.format_trait
94 && placeholder.format_options == Default::default()
95 && let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs()
96 && let ExprKind::Lit(lit) = arg.kind
97 {
98 literal = self.try_inline_lit(lit);
99 }
100
101 if let Some(literal) = literal {
102 let fmt = fmt.to_mut();
105 fmt.template[i] = FormatArgsPiece::Literal(literal);
107 was_inlined[arg_index] = true;
108 inlined_anything = true;
109 }
110 }
111
112 if inlined_anything {
114 let fmt = fmt.to_mut();
115
116 let mut remove = was_inlined;
117
118 for_all_argument_indexes(&mut fmt.template, |index| remove[*index] = false);
120
121 let mut remove_it = remove.iter();
123 fmt.arguments.all_args_mut().retain(|_| remove_it.next() != Some(&true));
124
125 let index_map: Vec<usize> = remove
127 .into_iter()
128 .scan(0, |i, remove| {
129 let mapped = *i;
130 *i += !remove as usize;
131 Some(mapped)
132 })
133 .collect();
134
135 for_all_argument_indexes(&mut fmt.template, |index| *index = index_map[*index]);
137 }
138
139 fmt
140 }
141}
142
143fn flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
153 let mut i = 0;
154 while i < fmt.template.len() {
155 if let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i]
156 && let FormatTrait::Display | FormatTrait::Debug = &placeholder.format_trait
157 && let Ok(arg_index) = placeholder.argument.index
158 && let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs()
159 && let ExprKind::FormatArgs(_) = &arg.kind
160 && fmt.template.iter().enumerate().all(|(j, p)|
162 i == j ||
163 !matches!(p, FormatArgsPiece::Placeholder(placeholder)
164 if placeholder.argument.index == Ok(arg_index))
165 )
166 {
167 let fmt = fmt.to_mut();
170
171 let args = fmt.arguments.all_args_mut();
176 let remaining_args = args.split_off(arg_index + 1);
177 let old_arg_offset = args.len();
178 let mut fmt2 = &mut args.pop().unwrap().expr; let fmt2 = loop {
180 match &mut fmt2.kind {
182 ExprKind::Paren(inner) | ExprKind::AddrOf(BorrowKind::Ref, _, inner) => {
183 fmt2 = inner
184 }
185 ExprKind::FormatArgs(fmt2) => break fmt2,
186 _ => unreachable!(),
187 }
188 };
189
190 args.append(fmt2.arguments.all_args_mut());
191 let new_arg_offset = args.len();
192 args.extend(remaining_args);
193
194 for_all_argument_indexes(&mut fmt.template, |index| {
196 if *index >= old_arg_offset {
197 *index -= old_arg_offset;
198 *index += new_arg_offset;
199 }
200 });
201
202 let rest = fmt.template.split_off(i + 1);
205 fmt.template.pop(); for_all_argument_indexes(&mut fmt2.template, |index| *index += arg_index);
209 fmt.template.append(&mut fmt2.template);
210 fmt.template.extend(rest);
211
212 } else {
214 i += 1;
215 }
216 }
217 fmt
218}
219
220#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
221enum ArgumentType {
222 Format(FormatTrait),
223 Usize,
224}
225
226fn make_argument<'hir>(
234 ctx: &mut LoweringContext<'_, 'hir>,
235 sp: Span,
236 arg: &'hir hir::Expr<'hir>,
237 ty: ArgumentType,
238) -> hir::Expr<'hir> {
239 use ArgumentType::*;
240 use FormatTrait::*;
241 let new_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
242 sp,
243 hir::LangItem::FormatArgument,
244 match ty {
245 Format(Display) => sym::new_display,
246 Format(Debug) => match ctx.tcx.sess.opts.unstable_opts.fmt_debug {
247 FmtDebug::Full | FmtDebug::Shallow => sym::new_debug,
248 FmtDebug::None => sym::new_debug_noop,
249 },
250 Format(LowerExp) => sym::new_lower_exp,
251 Format(UpperExp) => sym::new_upper_exp,
252 Format(Octal) => sym::new_octal,
253 Format(Pointer) => sym::new_pointer,
254 Format(Binary) => sym::new_binary,
255 Format(LowerHex) => sym::new_lower_hex,
256 Format(UpperHex) => sym::new_upper_hex,
257 Usize => sym::from_usize,
258 },
259 ));
260 ctx.expr_call_mut(sp, new_fn, std::slice::from_ref(arg))
261}
262
263fn make_count<'hir>(
283 ctx: &mut LoweringContext<'_, 'hir>,
284 sp: Span,
285 count: &Option<FormatCount>,
286 argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>,
287) -> hir::Expr<'hir> {
288 match count {
289 Some(FormatCount::Literal(n)) => {
290 let count_is = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
291 sp,
292 hir::LangItem::FormatCount,
293 sym::Is,
294 ));
295 let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, *n)]);
296 ctx.expr_call_mut(sp, count_is, value)
297 }
298 Some(FormatCount::Argument(arg)) => {
299 if let Ok(arg_index) = arg.index {
300 let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize), arg.span);
301 let count_param = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
302 sp,
303 hir::LangItem::FormatCount,
304 sym::Param,
305 ));
306 let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, i)]);
307 ctx.expr_call_mut(sp, count_param, value)
308 } else {
309 ctx.expr(
310 sp,
311 hir::ExprKind::Err(
312 ctx.dcx().span_delayed_bug(sp, "lowered bad format_args count"),
313 ),
314 )
315 }
316 }
317 None => ctx.expr_lang_item_type_relative(sp, hir::LangItem::FormatCount, sym::Implied),
318 }
319}
320
321fn make_format_spec<'hir>(
336 ctx: &mut LoweringContext<'_, 'hir>,
337 sp: Span,
338 placeholder: &FormatPlaceholder,
339 argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>,
340) -> hir::Expr<'hir> {
341 let position = match placeholder.argument.index {
342 Ok(arg_index) => {
343 let (i, _) = argmap.insert_full(
344 (arg_index, ArgumentType::Format(placeholder.format_trait)),
345 placeholder.span,
346 );
347 ctx.expr_usize(sp, i)
348 }
349 Err(_) => ctx.expr(
350 sp,
351 hir::ExprKind::Err(ctx.dcx().span_delayed_bug(sp, "lowered bad format_args count")),
352 ),
353 };
354 let &FormatOptions {
355 ref width,
356 ref precision,
357 alignment,
358 fill,
359 sign,
360 alternate,
361 zero_pad,
362 debug_hex,
363 } = &placeholder.format_options;
364 let fill = ctx.expr_char(sp, fill.unwrap_or(' '));
365 let align = ctx.expr_lang_item_type_relative(
366 sp,
367 hir::LangItem::FormatAlignment,
368 match alignment {
369 Some(FormatAlignment::Left) => sym::Left,
370 Some(FormatAlignment::Right) => sym::Right,
371 Some(FormatAlignment::Center) => sym::Center,
372 None => sym::Unknown,
373 },
374 );
375 let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)
377 | ((sign == Some(FormatSign::Minus)) as u32) << 1
378 | (alternate as u32) << 2
379 | (zero_pad as u32) << 3
380 | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4
381 | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5;
382 let flags = ctx.expr_u32(sp, flags);
383 let precision = make_count(ctx, sp, precision, argmap);
384 let width = make_count(ctx, sp, width, argmap);
385 let format_placeholder_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
386 sp,
387 hir::LangItem::FormatPlaceholder,
388 sym::new,
389 ));
390 let args = ctx.arena.alloc_from_iter([position, fill, align, flags, precision, width]);
391 ctx.expr_call_mut(sp, format_placeholder_new, args)
392}
393
394fn expand_format_args<'hir>(
395 ctx: &mut LoweringContext<'_, 'hir>,
396 macsp: Span,
397 fmt: &FormatArgs,
398 allow_const: bool,
399) -> hir::ExprKind<'hir> {
400 let mut incomplete_lit = String::new();
401 let lit_pieces =
402 ctx.arena.alloc_from_iter(fmt.template.iter().enumerate().filter_map(|(i, piece)| {
403 match piece {
404 &FormatArgsPiece::Literal(s) => {
405 if let Some(FormatArgsPiece::Literal(_)) = fmt.template.get(i + 1) {
407 incomplete_lit.push_str(s.as_str());
408 None
409 } else if !incomplete_lit.is_empty() {
410 incomplete_lit.push_str(s.as_str());
411 let s = Symbol::intern(&incomplete_lit);
412 incomplete_lit.clear();
413 Some(ctx.expr_str(fmt.span, s))
414 } else {
415 Some(ctx.expr_str(fmt.span, s))
416 }
417 }
418 &FormatArgsPiece::Placeholder(_) => {
419 if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) {
421 Some(ctx.expr_str(fmt.span, kw::Empty))
422 } else {
423 None
424 }
425 }
426 }
427 }));
428 let lit_pieces = ctx.expr_array_ref(fmt.span, lit_pieces);
429
430 let mut use_format_options = false;
433
434 let mut argmap = FxIndexMap::default();
437 for piece in &fmt.template {
438 let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
439 if placeholder.format_options != Default::default() {
440 use_format_options = true;
442 }
443 if let Ok(index) = placeholder.argument.index {
444 if argmap
445 .insert((index, ArgumentType::Format(placeholder.format_trait)), placeholder.span)
446 .is_some()
447 {
448 use_format_options = true;
451 }
452 }
453 }
454
455 let format_options = use_format_options.then(|| {
456 let elements = ctx.arena.alloc_from_iter(fmt.template.iter().filter_map(|piece| {
459 let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
460 Some(make_format_spec(ctx, macsp, placeholder, &mut argmap))
461 }));
462 ctx.expr_array_ref(macsp, elements)
463 });
464
465 let arguments = fmt.arguments.all_args();
466
467 if allow_const && arguments.is_empty() && argmap.is_empty() {
468 let new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
471 macsp,
472 hir::LangItem::FormatArguments,
473 sym::new_const,
474 ));
475 let new_args = ctx.arena.alloc_from_iter([lit_pieces]);
476 return hir::ExprKind::Call(new, new_args);
477 }
478
479 let use_simple_array = argmap.len() == arguments.len()
487 && argmap.iter().enumerate().all(|(i, (&(j, _), _))| i == j)
488 && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
489
490 let args = if arguments.is_empty() {
491 let none_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
507 macsp,
508 hir::LangItem::FormatArgument,
509 sym::none,
510 ));
511 let none = ctx.expr_call(macsp, none_fn, &[]);
512 ctx.expr(macsp, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, none))
513 } else if use_simple_array {
514 let elements = ctx.arena.alloc_from_iter(arguments.iter().zip(argmap).map(
522 |(arg, ((_, ty), placeholder_span))| {
523 let placeholder_span =
524 placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
525 let arg_span = match arg.kind {
526 FormatArgumentKind::Captured(_) => placeholder_span,
527 _ => arg.expr.span.with_ctxt(macsp.ctxt()),
528 };
529 let arg = ctx.lower_expr(&arg.expr);
530 let ref_arg = ctx.arena.alloc(ctx.expr(
531 arg_span,
532 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
533 ));
534 make_argument(ctx, placeholder_span, ref_arg, ty)
535 },
536 ));
537 ctx.expr_array_ref(macsp, elements)
538 } else {
539 let args_ident = Ident::new(sym::args, macsp);
549 let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
550 let args = ctx.arena.alloc_from_iter(argmap.iter().map(
551 |(&(arg_index, ty), &placeholder_span)| {
552 let arg = &arguments[arg_index];
553 let placeholder_span =
554 placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
555 let arg_span = match arg.kind {
556 FormatArgumentKind::Captured(_) => placeholder_span,
557 _ => arg.expr.span.with_ctxt(macsp.ctxt()),
558 };
559 let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);
560 let arg = ctx.arena.alloc(ctx.expr(
561 arg_span,
562 hir::ExprKind::Field(
563 args_ident_expr,
564 Ident::new(sym::integer(arg_index), macsp),
565 ),
566 ));
567 make_argument(ctx, placeholder_span, arg, ty)
568 },
569 ));
570 let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
571 let arg_expr = ctx.lower_expr(&arg.expr);
572 ctx.expr(
573 arg.expr.span.with_ctxt(macsp.ctxt()),
574 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
575 )
576 }));
577 let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
578 let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
579 let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]);
580 let match_expr = ctx.arena.alloc(ctx.expr_match(
581 macsp,
582 args_tuple,
583 match_arms,
584 hir::MatchSource::FormatArgs,
585 ));
586 ctx.expr(
587 macsp,
588 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, match_expr),
589 )
590 };
591
592 if let Some(format_options) = format_options {
593 let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
601 macsp,
602 hir::LangItem::FormatArguments,
603 sym::new_v1_formatted,
604 ));
605 let unsafe_arg_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
606 macsp,
607 hir::LangItem::FormatUnsafeArg,
608 sym::new,
609 ));
610 let unsafe_arg_new_call = ctx.expr_call(macsp, unsafe_arg_new, &[]);
611 let hir_id = ctx.next_id();
612 let unsafe_arg = ctx.expr_block(ctx.arena.alloc(hir::Block {
613 stmts: &[],
614 expr: Some(unsafe_arg_new_call),
615 hir_id,
616 rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
617 span: macsp,
618 targeted_by_break: false,
619 }));
620 let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options, unsafe_arg]);
621 hir::ExprKind::Call(new_v1_formatted, args)
622 } else {
623 let new_v1 = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
629 macsp,
630 hir::LangItem::FormatArguments,
631 sym::new_v1,
632 ));
633 let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
634 hir::ExprKind::Call(new_v1, new_args)
635 }
636}
637
638fn may_contain_yield_point(e: &ast::Expr) -> bool {
639 struct MayContainYieldPoint;
640
641 impl Visitor<'_> for MayContainYieldPoint {
642 type Result = ControlFlow<()>;
643
644 fn visit_expr(&mut self, e: &ast::Expr) -> ControlFlow<()> {
645 if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_) = e.kind {
646 ControlFlow::Break(())
647 } else {
648 visit::walk_expr(self, e)
649 }
650 }
651
652 fn visit_mac_call(&mut self, _: &ast::MacCall) -> ControlFlow<()> {
653 unreachable!("unexpanded macro in ast lowering");
655 }
656
657 fn visit_item(&mut self, _: &ast::Item) -> ControlFlow<()> {
658 ControlFlow::Continue(())
660 }
661 }
662
663 MayContainYieldPoint.visit_expr(e).is_break()
664}
665
666fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) {
667 for piece in template {
668 let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
669 if let Ok(index) = &mut placeholder.argument.index {
670 f(index);
671 }
672 if let Some(FormatCount::Argument(FormatArgPosition { index: Ok(index), .. })) =
673 &mut placeholder.format_options.width
674 {
675 f(index);
676 }
677 if let Some(FormatCount::Argument(FormatArgPosition { index: Ok(index), .. })) =
678 &mut placeholder.format_options.precision
679 {
680 f(index);
681 }
682 }
683}