Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 255aa22

Browse files
committed
Auto merge of #140748 - m-ou-se:super-format-args3, r=jdonszelmann
Allow storing `format_args!()` in variable Fixes #92698 Tracking issue for super let: #139076 Tracking issue for format_args: #99012 This change allows: ```rust let name = "world"; let f = format_args!("hello {name}!"); // New: Store format_args!() for later! println!("{f}"); ``` This will need an FCP. This implementation makes use of `super let`, which is unstable and might not exist in the future in its current form. However, it is entirely reasonable to assume future Rust will always have _a_ way of expressing temporary lifetimes like this, since the (stable) `pin!()` macro needs this too. (This was also the motivation for merging #139114.) (This is a second version of #139135)
2 parents 8de4c72 + b4f2cac commit 255aa22

25 files changed

+296
-392
lines changed

‎compiler/rustc_ast_lowering/src/expr.rs‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2289,12 +2289,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
22892289
span: Span,
22902290
elements: &'hir [hir::Expr<'hir>],
22912291
) -> hir::Expr<'hir> {
2292-
let addrof = hir::ExprKind::AddrOf(
2293-
hir::BorrowKind::Ref,
2294-
hir::Mutability::Not,
2295-
self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements))),
2296-
);
2297-
self.expr(span, addrof)
2292+
let array = self.arena.alloc(self.expr(span,hir::ExprKind::Array(elements)));
2293+
self.expr_ref(span, array)
2294+
}
2295+
2296+
pub(super)fnexpr_ref(&mutself,span:Span,expr:&'hir hir::Expr<'hir>) -> hir::Expr<'hir>{
2297+
self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr))
22982298
}
22992299

23002300
pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind<'hir>) -> hir::Expr<'hir> {

‎compiler/rustc_ast_lowering/src/format.rs‎

Lines changed: 77 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
use core::ops::ControlFlow;
21
use std::borrow::Cow;
32

4-
use rustc_ast::visit::Visitor;
53
use rustc_ast::*;
64
use rustc_data_structures::fx::FxIndexMap;
75
use rustc_hir as hir;
@@ -476,77 +474,52 @@ fn expand_format_args<'hir>(
476474
return hir::ExprKind::Call(new, new_args);
477475
}
478476

479-
// If the args array contains exactly all the original arguments once,
480-
// in order, we can use a simple array instead of a `match` construction.
481-
// However, if there's a yield point in any argument except the first one,
482-
// we don't do this, because an Argument cannot be kept across yield points.
483-
//
484-
// This is an optimization, speeding up compilation about 1-2% in some cases.
485-
// See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609
486-
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() {
477+
let (let_statements, args) = if arguments.is_empty() {
491478
// Generate:
492-
// &<core::fmt::Argument>::none()
479+
// []
480+
(vec![], ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[]))))
481+
} else if argmap.len() == 1 && arguments.len() == 1 {
482+
// Only one argument, so we don't need to make the `args` tuple.
493483
//
494-
// Note:
495-
// `none()` just returns `[]`. We use `none()` rather than `[]` to limit the lifetime.
496-
//
497-
// This makes sure that this still fails to compile, even when the argument is inlined:
498-
//
499-
// ```
500-
// let f = format_args!("{}", "a");
501-
// println!("{f}"); // error E0716
502-
// ```
503-
//
504-
// Cases where keeping the object around is allowed, such as `format_args!("a")`,
505-
// are handled above by the `allow_const` case.
506-
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 {
514484
// Generate:
515-
// &[
516-
// <core::fmt::Argument>::new_display(&arg0),
517-
// <core::fmt::Argument>::new_lower_hex(&arg1),
518-
// <core::fmt::Argument>::new_debug(&arg2),
519-
// ...
520-
// ]
521-
let elements = ctx.arena.alloc_from_iter(arguments.iter().zip(argmap).map(
522-
|(arg, ((_, ty), placeholder_span))| {
485+
// super let args = [<core::fmt::Argument>::new_display(&arg)];
486+
let args = ctx.arena.alloc_from_iter(argmap.iter().map(
487+
|(&(arg_index, ty), &placeholder_span)| {
488+
let arg = &arguments[arg_index];
523489
let placeholder_span =
524490
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-
};
529491
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-
));
492+
let ref_arg = ctx.arena.alloc(ctx.expr_ref(arg.span.with_ctxt(macsp.ctxt()), arg));
534493
make_argument(ctx, placeholder_span, ref_arg, ty)
535494
},
536495
));
537-
ctx.expr_array_ref(macsp, elements)
496+
let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
497+
let args_ident = Ident::new(sym::args, macsp);
498+
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
499+
let let_statement = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
500+
(vec![let_statement], ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)))
538501
} else {
539502
// Generate:
540-
// &match (&arg0, &arg1, &...) {
541-
// args => [
542-
// <core::fmt::Argument>::new_display(args.0),
543-
// <core::fmt::Argument>::new_lower_hex(args.1),
544-
// <core::fmt::Argument>::new_debug(args.0),
545-
// ...
546-
// ]
547-
// }
503+
// super let args = (&arg0, &arg1, &...);
548504
let args_ident = Ident::new(sym::args, macsp);
549505
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
506+
let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
507+
let arg_expr = ctx.lower_expr(&arg.expr);
508+
ctx.expr(
509+
arg.expr.span.with_ctxt(macsp.ctxt()),
510+
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
511+
)
512+
}));
513+
let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
514+
let let_statement_1 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args_tuple));
515+
516+
// Generate:
517+
// super let args = [
518+
// <core::fmt::Argument>::new_display(args.0),
519+
// <core::fmt::Argument>::new_lower_hex(args.1),
520+
// <core::fmt::Argument>::new_debug(args.0),
521+
// ...
522+
// ];
550523
let args = ctx.arena.alloc_from_iter(argmap.iter().map(
551524
|(&(arg_index, ty), &placeholder_span)| {
552525
let arg = &arguments[arg_index];
@@ -567,58 +540,47 @@ fn expand_format_args<'hir>(
567540
make_argument(ctx, placeholder_span, arg, ty)
568541
},
569542
));
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),
543+
let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
544+
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
545+
let let_statement_2 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
546+
(
547+
vec![let_statement_1, let_statement_2],
548+
ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)),
589549
)
590550
};
591551

