1use std::borrow::Cow;
2
3use rustc_ast::*;
4use rustc_data_structures::fx::FxIndexMap;
5use rustc_hir as hir;
6use rustc_session::config::FmtDebug;
7use rustc_span::{ByteSymbol, DesugaringKind, Ident, Span, Symbol, sym};
8
9use super::LoweringContext;
10use crate::ResolverAstLoweringExt;
11
12impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
13 pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> {
14 let allow_const = fmt.arguments.all_args().is_empty();
17 let mut fmt = Cow::Borrowed(fmt);
18
19 let sp = self.mark_span_with_reason(
20 DesugaringKind::FormatLiteral { source: fmt.is_source_literal },
21 sp,
22 sp.ctxt().outer_expn_data().allow_internal_unstable,
23 );
24
25 if self.tcx.sess.opts.unstable_opts.flatten_format_args {
26 fmt = flatten_format_args(fmt);
27 fmt = self.inline_literals(fmt);
28 }
29 expand_format_args(self, sp, &fmt, allow_const)
30 }
31
32 fn try_inline_lit(&self, lit: token::Lit) -> Option<Symbol> {
34 match LitKind::from_token_lit(lit) {
35 Ok(LitKind::Str(s, _)) => Some(s),
36 Ok(LitKind::Int(n, ty)) => {
37 match ty {
38 LitIntType::Unsuffixed => {
40 (n <= i32::MAX as u128).then_some(Symbol::intern(&n.to_string()))
41 }
42 LitIntType::Signed(int_ty) => {
43 let max_literal = self.int_ty_max(int_ty);
44 (n <= max_literal).then_some(Symbol::intern(&n.to_string()))
45 }
46 LitIntType::Unsigned(uint_ty) => {
47 let max_literal = self.uint_ty_max(uint_ty);
48 (n <= max_literal).then_some(Symbol::intern(&n.to_string()))
49 }
50 }
51 }
52 _ => None,
53 }
54 }
55
56 fn int_ty_max(&self, int_ty: IntTy) -> u128 {
58 match int_ty {
59 IntTy::Isize => self.tcx.data_layout.pointer_size().signed_int_max() as u128,
60 IntTy::I8 => i8::MAX as u128,
61 IntTy::I16 => i16::MAX as u128,
62 IntTy::I32 => i32::MAX as u128,
63 IntTy::I64 => i64::MAX as u128,
64 IntTy::I128 => i128::MAX as u128,
65 }
66 }
67
68 fn uint_ty_max(&self, uint_ty: UintTy) -> u128 {
70 match uint_ty {
71 UintTy::Usize => self.tcx.data_layout.pointer_size().unsigned_int_max(),
72 UintTy::U8 => u8::MAX as u128,
73 UintTy::U16 => u16::MAX as u128,
74 UintTy::U32 => u32::MAX as u128,
75 UintTy::U64 => u64::MAX as u128,
76 UintTy::U128 => u128::MAX as u128,
77 }
78 }
79
80 fn inline_literals<'fmt>(&self, mut fmt: Cow<'fmt, FormatArgs>) -> Cow<'fmt, FormatArgs> {
90 let mut was_inlined = ::alloc::vec::from_elem(false, fmt.arguments.all_args().len())vec![false; fmt.arguments.all_args().len()];
91 let mut inlined_anything = false;
92
93 for i in 0..fmt.template.len() {
94 if let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i]
95 && let Ok(arg_index) = placeholder.argument.index
96 && let FormatTrait::Display = placeholder.format_trait
97 && placeholder.format_options == Default::default()
98 && let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs()
99 && let ExprKind::Lit(lit) = arg.kind
100 && let Some(literal) = self.try_inline_lit(lit)
101 {
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 !#[allow(non_exhaustive_omitted_patterns)] match p {
FormatArgsPiece::Placeholder(placeholder) if
placeholder.argument.index == Ok(arg_index) => true,
_ => false,
}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 _ => ::core::panicking::panic("internal error: entered unreachable code")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(#[automatically_derived]
impl ::core::marker::Copy for ArgumentType { }Copy, #[automatically_derived]
impl ::core::clone::Clone for ArgumentType {
#[inline]
fn clone(&self) -> ArgumentType {
let _: ::core::clone::AssertParamIsClone<FormatTrait>;
*self
}
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for ArgumentType {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
ArgumentType::Format(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Format",
&__self_0),
ArgumentType::Usize =>
::core::fmt::Formatter::write_str(f, "Usize"),
}
}
}Debug, #[automatically_derived]
impl ::core::hash::Hash for ArgumentType {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
let __self_discr = ::core::intrinsics::discriminant_value(self);
::core::hash::Hash::hash(&__self_discr, state);
match self {
ArgumentType::Format(__self_0) =>
::core::hash::Hash::hash(__self_0, state),
_ => {}
}
}
}Hash, #[automatically_derived]
impl ::core::cmp::PartialEq for ArgumentType {
#[inline]
fn eq(&self, other: &ArgumentType) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(ArgumentType::Format(__self_0),
ArgumentType::Format(__arg1_0)) => __self_0 == __arg1_0,
_ => true,
}
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for ArgumentType {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<FormatTrait>;
}
}Eq)]
221enum ArgumentType {
222 Format(FormatTrait),
223 Usize,
224}
225
226fn make_argument<'hir>(
234 ctx: &mut LoweringContext<'_, 'hir, impl ResolverAstLoweringExt<'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(
267 count: &FormatCount,
268 argmap: &mut FxIndexMap<(usize, ArgumentType), Option<Span>>,
269) -> (bool, u16) {
270 match count {
271 FormatCount::Literal(n) => (false, *n),
272 FormatCount::Argument(arg) => (
273 true,
274 argmap.insert_full((arg.index.unwrap_or(usize::MAX), ArgumentType::Usize), arg.span).0
275 as u16,
276 ),
277 }
278}
279
280fn expand_format_args<'hir>(
281 ctx: &mut LoweringContext<'_, 'hir, impl ResolverAstLoweringExt<'hir>>,
282 macsp: Span,
283 fmt: &FormatArgs,
284 allow_const: bool,
285) -> hir::ExprKind<'hir> {
286 let macsp = ctx.lower_span(macsp);
287
288 let mut argmap = FxIndexMap::default();
294
295 let mut incomplete_lit = String::new();
296
297 let mut implicit_arg_index = 0;
298
299 let mut bytecode = Vec::new();
300
301 let template = if fmt.template.is_empty() {
302 &[FormatArgsPiece::Literal(sym::empty)][..]
305 } else {
306 &fmt.template[..]
307 };
308
309 for (i, piece) in template.iter().enumerate() {
312 match piece {
313 &FormatArgsPiece::Literal(sym) => {
314 if let Some(FormatArgsPiece::Literal(_)) = template.get(i + 1) {
316 incomplete_lit.push_str(sym.as_str());
317 continue;
318 }
319 let mut s = if incomplete_lit.is_empty() {
320 sym.as_str()
321 } else {
322 incomplete_lit.push_str(sym.as_str());
323 &incomplete_lit
324 };
325
326 if i + 1 == template.len() && bytecode.is_empty() {
331 let from_str = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
334 macsp,
335 hir::LangItem::FormatArguments,
336 if allow_const { sym::from_str } else { sym::from_str_nonconst },
337 ));
338 let sym = if incomplete_lit.is_empty() { sym } else { Symbol::intern(s) };
339 let s = ctx.expr_str(fmt.span, sym);
340 let args = ctx.arena.alloc_from_iter([s]);
341 return hir::ExprKind::Call(from_str, args);
342 }
343
344 while !s.is_empty() {
346 let len = s.floor_char_boundary(usize::from(u16::MAX));
347 if len < 0x80 {
348 bytecode.push(len as u8);
349 } else {
350 bytecode.push(0x80);
351 bytecode.extend_from_slice(&(len as u16).to_le_bytes());
352 }
353 bytecode.extend(&s.as_bytes()[..len]);
354 s = &s[len..];
355 }
356
357 incomplete_lit.clear();
358 }
359 FormatArgsPiece::Placeholder(p) => {
360 let i = bytecode.len();
362 bytecode.push(0xC0);
363
364 let position = argmap
365 .insert_full(
366 (
367 p.argument.index.unwrap_or(usize::MAX),
368 ArgumentType::Format(p.format_trait),
369 ),
370 p.span,
371 )
372 .0 as u64;
373
374 let o = &p.format_options;
376 let align = match o.alignment {
377 Some(FormatAlignment::Left) => 0,
378 Some(FormatAlignment::Right) => 1,
379 Some(FormatAlignment::Center) => 2,
380 None => 3,
381 };
382 let default_flags = 0x6000_0020;
383 let flags: u32 = o.fill.unwrap_or(' ') as u32
384 | ((o.sign == Some(FormatSign::Plus)) as u32) << 21
385 | ((o.sign == Some(FormatSign::Minus)) as u32) << 22
386 | (o.alternate as u32) << 23
387 | (o.zero_pad as u32) << 24
388 | ((o.debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25
389 | ((o.debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26
390 | (o.width.is_some() as u32) << 27
391 | (o.precision.is_some() as u32) << 28
392 | align << 29;
393 if flags != default_flags {
394 bytecode[i] |= 1;
395 bytecode.extend_from_slice(&flags.to_le_bytes());
396 if let Some(val) = &o.width {
397 let (indirect, val) = make_count(val, &mut argmap);
398 if indirect || val != 0 {
400 bytecode[i] |= 1 << 1 | (indirect as u8) << 4;
401 bytecode.extend_from_slice(&val.to_le_bytes());
402 }
403 }
404 if let Some(val) = &o.precision {
405 let (indirect, val) = make_count(val, &mut argmap);
406 if indirect || val != 0 {
408 bytecode[i] |= 1 << 2 | (indirect as u8) << 5;
409 bytecode.extend_from_slice(&val.to_le_bytes());
410 }
411 }
412 }
413 if implicit_arg_index != position {
414 bytecode[i] |= 1 << 3;
415 bytecode.extend_from_slice(&(position as u16).to_le_bytes());
416 }
417 implicit_arg_index = position + 1;
418 }
419 }
420 }
421
422 if !incomplete_lit.is_empty() {
::core::panicking::panic("assertion failed: incomplete_lit.is_empty()")
};assert!(incomplete_lit.is_empty());
423
424 bytecode.push(0);
426
427 if argmap.len() > u16::MAX as usize {
429 ctx.dcx().span_err(macsp, "too many format arguments");
430 }
431
432 let arguments = fmt.arguments.all_args();
433
434 let (let_statements, args) = if arguments.is_empty() {
435 (::alloc::vec::Vec::new()vec![], ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[]))))
438 } else {
439 let args_ident = Ident::new(sym::args, macsp);
442 let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
443 let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
444 let arg_expr = ctx.lower_expr(&arg.expr);
445 ctx.expr(
446 arg.expr.span.with_ctxt(macsp.ctxt()),
447 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
448 )
449 }));
450 let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
451 let let_statement_1 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args_tuple));
452
453 let args = ctx.arena.alloc_from_iter(argmap.iter().map(
461 |(&(arg_index, ty), &placeholder_span)| {
462 if let Some(arg) = arguments.get(arg_index) {
463 let placeholder_span =
464 placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
465 let arg_span = match arg.kind {
466 FormatArgumentKind::Captured(_) => placeholder_span,
467 _ => arg.expr.span.with_ctxt(macsp.ctxt()),
468 };
469 let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);
470 let arg = ctx.arena.alloc(ctx.expr(
471 arg_span,
472 hir::ExprKind::Field(
473 args_ident_expr,
474 Ident::new(sym::integer(arg_index), macsp),
475 ),
476 ));
477 make_argument(ctx, placeholder_span, arg, ty)
478 } else {
479 ctx.expr(
480 macsp,
481 hir::ExprKind::Err(
482 ctx.dcx().span_delayed_bug(macsp, "missing format_args argument"),
483 ),
484 )
485 }
486 },
487 ));
488 let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
489 let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
490 let let_statement_2 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
491 (
492 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[let_statement_1, let_statement_2]))vec![let_statement_1, let_statement_2],
493 ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)),
494 )
495 };
496
497 let template = ctx.expr_byte_str(macsp, ByteSymbol::intern(&bytecode));
502 let call = {
503 let new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
504 macsp,
505 hir::LangItem::FormatArguments,
506 sym::new,
507 ));
508 let args = ctx.expr_ref(macsp, args);
509 let new_args = ctx.arena.alloc_from_iter([template, args]);
510 ctx.expr_call(macsp, new, new_args)
511 };
512 let call = hir::ExprKind::Block(
513 ctx.arena.alloc(hir::Block {
514 stmts: &[],
515 expr: Some(call),
516 hir_id: ctx.next_id(),
517 rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
518 span: macsp,
519 targeted_by_break: false,
520 }),
521 None,
522 );
523
524 if !let_statements.is_empty() {
525 let call = ctx.arena.alloc(ctx.expr(macsp, call));
532 let block = ctx.block_all(macsp, ctx.arena.alloc_from_iter(let_statements), Some(call));
533 hir::ExprKind::Block(block, None)
534 } else {
535 call
536 }
537}
538
539fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) {
540 for piece in template {
541 let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
542 if let Ok(index) = &mut placeholder.argument.index {
543 f(index);
544 }
545 if let Some(FormatCount::Argument(FormatArgPosition { index: Ok(index), .. })) =
546 &mut placeholder.format_options.width
547 {
548 f(index);
549 }
550 if let Some(FormatCount::Argument(FormatArgPosition { index: Ok(index), .. })) =
551 &mut placeholder.format_options.precision
552 {
553 f(index);
554 }
555 }
556}