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 fn make_argument<'hir>(
26 ctx: &mut LoweringContext<'_, 'hir>,
28 arg: &'hir hir::Expr<'hir>,
30 ) -> hir::Expr<'hir> {
32 // ::core::fmt::ArgumentV1::new_…(arg)
35 let new_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
37 hir::LangItem::FormatArgument,
39 Format(Display) => sym::new_display,
40 Format(Debug) => sym::new_debug,
41 Format(LowerExp) => sym::new_lower_exp,
42 Format(UpperExp) => sym::new_upper_exp,
43 Format(Octal) => sym::new_octal,
44 Format(Pointer) => sym::new_pointer,
45 Format(Binary) => sym::new_binary,
46 Format(LowerHex) => sym::new_lower_hex,
47 Format(UpperHex) => sym::new_upper_hex,
48 Usize => sym::from_usize,
51 ctx.expr_call_mut(sp, new_fn, std::slice::from_ref(arg))
55 ctx: &mut LoweringContext<'_, 'hir>,
57 count: &Option<FormatCount>,
58 argmap: &mut FxIndexSet<(usize, ArgumentType)>,
59 ) -> hir::Expr<'hir> {
61 // ::core::fmt::rt::v1::Count::…(…)
63 Some(FormatCount::Literal(n)) => {
64 let count_is = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
66 hir::LangItem::FormatCount,
69 let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, *n)]);
70 ctx.expr_call_mut(sp, count_is, value)
72 Some(FormatCount::Argument(arg)) => {
73 if let Ok(arg_index) = arg.index {
74 let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
75 let count_param = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
77 hir::LangItem::FormatCount,
80 let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, i)]);
81 ctx.expr_call_mut(sp, count_param, value)
83 ctx.expr(sp, hir::ExprKind::Err)
86 None => ctx.expr_lang_item_type_relative(sp, hir::LangItem::FormatCount, sym::Implied),
90 fn make_format_spec<'hir>(
91 ctx: &mut LoweringContext<'_, 'hir>,
93 placeholder: &FormatPlaceholder,
94 argmap: &mut FxIndexSet<(usize, ArgumentType)>,
95 ) -> hir::Expr<'hir> {
97 // ::core::fmt::rt::v1::Argument::new(
98 // 0usize, // position
100 // ::core::fmt::rt::v1::Alignment::Unknown,
102 // ::core::fmt::rt::v1::Count::Implied, // width
103 // ::core::fmt::rt::v1::Count::Implied, // precision
105 let position = match placeholder.argument.index {
108 argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
109 ctx.expr_usize(sp, i)
111 Err(_) => ctx.expr(sp, hir::ExprKind::Err),
113 let fill = ctx.expr_char(sp, placeholder.format_options.fill.unwrap_or(' '));
114 let align = ctx.expr_lang_item_type_relative(
116 hir::LangItem::FormatAlignment,
117 match placeholder.format_options.alignment {
118 Some(FormatAlignment::Left) => sym::Left,
119 Some(FormatAlignment::Right) => sym::Right,
120 Some(FormatAlignment::Center) => sym::Center,
121 None => sym::Unknown,
124 let flags = ctx.expr_u32(sp, placeholder.format_options.flags);
125 let prec = make_count(ctx, sp, &placeholder.format_options.precision, argmap);
126 let width = make_count(ctx, sp, &placeholder.format_options.width, argmap);
127 let format_placeholder_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
129 hir::LangItem::FormatPlaceholder,
132 let args = ctx.arena.alloc_from_iter([position, fill, align, flags, prec, width]);
133 ctx.expr_call_mut(sp, format_placeholder_new, args)
136 fn expand_format_args<'hir>(
137 ctx: &mut LoweringContext<'_, 'hir>,
140 ) -> hir::ExprKind<'hir> {
142 ctx.arena.alloc_from_iter(fmt.template.iter().enumerate().filter_map(|(i, piece)| {
144 &FormatArgsPiece::Literal(s) => Some(ctx.expr_str(fmt.span, s)),
145 &FormatArgsPiece::Placeholder(_) => {
146 // Inject empty string before placeholders when not already preceded by a literal piece.
147 if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) {
148 Some(ctx.expr_str(fmt.span, kw::Empty))
155 let lit_pieces = ctx.expr_array_ref(fmt.span, lit_pieces);
157 // Whether we'll use the `Arguments::new_v1_formatted` form (true),
158 // or the `Arguments::new_v1` form (false).
159 let mut use_format_options = false;
161 // Create a list of all _unique_ (argument, format trait) combinations.
162 // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
163 let mut argmap = FxIndexSet::default();
164 for piece in &fmt.template {
165 let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
166 if placeholder.format_options != Default::default() {
167 // Can't use basic form if there's any formatting options.
168 use_format_options = true;
170 if let Ok(index) = placeholder.argument.index {
171 if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) {
172 // Duplicate (argument, format trait) combination,
173 // which we'll only put once in the args array.
174 use_format_options = true;
179 let format_options = use_format_options.then(|| {
181 // &[format_spec_0, format_spec_1, format_spec_2]
182 let elements: Vec<_> = fmt
185 .filter_map(|piece| {
186 let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
187 Some(make_format_spec(ctx, macsp, placeholder, &mut argmap))
190 ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
193 let arguments = fmt.arguments.all_args();
195 // If the args array contains exactly all the original arguments once,
196 // in order, we can use a simple array instead of a `match` construction.
197 // However, if there's a yield point in any argument except the first one,
198 // we don't do this, because an ArgumentV1 cannot be kept across yield points.
199 let use_simple_array = argmap.len() == arguments.len()
200 && argmap.iter().enumerate().all(|(i, &(j, _))| i == j)
201 && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
203 let args = if use_simple_array {
206 // ::core::fmt::ArgumentV1::new_display(&arg0),
207 // ::core::fmt::ArgumentV1::new_lower_hex(&arg1),
208 // ::core::fmt::ArgumentV1::new_debug(&arg2),
210 let elements: Vec<_> = arguments
213 .map(|(arg, (_, ty))| {
214 let sp = arg.expr.span.with_ctxt(macsp.ctxt());
215 let arg = ctx.lower_expr(&arg.expr);
216 let ref_arg = ctx.arena.alloc(ctx.expr(
218 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
220 make_argument(ctx, sp, ref_arg, ty)
223 ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
226 // &match (&arg0, &arg1, &arg2) {
228 // ::core::fmt::ArgumentV1::new_display(args.0),
229 // ::core::fmt::ArgumentV1::new_lower_hex(args.1),
230 // ::core::fmt::ArgumentV1::new_debug(args.0),
233 let args_ident = Ident::new(sym::args, macsp);
234 let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
235 let args = ctx.arena.alloc_from_iter(argmap.iter().map(|&(arg_index, ty)| {
236 if let Some(arg) = arguments.get(arg_index) {
237 let sp = arg.expr.span.with_ctxt(macsp.ctxt());
238 let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);
239 let arg = ctx.arena.alloc(ctx.expr(
241 hir::ExprKind::Field(
243 Ident::new(sym::integer(arg_index), macsp),
246 make_argument(ctx, sp, arg, ty)
248 ctx.expr(macsp, hir::ExprKind::Err)
251 let elements: Vec<_> = arguments
254 let arg_expr = ctx.lower_expr(&arg.expr);
256 arg.expr.span.with_ctxt(macsp.ctxt()),
257 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
263 .alloc(ctx.expr(macsp, hir::ExprKind::Tup(ctx.arena.alloc_from_iter(elements))));
264 let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
265 let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]);
266 let match_expr = ctx.arena.alloc(ctx.expr_match(
270 hir::MatchSource::FormatArgs,
274 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, match_expr),
278 if let Some(format_options) = format_options {
280 // ::core::fmt::Arguments::new_v1_formatted(
284 // unsafe { ::core::fmt::UnsafeArg::new() }
286 let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
288 hir::LangItem::FormatArguments,
289 sym::new_v1_formatted,
291 let unsafe_arg_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
293 hir::LangItem::FormatUnsafeArg,
296 let unsafe_arg_new_call = ctx.expr_call(macsp, unsafe_arg_new, &[]);
297 let hir_id = ctx.next_id();
298 let unsafe_arg = ctx.expr_block(ctx.arena.alloc(hir::Block {
300 expr: Some(unsafe_arg_new_call),
302 rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
304 targeted_by_break: false,
306 let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options, unsafe_arg]);
307 hir::ExprKind::Call(new_v1_formatted, args)
310 // ::core::fmt::Arguments::new_v1(
314 let new_v1 = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
316 hir::LangItem::FormatArguments,
319 let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
320 hir::ExprKind::Call(new_v1, new_args)
324 fn may_contain_yield_point(e: &ast::Expr) -> bool {
325 struct MayContainYieldPoint(bool);
327 impl Visitor<'_> for MayContainYieldPoint {
328 fn visit_expr(&mut self, e: &ast::Expr) {
329 if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind {
332 visit::walk_expr(self, e);
336 fn visit_mac_call(&mut self, _: &ast::MacCall) {
337 // Macros should be expanded at this point.
338 unreachable!("unexpanded macro in ast lowering");
341 fn visit_item(&mut self, _: &ast::Item) {
342 // Do not recurse into nested items.
346 let mut visitor = MayContainYieldPoint(false);
347 visitor.visit_expr(e);