From d5843ddaf1f3291e30b60af44b58dccbcd9b6090 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Mon, 24 Apr 2023 17:31:32 +0200 Subject: [PATCH] Limit lifetime of format_args!() with inlined args. --- compiler/rustc_ast_lowering/src/format.rs | 25 ++++++++++++++++++++++- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/fmt/rt.rs | 15 ++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index 9b295339d94..afcf8b15cd8 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -446,7 +446,30 @@ fn expand_format_args<'hir>( && argmap.iter().enumerate().all(|(i, (&(j, _), _))| i == j) && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr)); - let args = if use_simple_array { + let args = if arguments.is_empty() { + // Generate: + // &::none() + // + // Note: + // `none()` just returns `[]`. We use `none()` rather than `[]` to limit the lifetime. + // + // This makes sure that this still fails to compile, even when the argument is inlined: + // + // ``` + // let f = format_args!("{}", "a"); + // println!("{f}"); // error E0716 + // ``` + // + // Cases where keeping the object around is allowed, such as `format_args!("a")`, + // are handled above by the `allow_const` case. + let none_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative( + macsp, + hir::LangItem::FormatArgument, + sym::none, + )); + let none = ctx.expr_call(macsp, none_fn, &[]); + ctx.expr(macsp, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, none)) + } else if use_simple_array { // Generate: // &[ // ::new_display(&arg0), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 117e2774bd8..60efcb768cb 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1033,6 +1033,7 @@ symbols! { non_exhaustive_omitted_patterns_lint, non_lifetime_binders, non_modrs_mods, + none, nontemporal_store, noop_method_borrow, noop_method_clone, diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs index 0596f6c30ce..d37888c27bd 100644 --- a/library/core/src/fmt/rt.rs +++ b/library/core/src/fmt/rt.rs @@ -152,6 +152,21 @@ impl<'a> Argument<'a> { None } } + + /// Used by `format_args` when all arguments are gone after inlining, + /// when using `&[]` would incorrectly allow for a bigger lifetime. + /// + /// This fails without format argument inlining, and that shouldn't be different + /// when the argument is inlined: + /// + /// ```compile_fail,E0716 + /// let f = format_args!("{}", "a"); + /// println!("{f}"); + /// ``` + #[inline(always)] + pub fn none() -> [Self; 0] { + [] + } } /// This struct represents the unsafety of constructing an `Arguments`.