diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index ba27f10330928..b1be50b0bf990 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -72,6 +72,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { file: source_util::expand_file, format_args_nl: format::expand_format_args_nl, format_args: format::expand_format_args, + const_format_args: format::expand_format_args, global_asm: asm::expand_global_asm, include_bytes: source_util::expand_include_bytes, include_str: source_util::expand_include_str, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 28ae08030e630..55000ae7e59d4 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -276,13 +276,16 @@ language_item_table! { // is required to define it somewhere. Additionally, there are restrictions on crates that use // a weak lang item, but do not have it defined. Panic, sym::panic, panic_fn, Target::Fn; + PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn; PanicStr, sym::panic_str, panic_str, Target::Fn; + ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn; PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn; PanicInfo, sym::panic_info, panic_info, Target::Struct; PanicLocation, sym::panic_location, panic_location, Target::Struct; PanicImpl, sym::panic_impl, panic_impl, Target::Fn; /// libstd panic entry point. Necessary for const eval to be able to catch it BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn; + BeginPanicFmt, sym::begin_panic_fmt, begin_panic_fmt, Target::Fn; ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn; BoxFree, sym::box_free, box_free_fn, Target::Fn; diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs index daaf68c1d2bd5..8a90686b9003f 100644 --- a/compiler/rustc_mir/src/const_eval/machine.rs +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -30,7 +30,9 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { &mut self, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], - ) -> InterpResult<'tcx> { + ) -> InterpResult<'tcx, Option>> { + // The list of functions we handle here must be in sync with + // `is_lang_panic_fn` in `transform/check_consts/mod.rs`. let def_id = instance.def_id(); if Some(def_id) == self.tcx.lang_items().panic_fn() || Some(def_id) == self.tcx.lang_items().panic_str() @@ -43,10 +45,25 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { let msg = Symbol::intern(self.read_str(&msg_place)?); let span = self.find_closest_untracked_caller_location(); let (file, line, col) = self.location_triple_for_span(span); - Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()) - } else { - Ok(()) + return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); + } else if Some(def_id) == self.tcx.lang_items().panic_fmt() + || Some(def_id) == self.tcx.lang_items().begin_panic_fmt() + { + // For panic_fmt, call const_panic_fmt instead. + if let Some(const_panic_fmt) = self.tcx.lang_items().const_panic_fmt() { + return Ok(Some( + ty::Instance::resolve( + *self.tcx, + ty::ParamEnv::reveal_all(), + const_panic_fmt, + self.tcx.intern_substs(&[]), + ) + .unwrap() + .unwrap(), + )); + } } + Ok(None) } } @@ -241,10 +258,21 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, if !ecx.tcx.has_attr(def.did, sym::default_method_body_is_const) { // Some functions we support even if they are non-const -- but avoid testing // that for const fn! - ecx.hook_panic_fn(instance, args)?; - // We certainly do *not* want to actually call the fn - // though, so be sure we return here. - throw_unsup_format!("calling non-const function `{}`", instance) + if let Some(new_instance) = ecx.hook_panic_fn(instance, args)? { + // We call another const fn instead. + return Self::find_mir_or_eval_fn( + ecx, + new_instance, + _abi, + args, + _ret, + _unwind, + ); + } else { + // We certainly do *not* want to actually call the fn + // though, so be sure we return here. + throw_unsup_format!("calling non-const function `{}`", instance) + } } } } diff --git a/compiler/rustc_mir/src/transform/check_consts/mod.rs b/compiler/rustc_mir/src/transform/check_consts/mod.rs index 0ca086d74db7b..7e22ed22db4fe 100644 --- a/compiler/rustc_mir/src/transform/check_consts/mod.rs +++ b/compiler/rustc_mir/src/transform/check_consts/mod.rs @@ -74,9 +74,14 @@ impl ConstCx<'mir, 'tcx> { /// Returns `true` if this `DefId` points to one of the official `panic` lang items. pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { + // We can allow calls to these functions because `hook_panic_fn` in + // `const_eval/machine.rs` ensures the calls are handled specially. + // Keep in sync with what that function handles! Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().panic_str() || Some(def_id) == tcx.lang_items().begin_panic_fn() + || Some(def_id) == tcx.lang_items().panic_fmt() + || Some(def_id) == tcx.lang_items().begin_panic_fmt() } pub fn rustc_allow_const_fn_unstable( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 5fc773e431c8b..114750d9b7b3a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -323,6 +323,7 @@ symbols! { await_macro, bang, begin_panic, + begin_panic_fmt, bench, bin, bind_by_move_pattern_guards, @@ -409,6 +410,7 @@ symbols! { const_fn_transmute, const_fn_union, const_fn_unsize, + const_format_args, const_generic_defaults, const_generics, const_generics_defaults, @@ -420,6 +422,7 @@ symbols! { const_loop, const_mut_refs, const_panic, + const_panic_fmt, const_precise_live_drops, const_ptr, const_raw_ptr_deref, @@ -586,6 +589,7 @@ symbols! { fmaf32, fmaf64, fmt, + fmt_as_str, fmt_internals, fmul_fast, fn_align, @@ -881,6 +885,7 @@ symbols! { panic_2021, panic_abort, panic_bounds_check, + panic_fmt, panic_handler, panic_impl, panic_implementation, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 48142f66915bb..6ad10990840c2 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -337,7 +337,8 @@ impl<'a> Arguments<'a> { #[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> { + #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] + pub const fn new_v1(pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> { Arguments { pieces, fmt: None, args } } @@ -350,7 +351,8 @@ impl<'a> Arguments<'a> { #[doc(hidden)] #[inline] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn new_v1_formatted( + #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] + pub const fn new_v1_formatted( pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>], fmt: &'a [rt::v1::Argument], diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 839be5a143f71..d3e66a854c9b5 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -73,6 +73,7 @@ #![feature(cfg_target_has_atomic)] #![feature(const_heap)] #![feature(const_alloc_layout)] +#![feature(const_arguments_as_str)] #![feature(const_assert_type)] #![feature(const_discriminant)] #![feature(const_cell_into_inner)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 07ee589e29f56..50fefb8273199 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -837,6 +837,31 @@ pub(crate) mod builtin { ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; } + /// Same as `format_args`, but can be used in some const contexts. + /// + /// This macro is used by the panic macros for the `const_panic` feature. + /// + /// This macro will be removed once `format_args` is allowed in const contexts. + #[cfg(not(bootstrap))] + #[unstable(feature = "const_format_args", issue = "none")] + #[allow_internal_unstable(fmt_internals, const_fmt_arguments_new)] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! const_format_args { + ($fmt:expr) => {{ /* compiler built-in */ }}; + ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; + } + + /// Same as `format_args`, but can be used in some const contexts. + #[cfg(bootstrap)] + #[unstable(feature = "const_format_args", issue = "none")] + #[macro_export] + macro_rules! const_format_args { + ($($t:tt)*) => { + $crate::format_args!($($t)*) + } + } + /// Same as `format_args`, but adds a newline in the end. #[unstable( feature = "format_args_nl", diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index cbb10c324c495..4b72f9ed169ec 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -7,7 +7,7 @@ use crate::fmt; #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(core_panic, const_format_args)] #[rustc_diagnostic_item = "core_panic_2015_macro"] #[rustc_macro_transparency = "semitransparent"] pub macro panic_2015 { @@ -21,13 +21,13 @@ pub macro panic_2015 { $crate::panicking::panic_str($msg) ), ($fmt:expr, $($arg:tt)+) => ( - $crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+)) + $crate::panicking::panic_fmt($crate::const_format_args!($fmt, $($arg)+)) ), } #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(core_panic, const_format_args)] #[rustc_diagnostic_item = "core_panic_2021_macro"] #[rustc_macro_transparency = "semitransparent"] pub macro panic_2021 { @@ -35,7 +35,7 @@ pub macro panic_2021 { $crate::panicking::panic("explicit panic") ), ($($t:tt)+) => ( - $crate::panicking::panic_fmt($crate::format_args!($($t)+)) + $crate::panicking::panic_fmt($crate::const_format_args!($($t)+)) ), } diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 3e3e96fcd7f78..2ec6b4d15ffd1 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -74,6 +74,7 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] +#[cfg_attr(not(bootstrap), lang = "panic_fmt")] // needed for const-evaluated panics pub fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if cfg!(feature = "panic_immediate_abort") { super::intrinsics::abort() @@ -92,6 +93,20 @@ pub fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { unsafe { panic_impl(&pi) } } +/// This function is used instead of panic_fmt in const eval. +#[cfg(not(bootstrap))] +#[lang = "const_panic_fmt"] +pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! { + if let Some(msg) = fmt.as_str() { + panic_str(msg); + } else { + // SAFETY: This is only evaluated at compile time, which reliably + // handles this UB (in case this branch turns out to be reachable + // somehow). + unsafe { crate::hint::unreachable_unchecked() }; + } +} + #[derive(Debug)] #[doc(hidden)] pub enum AssertKind { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f0e628c3d8363..808a2202b3e9f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -247,6 +247,7 @@ #![feature(const_fn_floating_point_arithmetic)] #![feature(const_fn_fn_ptr_basics)] #![cfg_attr(bootstrap, feature(const_fn_transmute))] +#![feature(const_format_args)] #![feature(const_io_structs)] #![feature(const_ip)] #![feature(const_ipv4)] @@ -556,9 +557,9 @@ pub use core::{ #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow(deprecated)] pub use core::{ - assert, assert_matches, cfg, column, compile_error, concat, concat_idents, env, file, - format_args, format_args_nl, include, include_bytes, include_str, line, llvm_asm, log_syntax, - module_path, option_env, stringify, trace_macros, + assert, assert_matches, cfg, column, compile_error, concat, concat_idents, const_format_args, + env, file, format_args, format_args_nl, include, include_bytes, include_str, line, llvm_asm, + log_syntax, module_path, option_env, stringify, trace_macros, }; #[stable(feature = "core_primitive", since = "1.43.0")] diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 7bc987db8814b..3b3996e437c2b 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -20,7 +20,7 @@ use crate::thread::Result; #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] -#[allow_internal_unstable(libstd_sys_internals)] +#[allow_internal_unstable(libstd_sys_internals, const_format_args)] #[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_2015_macro")] #[rustc_macro_transparency = "semitransparent"] pub macro panic_2015 { @@ -31,7 +31,7 @@ pub macro panic_2015 { $crate::rt::begin_panic($msg) }), ($fmt:expr, $($arg:tt)+) => ({ - $crate::rt::begin_panic_fmt(&$crate::format_args!($fmt, $($arg)+)) + $crate::rt::begin_panic_fmt(&$crate::const_format_args!($fmt, $($arg)+)) }), } diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 0b9c9fb479f51..5f43393e58583 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -448,6 +448,7 @@ pub fn panicking() -> bool { #[cfg_attr(not(feature = "panic_immediate_abort"), track_caller)] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cfg_attr(all(not(bootstrap), not(test)), lang = "begin_panic_fmt")] pub fn begin_panic_fmt(msg: &fmt::Arguments<'_>) -> ! { if cfg!(feature = "panic_immediate_abort") { intrinsics::abort() diff --git a/src/test/ui/borrowck/issue-64453.rs b/src/test/ui/borrowck/issue-64453.rs index 3e803f3b6d8b2..9e70a847457c0 100644 --- a/src/test/ui/borrowck/issue-64453.rs +++ b/src/test/ui/borrowck/issue-64453.rs @@ -3,7 +3,7 @@ struct Value; static settings_dir: String = format!(""); //~^ ERROR calls in statics are limited to constant functions -//~| ERROR calls in statics are limited to constant functions +//~| ERROR is not yet stable as a const fn from_string(_: String) -> Value { Value diff --git a/src/test/ui/borrowck/issue-64453.stderr b/src/test/ui/borrowck/issue-64453.stderr index bd8270ef958cb..5513c3d217e2a 100644 --- a/src/test/ui/borrowck/issue-64453.stderr +++ b/src/test/ui/borrowck/issue-64453.stderr @@ -4,12 +4,13 @@ error[E0507]: cannot move out of static item `settings_dir` LL | let settings_data = from_string(settings_dir); | ^^^^^^^^^^^^ move occurs because `settings_dir` has type `String`, which does not implement the `Copy` trait -error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants +error: `Arguments::<'a>::new_v1` is not yet stable as a const fn --> $DIR/issue-64453.rs:4:31 | LL | static settings_dir: String = format!(""); | ^^^^^^^^^^^ | + = help: add `#![feature(const_fmt_arguments_new)]` to the crate attributes to enable = note: this error originates in the macro `$crate::__export::format_args` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants diff --git a/src/test/ui/consts/const-eval/const_panic_2021.rs b/src/test/ui/consts/const-eval/const_panic_2021.rs new file mode 100644 index 0000000000000..daef34cd6a306 --- /dev/null +++ b/src/test/ui/consts/const-eval/const_panic_2021.rs @@ -0,0 +1,27 @@ +// edition:2021 +#![feature(const_panic)] +#![crate_type = "lib"] + +const A: () = std::panic!("blåhaj"); +//~^ ERROR evaluation of constant value failed + +const B: () = std::panic!(); +//~^ ERROR evaluation of constant value failed + +const C: () = std::unreachable!(); +//~^ ERROR evaluation of constant value failed + +const D: () = std::unimplemented!(); +//~^ ERROR evaluation of constant value failed + +const E: () = core::panic!("shark"); +//~^ ERROR evaluation of constant value failed + +const F: () = core::panic!(); +//~^ ERROR evaluation of constant value failed + +const G: () = core::unreachable!(); +//~^ ERROR evaluation of constant value failed + +const H: () = core::unimplemented!(); +//~^ ERROR evaluation of constant value failed diff --git a/src/test/ui/consts/const-eval/const_panic_2021.stderr b/src/test/ui/consts/const-eval/const_panic_2021.stderr new file mode 100644 index 0000000000000..c1bdab3693d11 --- /dev/null +++ b/src/test/ui/consts/const-eval/const_panic_2021.stderr @@ -0,0 +1,67 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_2021.rs:5:15 + | +LL | const A: () = std::panic!("blåhaj"); + | ^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'blåhaj', $DIR/const_panic_2021.rs:5:15 + | + = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_2021.rs:8:15 + | +LL | const B: () = std::panic!(); + | ^^^^^^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/const_panic_2021.rs:8:15 + | + = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_2021.rs:11:15 + | +LL | const C: () = std::unreachable!(); + | ^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic_2021.rs:11:15 + | + = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_2021.rs:14:15 + | +LL | const D: () = std::unimplemented!(); + | ^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'not implemented', $DIR/const_panic_2021.rs:14:15 + | + = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_2021.rs:17:15 + | +LL | const E: () = core::panic!("shark"); + | ^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'shark', $DIR/const_panic_2021.rs:17:15 + | + = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_2021.rs:20:15 + | +LL | const F: () = core::panic!(); + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/const_panic_2021.rs:20:15 + | + = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_2021.rs:23:15 + | +LL | const G: () = core::unreachable!(); + | ^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic_2021.rs:23:15 + | + = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_2021.rs:26:15 + | +LL | const H: () = core::unimplemented!(); + | ^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'not implemented', $DIR/const_panic_2021.rs:26:15 + | + = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0080`.