592-
if let Some(format_options) = format_options {
552+
// Generate:
553+
// &args
554+
let args = ctx.expr_ref(macsp, args);
555+
556+
let call = if let Some(format_options) = format_options {
593557
// Generate:
594-
// <core::fmt::Arguments>::new_v1_formatted(
595-
// lit_pieces,
596-
// args,
597-
// format_options,
598-
// unsafe { ::core::fmt::UnsafeArg::new() }
599-
// )
558+
// unsafe {
559+
// <core::fmt::Arguments>::new_v1_formatted(
560+
// lit_pieces,
561+
// args,
562+
// format_options,
563+
// )
564+
// }
600565
let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
601566
macsp,
602567
hir::LangItem::FormatArguments,
603568
sym::new_v1_formatted,
604569
));
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, &[]);
570+
let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options]);
571+
let call = ctx.expr_call(macsp, new_v1_formatted, args);
611572
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)
573+
hir::ExprKind::Block(
574+
ctx.arena.alloc(hir::Block {
575+
stmts: &[],
576+
expr: Some(call),
577+
hir_id,
578+
rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
579+
span: macsp,
580+
targeted_by_break: false,
581+
}),
582+
None,
583+
)
622584
} else {
623585
// Generate:
624586
// <core::fmt::Arguments>::new_v1(
@@ -632,35 +594,21 @@ fn expand_format_args<'hir>(
632594
));
633595
let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
634596
hir::ExprKind::Call(new_v1, new_args)
635-
}
636-
}
637-
638-
fn 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-
// Macros should be expanded at this point.
654-
unreachable!("unexpanded macro in ast lowering");
655-
}
597+
};
656598

657-
fn visit_item(&mut self, _: &ast::Item) -> ControlFlow<()> {
658-
// Do not recurse into nested items.
659-
ControlFlow::Continue(())
660-
}
599+
if !let_statements.is_empty() {
600+
// Generate:
601+
// {
602+
// super let ...
603+
// super let ...
604+
// <core::fmt::Arguments>::new_...(...)
605+
// }
606+
let call = ctx.arena.alloc(ctx.expr(macsp, call));
607+
let block = ctx.block_all(macsp, ctx.arena.alloc_from_iter(let_statements), Some(call));
608+
hir::ExprKind::Block(block, None)
609+
} else {
610+
call
661611
}
662-
663-
MayContainYieldPoint.visit_expr(e).is_break()
664612
}
665613

666614
fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) {

‎compiler/rustc_ast_lowering/src/lib.rs‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2292,6 +2292,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
22922292
self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
22932293
}
22942294

2295+
fn stmt_super_let_pat(
2296+
&mut self,
2297+
span: Span,
2298+
pat: &'hir hir::Pat<'hir>,
2299+
init: Option<&'hir hir::Expr<'hir>>,
2300+
) -> hir::Stmt<'hir> {
2301+
let hir_id = self.next_id();
2302+
let local = hir::LetStmt {
2303+
super_: Some(span),
2304+
hir_id,
2305+
init,
2306+
pat,
2307+
els: None,
2308+
source: hir::LocalSource::Normal,
2309+
span: self.lower_span(span),
2310+
ty: None,
2311+
};
2312+
self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
2313+
}
2314+
22952315
fn block_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> &'hir hir::Block<'hir> {
22962316
self.block_all(expr.span, &[], Some(expr))
22972317
}

‎compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs‎

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3201,14 +3201,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
32013201
let expr_ty: Option<Ty<'_>> =
32023202
visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());
32033203

3204-
let is_format_arguments_item = if let Some(expr_ty) = expr_ty
3205-
&& let ty::Adt(adt, _) = expr_ty.kind()
3206-
{
3207-
self.infcx.tcx.is_lang_item(adt.did(), LangItem::FormatArguments)
3208-
} else {
3209-
false
3210-
};
3211-
32123204
if visitor.found == 0
32133205
&& stmt.span.contains(proper_span)
32143206
&& let Some(p) = sm.span_to_margin(stmt.span)
@@ -3236,25 +3228,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
32363228
""
32373229
};
32383230

3239-
if !is_format_arguments_item {
3240-
let addition = format!(
3241-
"let {}binding = {};\n{}",
3242-
mutability,
3243-
s,
3244-
" ".repeat(p)
3245-
);
3246-
err.multipart_suggestion_verbose(
3247-
msg,
3248-
vec![
3249-
(stmt.span.shrink_to_lo(), addition),
3250-
(proper_span, "binding".to_string()),
3251-
],
3252-
Applicability::MaybeIncorrect,
3253-
);
3254-
} else {
3255-
err.note("the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used");
3256-
err.note("to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>");
3257-
}
3231+
let addition =
3232+
format!("let {}binding = {};\n{}", mutability, s, " ".repeat(p));
3233+
err.multipart_suggestion_verbose(
3234+
msg,
3235+
vec![
3236+
(stmt.span.shrink_to_lo(), addition),
3237+
(proper_span, "binding".to_string()),
3238+
],
3239+
Applicability::MaybeIncorrect,
3240+
);
3241+
32583242
suggested = true;
32593243
break;
32603244
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /