From f2f6bcc4998c6f2a2e044bac428a7ca16028c148 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 1 Feb 2023 17:39:14 +0100 Subject: [PATCH] Don't allow new const panic through format flattening. panic!("a {}", "b") is still not allowed in const, even if the hir flattens to panic!("a b"). --- compiler/rustc_ast_lowering/src/format.rs | 18 +++++++++++++- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/fmt/mod.rs | 26 ++++++++++++++++++-- library/core/src/panicking.rs | 4 ++-- tests/ui/borrowck/issue-64453.stderr | 2 +- tests/ui/consts/const-eval/format.rs | 3 ++- tests/ui/consts/const-eval/format.stderr | 29 +++++++++++++++-------- 7 files changed, 66 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index 8a8318312d2..e094457961e 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -13,9 +13,12 @@ use std::borrow::Cow; impl<'hir> LoweringContext<'_, 'hir> { pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> { + // Never call the const constructor of `fmt::Arguments` if the + // format_args!() had any arguments _before_ flattening/inlining. + let allow_const = fmt.arguments.all_args().is_empty(); let fmt = flatten_format_args(Cow::Borrowed(fmt)); let fmt = inline_literals(fmt); - expand_format_args(self, sp, &fmt) + expand_format_args(self, sp, &fmt, allow_const) } } @@ -342,6 +345,7 @@ fn expand_format_args<'hir>( ctx: &mut LoweringContext<'_, 'hir>, macsp: Span, fmt: &FormatArgs, + allow_const: bool, ) -> hir::ExprKind<'hir> { let mut incomplete_lit = String::new(); let lit_pieces = @@ -411,6 +415,18 @@ fn expand_format_args<'hir>( let arguments = fmt.arguments.all_args(); + if allow_const && arguments.is_empty() && argmap.is_empty() { + // Generate: + // ::new_const(lit_pieces) + let new = ctx.arena.alloc(ctx.expr_lang_item_type_relative( + macsp, + hir::LangItem::FormatArguments, + sym::new_const, + )); + let new_args = ctx.arena.alloc_from_iter([lit_pieces]); + return hir::ExprKind::Call(new, new_args); + } + // If the args array contains exactly all the original arguments once, // in order, we can use a simple array instead of a `match` construction. // However, if there's a yield point in any argument except the first one, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 0154c719ef6..abe5af8f9e0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -984,6 +984,7 @@ symbols! { never_type_fallback, new, new_binary, + new_const, new_debug, new_display, new_lower_exp, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index c9821bf8109..6d764237dc8 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -392,8 +392,31 @@ enum FlagV1 { } impl<'a> Arguments<'a> { + #[doc(hidden)] + #[inline] + #[unstable(feature = "fmt_internals", issue = "none")] + #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] + pub const fn new_const(pieces: &'a [&'static str]) -> Self { + if pieces.len() > 1 { + panic!("invalid args"); + } + Arguments { pieces, fmt: None, args: &[] } + } + /// When using the format_args!() macro, this function is used to generate the /// Arguments structure. + #[cfg(not(bootstrap))] + #[doc(hidden)] + #[inline] + #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + pub fn new_v1(pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> { + if pieces.len() < args.len() || pieces.len() > args.len() + 1 { + panic!("invalid args"); + } + Arguments { pieces, fmt: None, args } + } + + #[cfg(bootstrap)] #[doc(hidden)] #[inline] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] @@ -417,8 +440,7 @@ impl<'a> Arguments<'a> { #[doc(hidden)] #[inline] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] - pub const fn new_v1_formatted( + pub fn new_v1_formatted( pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>], fmt: &'a [rt::v1::Argument], diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 805a1e51ae9..dd0105c0eb4 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -111,7 +111,7 @@ pub const fn panic(expr: &'static str) -> ! { // truncation and padding (even though none is used here). Using // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the // output binary, saving up to a few kilobytes. - panic_fmt(fmt::Arguments::new_v1(&[expr], &[])); + panic_fmt(fmt::Arguments::new_const(&[expr])); } /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize. @@ -120,7 +120,7 @@ pub const fn panic(expr: &'static str) -> ! { #[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics #[rustc_nounwind] pub fn panic_nounwind(expr: &'static str) -> ! { - panic_nounwind_fmt(fmt::Arguments::new_v1(&[expr], &[])); + panic_nounwind_fmt(fmt::Arguments::new_const(&[expr])); } #[inline] diff --git a/tests/ui/borrowck/issue-64453.stderr b/tests/ui/borrowck/issue-64453.stderr index 245c3a40e05..f032ea779dd 100644 --- a/tests/ui/borrowck/issue-64453.stderr +++ b/tests/ui/borrowck/issue-64453.stderr @@ -1,4 +1,4 @@ -error: `Arguments::<'a>::new_v1` is not yet stable as a const fn +error: `Arguments::<'a>::new_const` is not yet stable as a const fn --> $DIR/issue-64453.rs:4:31 | LL | static settings_dir: String = format!(""); diff --git a/tests/ui/consts/const-eval/format.rs b/tests/ui/consts/const-eval/format.rs index 0d8b7c12d8a..5bdb2bf1954 100644 --- a/tests/ui/consts/const-eval/format.rs +++ b/tests/ui/consts/const-eval/format.rs @@ -1,12 +1,13 @@ const fn failure() { panic!("{:?}", 0); //~^ ERROR cannot call non-const formatting macro in constant functions + //~| ERROR cannot call non-const fn `Arguments::<'_>::new_v1` in constant functions } const fn print() { println!("{:?}", 0); //~^ ERROR cannot call non-const formatting macro in constant functions - //~| ERROR `Arguments::<'a>::new_v1` is not yet stable as a const fn + //~| ERROR cannot call non-const fn `Arguments::<'_>::new_v1` in constant functions //~| ERROR cannot call non-const fn `_print` in constant functions } diff --git a/tests/ui/consts/const-eval/format.stderr b/tests/ui/consts/const-eval/format.stderr index 4bf39db5874..c39920d444d 100644 --- a/tests/ui/consts/const-eval/format.stderr +++ b/tests/ui/consts/const-eval/format.stderr @@ -7,8 +7,17 @@ LL | panic!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) +error[E0015]: cannot call non-const fn `Arguments::<'_>::new_v1` in constant functions + --> $DIR/format.rs:2:5 + | +LL | panic!("{:?}", 0); + | ^^^^^^^^^^^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0015]: cannot call non-const formatting macro in constant functions - --> $DIR/format.rs:7:22 + --> $DIR/format.rs:8:22 | LL | println!("{:?}", 0); | ^ @@ -16,17 +25,17 @@ LL | println!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `Arguments::<'a>::new_v1` is not yet stable as a const fn - --> $DIR/format.rs:7:5 +error[E0015]: cannot call non-const fn `Arguments::<'_>::new_v1` in constant functions + --> $DIR/format.rs:8:5 | LL | println!("{:?}", 0); | ^^^^^^^^^^^^^^^^^^^ | - = help: add `#![feature(const_fmt_arguments_new)]` to the crate attributes to enable + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `_print` in constant functions - --> $DIR/format.rs:7:5 + --> $DIR/format.rs:8:5 | LL | println!("{:?}", 0); | ^^^^^^^^^^^^^^^^^^^ @@ -63,19 +72,19 @@ LL | panic!("{:?}", 0); = note: this note originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant used - --> $DIR/format.rs:7:14 + --> $DIR/format.rs:8:14 | LL | println!("{:?}", 0); | ^^^^^^ note: erroneous constant used - --> $DIR/format.rs:7:14 + --> $DIR/format.rs:8:14 | LL | println!("{:?}", 0); | ^^^^^^ note: erroneous constant used - --> $DIR/format.rs:7:22 + --> $DIR/format.rs:8:22 | LL | println!("{:?}", 0); | ^ @@ -83,13 +92,13 @@ LL | println!("{:?}", 0); = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant used - --> $DIR/format.rs:7:22 + --> $DIR/format.rs:8:22 | LL | println!("{:?}", 0); | ^ | = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0015`.