1 use super::LoweringContext;
3 use rustc_ast::visit::{self, Visitor};
5 use rustc_data_structures::fx::FxIndexSet;
13 impl<'hir> LoweringContext<'_, 'hir> {
14 pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> {
15 expand_format_args(self, sp, fmt)
19 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
25 /// Generate a hir expression representing an argument to a format_args invocation.
30 /// <core::fmt::ArgumentV1>::new_…(arg)
32 fn make_argument<'hir>(
33 ctx: &mut LoweringContext<'_, 'hir>,
35 arg: &'hir hir::Expr<'hir>,
37 ) -> hir::Expr<'hir> {
40 let new_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
42 hir::LangItem::FormatArgument,
44 Format(Display) => sym::new_display,
45 Format(Debug) => sym::new_debug,
46 Format(LowerExp) => sym::new_lower_exp,
47 Format(UpperExp) => sym::new_upper_exp,
48 Format(Octal) => sym::new_octal,
49 Format(Pointer) => sym::new_pointer,
50 Format(Binary) => sym::new_binary,
51 Format(LowerHex) => sym::new_lower_hex,
52 Format(UpperHex) => sym::new_upper_hex,
53 Usize => sym::from_usize,
56 ctx.expr_call_mut(sp, new_fn, std::slice::from_ref(arg))
59 /// Generate a hir expression for a format_args Count.
64 /// <core::fmt::rt::v1::Count>::Is(…)
70 /// <core::fmt::rt::v1::Count>::Param(…)
76 /// <core::fmt::rt::v1::Count>::Implied
79 ctx: &mut LoweringContext<'_, 'hir>,
81 count: &Option<FormatCount>,
82 argmap: &mut FxIndexSet<(usize, ArgumentType)>,
83 ) -> hir::Expr<'hir> {
85 Some(FormatCount::Literal(n)) => {
86 let count_is = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
88 hir::LangItem::FormatCount,
91 let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, *n)]);
92 ctx.expr_call_mut(sp, count_is, value)
94 Some(FormatCount::Argument(arg)) => {
95 if let Ok(arg_index) = arg.index {
96 let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
97 let count_param = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
99 hir::LangItem::FormatCount,
102 let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, i)]);
103 ctx.expr_call_mut(sp, count_param, value)
105 ctx.expr(sp, hir::ExprKind::Err)
108 None => ctx.expr_lang_item_type_relative(sp, hir::LangItem::FormatCount, sym::Implied),
112 /// Generate a hir expression for a format_args placeholder specification.
117 /// <core::fmt::rt::v1::Argument::new(
118 /// …usize, // position
120 /// <core::fmt::rt::v1::Alignment>::…, // alignment
122 /// <core::fmt::rt::v1::Count::…>, // width
123 /// <core::fmt::rt::v1::Count::…>, // precision
126 fn make_format_spec<'hir>(
127 ctx: &mut LoweringContext<'_, 'hir>,
129 placeholder: &FormatPlaceholder,
130 argmap: &mut FxIndexSet<(usize, ArgumentType)>,
131 ) -> hir::Expr<'hir> {
132 let position = match placeholder.argument.index {
135 argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
136 ctx.expr_usize(sp, i)
138 Err(_) => ctx.expr(sp, hir::ExprKind::Err),
140 let fill = ctx.expr_char(sp, placeholder.format_options.fill.unwrap_or(' '));
141 let align = ctx.expr_lang_item_type_relative(
143 hir::LangItem::FormatAlignment,
144 match placeholder.format_options.alignment {
145 Some(FormatAlignment::Left) => sym::Left,
146 Some(FormatAlignment::Right) => sym::Right,
147 Some(FormatAlignment::Center) => sym::Center,
148 None => sym::Unknown,
151 let flags = ctx.expr_u32(sp, placeholder.format_options.flags);
152 let prec = make_count(ctx, sp, &placeholder.format_options.precision, argmap);
153 let width = make_count(ctx, sp, &placeholder.format_options.width, argmap);
154 let format_placeholder_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
156 hir::LangItem::FormatPlaceholder,
159 let args = ctx.arena.alloc_from_iter([position, fill, align, flags, prec, width]);
160 ctx.expr_call_mut(sp, format_placeholder_new, args)
163 fn expand_format_args<'hir>(
164 ctx: &mut LoweringContext<'_, 'hir>,
167 ) -> hir::ExprKind<'hir> {
169 ctx.arena.alloc_from_iter(fmt.template.iter().enumerate().filter_map(|(i, piece)| {
171 &FormatArgsPiece::Literal(s) => Some(ctx.expr_str(fmt.span, s)),
172 &FormatArgsPiece::Placeholder(_) => {
173 // Inject empty string before placeholders when not already preceded by a literal piece.
174 if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) {
175 Some(ctx.expr_str(fmt.span, kw::Empty))
182 let lit_pieces = ctx.expr_array_ref(fmt.span, lit_pieces);
184 // Whether we'll use the `Arguments::new_v1_formatted` form (true),
185 // or the `Arguments::new_v1` form (false).
186 let mut use_format_options = false;
188 // Create a list of all _unique_ (argument, format trait) combinations.
189 // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
190 let mut argmap = FxIndexSet::default();
191 for piece in &fmt.template {
192 let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
193 if placeholder.format_options != Default::default() {
194 // Can't use basic form if there's any formatting options.
195 use_format_options = true;
197 if let Ok(index) = placeholder.argument.index {
198 if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) {
199 // Duplicate (argument, format trait) combination,
200 // which we'll only put once in the args array.
201 use_format_options = true;
206 let format_options = use_format_options.then(|| {
208 // &[format_spec_0, format_spec_1, format_spec_2]
209 let elements: Vec<_> = fmt
212 .filter_map(|piece| {
213 let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
214 Some(make_format_spec(ctx, macsp, placeholder, &mut argmap))
217 ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
220 let arguments = fmt.arguments.all_args();
222 // If the args array contains exactly all the original arguments once,
223 // in order, we can use a simple array instead of a `match` construction.
224 // However, if there's a yield point in any argument except the first one,
225 // we don't do this, because an ArgumentV1 cannot be kept across yield points.
227 // This is an optimization, speeding up compilation about 1-2% in some cases.
228 // See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609
229 let use_simple_array = argmap.len() == arguments.len()
230 && argmap.iter().enumerate().all(|(i, &(j, _))| i == j)
231 && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
233 let args = if use_simple_array {
236 // <core::fmt::ArgumentV1>::new_display(&arg0),
237 // <core::fmt::ArgumentV1>::new_lower_hex(&arg1),
238 // <core::fmt::ArgumentV1>::new_debug(&arg2),
241 let elements: Vec<_> = arguments
244 .map(|(arg, (_, ty))| {
245 let sp = arg.expr.span.with_ctxt(macsp.ctxt());
246 let arg = ctx.lower_expr(&arg.expr);
247 let ref_arg = ctx.arena.alloc(ctx.expr(
249 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
251 make_argument(ctx, sp, ref_arg, ty)
254 ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
257 // &match (&arg0, &arg1, &…) {
259 // <core::fmt::ArgumentV1>::new_display(args.0),
260 // <core::fmt::ArgumentV1>::new_lower_hex(args.1),
261 // <core::fmt::ArgumentV1>::new_debug(args.0),
265 let args_ident = Ident::new(sym::args, macsp);
266 let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
267 let args = ctx.arena.alloc_from_iter(argmap.iter().map(|&(arg_index, ty)| {
268 if let Some(arg) = arguments.get(arg_index) {
269 let sp = arg.expr.span.with_ctxt(macsp.ctxt());
270 let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);
271 let arg = ctx.arena.alloc(ctx.expr(
273 hir::ExprKind::Field(
275 Ident::new(sym::integer(arg_index), macsp),
278 make_argument(ctx, sp, arg, ty)
280 ctx.expr(macsp, hir::ExprKind::Err)
283 let elements: Vec<_> = arguments
286 let arg_expr = ctx.lower_expr(&arg.expr);
288 arg.expr.span.with_ctxt(macsp.ctxt()),
289 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
295 .alloc(ctx.expr(macsp, hir::ExprKind::Tup(ctx.arena.alloc_from_iter(elements))));
296 let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
297 let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]);
298 let match_expr = ctx.arena.alloc(ctx.expr_match(
302 hir::MatchSource::FormatArgs,
306 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, match_expr),
310 if let Some(format_options) = format_options {
312 // <core::fmt::Arguments>::new_v1_formatted(
316 // unsafe { ::core::fmt::UnsafeArg::new() }
318 let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
320 hir::LangItem::FormatArguments,
321 sym::new_v1_formatted,
323 let unsafe_arg_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
325 hir::LangItem::FormatUnsafeArg,
328 let unsafe_arg_new_call = ctx.expr_call(macsp, unsafe_arg_new, &[]);
329 let hir_id = ctx.next_id();
330 let unsafe_arg = ctx.expr_block(ctx.arena.alloc(hir::Block {
332 expr: Some(unsafe_arg_new_call),
334 rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
336 targeted_by_break: false,
338 let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options, unsafe_arg]);
339 hir::ExprKind::Call(new_v1_formatted, args)
342 // <core::fmt::Arguments>::new_v1(
346 let new_v1 = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
348 hir::LangItem::FormatArguments,
351 let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
352 hir::ExprKind::Call(new_v1, new_args)
356 fn may_contain_yield_point(e: &ast::Expr) -> bool {
357 struct MayContainYieldPoint(bool);
359 impl Visitor<'_> for MayContainYieldPoint {
360 fn visit_expr(&mut self, e: &ast::Expr) {
361 if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind {
364 visit::walk_expr(self, e);
368 fn visit_mac_call(&mut self, _: &ast::MacCall) {
369 // Macros should be expanded at this point.
370 unreachable!("unexpanded macro in ast lowering");
373 fn visit_item(&mut self, _: &ast::Item) {
374 // Do not recurse into nested items.
378 let mut visitor = MayContainYieldPoint(false);
379 visitor.visit_expr(e);