From f220866c9914c746e3a2da4e9c9b2cd2ab97bc8c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 18 Oct 2021 00:41:57 +0100 Subject: [PATCH 1/8] Create `core::fmt::ArgumentV1` with generics instead of fn pointer --- compiler/rustc_builtin_macros/src/format.rs | 18 ++++++++++++++---- library/core/src/fmt/mod.rs | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index f0056cb79766a..c7629418ef553 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -889,11 +889,21 @@ impl<'a, 'b> Context<'a, 'b> { return ecx.expr_call_global(macsp, path, vec![arg]); } }; + let new_fn_name = match trait_ { + "Display" => "new_display", + "Debug" => "new_debug", + "LowerExp" => "new_lower_exp", + "UpperExp" => "new_upper_exp", + "Octal" => "new_octal", + "Pointer" => "new_pointer", + "Binary" => "new_binary", + "LowerHex" => "new_lower_hex", + "UpperHex" => "new_upper_hex", + _ => unreachable!(), + }; - let path = ecx.std_path(&[sym::fmt, Symbol::intern(trait_), sym::fmt]); - let format_fn = ecx.path_global(sp, path); - let path = ecx.std_path(&[sym::fmt, sym::ArgumentV1, sym::new]); - ecx.expr_call_global(macsp, path, vec![arg, ecx.expr_path(format_fn)]) + let path = ecx.std_path(&[sym::fmt, sym::ArgumentV1, Symbol::intern(new_fn_name)]); + ecx.expr_call_global(macsp, path, vec![arg]) } } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 80d3270d73cc8..01eba56bd7714 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -308,6 +308,16 @@ static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr, _| { loop {} }; +macro_rules! arg_new { + ($f: ident, $t: ident) => { + #[doc(hidden)] + #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + pub fn $f<'b, T: $t>(x: &'b T) -> ArgumentV1<'_> { + Self::new(x, $t::fmt) + } + }; +} + impl<'a> ArgumentV1<'a> { #[doc(hidden)] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] @@ -323,6 +333,16 @@ impl<'a> ArgumentV1<'a> { unsafe { ArgumentV1 { formatter: mem::transmute(f), value: mem::transmute(x) } } } + arg_new!(new_display, Display); + arg_new!(new_debug, Debug); + arg_new!(new_octal, Octal); + arg_new!(new_lower_hex, LowerHex); + arg_new!(new_upper_hex, UpperHex); + arg_new!(new_pointer, Pointer); + arg_new!(new_binary, Binary); + arg_new!(new_lower_exp, LowerExp); + arg_new!(new_upper_exp, UpperExp); + #[doc(hidden)] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] pub fn from_usize(x: &usize) -> ArgumentV1<'_> { From bf39266fb0de6cc4e9e917d44ccec61a8179773a Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 24 Oct 2021 02:13:15 +0100 Subject: [PATCH 2/8] Add `const_panic_extra` feature gate for allowing panicking with format --- library/core/src/fmt/mod.rs | 6 ++++-- library/core/src/lib.rs | 1 + library/core/src/panicking.rs | 5 +---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 01eba56bd7714..4d0838fa1ebb1 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -312,7 +312,8 @@ macro_rules! arg_new { ($f: ident, $t: ident) => { #[doc(hidden)] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn $f<'b, T: $t>(x: &'b T) -> ArgumentV1<'_> { + #[rustc_const_unstable(feature = "const_panic_extra", issue = "none")] + pub const fn $f<'b, T: $t>(x: &'b T) -> ArgumentV1<'_> { Self::new(x, $t::fmt) } }; @@ -321,7 +322,8 @@ macro_rules! arg_new { impl<'a> ArgumentV1<'a> { #[doc(hidden)] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] - pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> { + #[rustc_const_unstable(feature = "const_panic_extra", issue = "none")] + pub const fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> { // SAFETY: `mem::transmute(x)` is safe because // 1. `&'b T` keeps the lifetime it originated with `'b` // (so as to not have an unbounded lifetime) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 5f44087cabbbc..9cb38432ec0b5 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -120,6 +120,7 @@ #![feature(const_num_from_num)] #![feature(const_ops)] #![feature(const_option)] +#![feature(const_panic_extra)] #![feature(const_pin)] #![feature(const_replace)] #![feature(const_ptr_offset)] diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 29124c87e1bc5..009f0b8af563f 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -112,10 +112,7 @@ 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() }; + panic_str(""); } } From c54098fbdafc0870e562270e3b62796b825ab9e2 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 24 Oct 2021 02:32:08 +0100 Subject: [PATCH 3/8] Constify `assert_{eq,ne,matches}` --- library/core/src/macros/mod.rs | 12 ++++++------ library/core/src/panicking.rs | 8 +++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 5b3e988caa506..2d3a21bbf5def 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -31,7 +31,7 @@ macro_rules! panic { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(core_panic, const_format_args)] macro_rules! assert_eq { ($left:expr, $right:expr $(,)?) => ({ match (&$left, &$right) { @@ -54,7 +54,7 @@ macro_rules! assert_eq { // The reborrows below are intentional. Without them, the stack slot for the // borrow is initialized even before the values are compared, leading to a // noticeable slow down. - $crate::panicking::assert_failed(kind, &*left_val, &*right_val, $crate::option::Option::Some($crate::format_args!($($arg)+))); + $crate::panicking::assert_failed(kind, &*left_val, &*right_val, $crate::option::Option::Some($crate::const_format_args!($($arg)+))); } } } @@ -80,7 +80,7 @@ macro_rules! assert_eq { /// ``` #[macro_export] #[stable(feature = "assert_ne", since = "1.13.0")] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(core_panic, const_format_args)] macro_rules! assert_ne { ($left:expr, $right:expr $(,)?) => ({ match (&$left, &$right) { @@ -103,7 +103,7 @@ macro_rules! assert_ne { // The reborrows below are intentional. Without them, the stack slot for the // borrow is initialized even before the values are compared, leading to a // noticeable slow down. - $crate::panicking::assert_failed(kind, &*left_val, &*right_val, $crate::option::Option::Some($crate::format_args!($($arg)+))); + $crate::panicking::assert_failed(kind, &*left_val, &*right_val, $crate::option::Option::Some($crate::const_format_args!($($arg)+))); } } } @@ -137,7 +137,7 @@ macro_rules! assert_ne { /// assert_matches!(c, Ok(x) | Err(x) if x.len() < 100); /// ``` #[unstable(feature = "assert_matches", issue = "82775")] -#[allow_internal_unstable(core_panic)] +#[allow_internal_unstable(core_panic, const_format_args)] #[rustc_macro_transparency = "semitransparent"] pub macro assert_matches { ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => ({ @@ -159,7 +159,7 @@ pub macro assert_matches { $crate::panicking::assert_matches_failed( left_val, $crate::stringify!($($pattern)|+ $(if $guard)?), - $crate::option::Option::Some($crate::format_args!($($arg)+)) + $crate::option::Option::Some($crate::const_format_args!($($arg)+)) ); } } diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 009f0b8af563f..0a4d6b5ccfcc0 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -127,8 +127,9 @@ pub enum AssertKind { /// Internal function for `assert_eq!` and `assert_ne!` macros #[cold] #[track_caller] +#[rustc_const_unstable(feature = "const_panic_extra", issue = "none")] #[doc(hidden)] -pub fn assert_failed( +pub const fn assert_failed( kind: AssertKind, left: &T, right: &U, @@ -144,8 +145,9 @@ where /// Internal function for `assert_match!` #[cold] #[track_caller] +#[rustc_const_unstable(feature = "const_panic_extra", issue = "none")] #[doc(hidden)] -pub fn assert_matches_failed( +pub const fn assert_matches_failed( left: &T, right: &str, args: Option>, @@ -162,7 +164,7 @@ pub fn assert_matches_failed( /// Non-generic version of the above functions, to avoid code bloat. #[track_caller] -fn assert_failed_inner( +const fn assert_failed_inner( kind: AssertKind, left: &dyn fmt::Debug, right: &dyn fmt::Debug, From 0a77ec2462952d2d39ebce74203db755aacb031a Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 24 Oct 2021 02:42:36 +0100 Subject: [PATCH 4/8] Evaluate const panic fmt arguments for builtin types --- .../src/const_eval/machine.rs | 19 +- .../rustc_const_eval/src/const_eval/mod.rs | 1 + .../rustc_const_eval/src/const_eval/panic.rs | 239 ++++++++++++++++++ .../rustc_const_eval/src/interpret/place.rs | 4 +- compiler/rustc_const_eval/src/lib.rs | 1 + compiler/rustc_hir/src/lang_items.rs | 1 - compiler/rustc_span/src/symbol.rs | 1 - library/core/src/panicking.rs | 1 + 8 files changed, 250 insertions(+), 17 deletions(-) create mode 100644 compiler/rustc_const_eval/src/const_eval/panic.rs diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index dacd8f7c12cfd..211ffafe3a271 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -64,19 +64,12 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { let (file, line, col) = self.location_triple_for_span(span); return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); } else if Some(def_id) == self.tcx.lang_items().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(), - )); - } + assert!(args.len() == 1); + let msg = self.eval_const_panic_fmt(args[0])?; + let msg = Symbol::intern(&msg); + let span = self.find_closest_untracked_caller_location(); + let (file, line, col) = self.location_triple_for_span(span); + return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); } Ok(None) } diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index a334165df4cb1..6755887765777 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -18,6 +18,7 @@ mod error; mod eval_queries; mod fn_queries; mod machine; +mod panic; pub use error::*; pub use eval_queries::*; diff --git a/compiler/rustc_const_eval/src/const_eval/panic.rs b/compiler/rustc_const_eval/src/const_eval/panic.rs new file mode 100644 index 0000000000000..0ab93c0e7914d --- /dev/null +++ b/compiler/rustc_const_eval/src/const_eval/panic.rs @@ -0,0 +1,239 @@ +use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, layout::LayoutOf, subst::Subst}; +use std::cell::Cell; +use std::fmt::{self, Debug, Formatter}; + +use crate::interpret::{FnVal, InterpCx, InterpErrorInfo, InterpResult, OpTy}; + +use super::CompileTimeInterpreter; + +struct Arg<'mir, 'tcx, 'err> { + cx: &'err InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + arg: OpTy<'tcx>, + fmt_trait: DefId, + err: &'err Cell>>, +} + +impl Debug for Arg<'_, '_, '_> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self.cx.fmt_arg(self.arg, self.fmt_trait, f) { + Ok(_) => Ok(()), + Err(e) => { + self.err.set(Some(e)); + Err(fmt::Error) + } + } + } +} + +impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { + fn fmt_arg( + &self, + arg: OpTy<'tcx>, + fmt_trait: DefId, + f: &mut Formatter<'_>, + ) -> InterpResult<'tcx> { + let fmt_trait_sym = self.tcx.item_name(fmt_trait); + let fmt_trait_name = fmt_trait_sym.as_str(); + + macro_rules! dispatch_fmt { + ($e: expr, $($t: ident)|*) => { + let _ = match &*fmt_trait_name { + $(stringify!($t) => fmt::$t::fmt($e, f),)* + _ => Debug::fmt($e, f), + }; + } + } + + match arg.layout.ty.kind() { + ty::Bool => { + let v = self.read_scalar(&arg)?.to_bool()?; + dispatch_fmt!(&v, Display); + } + ty::Char => { + let v = self.read_scalar(&arg)?.to_char()?; + dispatch_fmt!(&v, Display); + } + ty::Int(int_ty) => { + let v = self.read_scalar(&arg)?.check_init()?; + let v = match int_ty { + ty::IntTy::I8 => v.to_i8()?.into(), + ty::IntTy::I16 => v.to_i16()?.into(), + ty::IntTy::I32 => v.to_i32()?.into(), + ty::IntTy::I64 => v.to_i64()?.into(), + ty::IntTy::I128 => v.to_i128()?, + ty::IntTy::Isize => v.to_machine_isize(self)?.into(), + }; + dispatch_fmt!( + &v, + Display | Binary | Octal | LowerHex | UpperHex | LowerExp | UpperExp + ); + } + ty::Uint(int_ty) => { + let v = self.read_scalar(&arg)?.check_init()?; + let v = match int_ty { + ty::UintTy::U8 => v.to_u8()?.into(), + ty::UintTy::U16 => v.to_u16()?.into(), + ty::UintTy::U32 => v.to_u32()?.into(), + ty::UintTy::U64 => v.to_u64()?.into(), + ty::UintTy::U128 => v.to_u128()?, + ty::UintTy::Usize => v.to_machine_usize(self)?.into(), + }; + dispatch_fmt!( + &v, + Display | Binary | Octal | LowerHex | UpperHex | LowerExp | UpperExp + ); + } + ty::Float(ty::FloatTy::F32) => { + let v = f32::from_bits(self.read_scalar(&arg)?.to_u32()?); + dispatch_fmt!(&v, Display); + } + ty::Float(ty::FloatTy::F64) => { + let v = f64::from_bits(self.read_scalar(&arg)?.to_u64()?); + dispatch_fmt!(&v, Display); + } + ty::Str => { + let Ok(place) = arg.try_as_mplace() else { + bug!("str is not in MemPlace"); + }; + let v = self.read_str(&place)?; + dispatch_fmt!(v, Display); + } + ty::Array(..) | ty::Slice(..) => { + let Ok(place) = arg.try_as_mplace() else { + bug!("array/slice is not in MemPlace"); + }; + let err = Cell::new(None); + let mut debug_list = f.debug_list(); + for field in self.mplace_array_fields(&place)? { + debug_list.entry(&Arg { cx: self, arg: field?.into(), fmt_trait, err: &err }); + } + let _ = debug_list.finish(); + if let Some(e) = err.into_inner() { + return Err(e); + } + } + ty::RawPtr(..) | ty::FnPtr(..) => { + // This isn't precisely how Pointer is implemented, but it's best we can do. + let ptr = self.read_pointer(&arg)?; + let _ = write!(f, "{:?}", ptr); + } + ty::Tuple(substs) => { + let err = Cell::new(None); + let mut debug_tuple = f.debug_tuple(""); + for i in 0..substs.len() { + debug_tuple.field(&Arg { + cx: self, + arg: self.operand_field(&arg, i)?, + fmt_trait, + err: &err, + }); + } + let _ = debug_tuple.finish(); + if let Some(e) = err.into_inner() { + return Err(e); + } + } + + // FIXME(nbdd0121): extend to allow fmt trait as super trait + ty::Dynamic(list, _) if list.principal_def_id() == Some(fmt_trait) => { + let Ok(place) = arg.try_as_mplace() else { + bug!("dyn is not in MemPlace"); + }; + let place = self.unpack_dyn_trait(&place)?.1; + return self.fmt_arg(place.into(), fmt_trait, f); + } + + ty::Ref(..) if fmt_trait_name == "Pointer" => { + let ptr = self.read_pointer(&arg)?; + let _ = write!(f, "{:?}", ptr); + } + ty::Ref(..) => { + // FIXME(nbdd0121): User can implement trait on &UserType, so this isn't always correct. + let place = self.deref_operand(&arg)?; + return self.fmt_arg(place.into(), fmt_trait, f); + } + + // FIXME(nbdd0121): ty::Adt(..) => (), + _ => { + let _ = write!(f, "", arg.layout.ty); + } + } + Ok(()) + } + + fn fmt_arguments(&self, arguments: OpTy<'tcx>, f: &mut Formatter<'_>) -> InterpResult<'tcx> { + // Check we are dealing with the simple form + let fmt_variant_idx = self.read_discriminant(&self.operand_field(&arguments, 1)?)?.1; + if fmt_variant_idx.as_usize() != 0 { + // FIXME(nbdd0121): implement complex format + let _ = write!(f, ""); + return Ok(()); + } + + // `pieces: &[&str]` + let pieces_place = self.deref_operand(&self.operand_field(&arguments, 0)?)?; + let mut pieces = Vec::new(); + for piece in self.mplace_array_fields(&pieces_place)? { + let piece: OpTy<'tcx> = piece?.into(); + pieces.push(self.read_str(&self.deref_operand(&piece)?)?); + } + + // `args: &[ArgumentV1]` + let args_place = self.deref_operand(&self.operand_field(&arguments, 2)?)?; + let mut args = Vec::new(); + let err = Cell::new(None); + for arg in self.mplace_array_fields(&args_place)? { + let arg: OpTy<'tcx> = arg?.into(); + + let fmt_fn = self.memory.get_fn(self.read_pointer(&self.operand_field(&arg, 1)?)?)?; + let fmt_fn = match fmt_fn { + FnVal::Instance(instance) => instance, + FnVal::Other(o) => match o {}, + }; + + // The formatter must an instance of fmt method of a fmt trait. + let Some(fmt_impl) = self.tcx.impl_of_method(fmt_fn.def_id()) else { + throw_unsup_format!("fmt function is not from trait impl") + }; + let Some(fmt_trait) = self.tcx.impl_trait_ref(fmt_impl) else { + throw_unsup_format!("fmt function is not from trait impl") + }; + + // Retrieve the trait ref with concrete self ty. + let fmt_trait = fmt_trait.subst(*self.tcx, &fmt_fn.substs); + + // Change the opaque type into the actual type. + let mut value_place = self.deref_operand(&self.operand_field(&arg, 0)?)?; + value_place.layout = self.layout_of(fmt_trait.self_ty())?; + + args.push(Arg { + cx: self, + arg: value_place.into(), + fmt_trait: fmt_trait.def_id, + err: &err, + }); + } + + // SAFETY: This transmutes `&[&str]` to `&[&'static str]` so it can be used in + // `core::fmt::Arguments`. The slice will not be used after `write_fmt`. + let static_pieces = unsafe { core::mem::transmute(&pieces[..]) }; + let arg_v1s = args.iter().map(|x| fmt::ArgumentV1::new(x, Debug::fmt)).collect::>(); + let fmt_args = fmt::Arguments::new_v1(static_pieces, &arg_v1s); + let _ = f.write_fmt(fmt_args); + if let Some(v) = err.into_inner() { + return Err(v); + } + Ok(()) + } + + pub(super) fn eval_const_panic_fmt( + &mut self, + arguments: OpTy<'tcx>, + ) -> InterpResult<'tcx, String> { + let mut msg = String::new(); + let mut formatter = Formatter::new(&mut msg); + self.fmt_arguments(arguments, &mut formatter)?; + Ok(msg) + } +} diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index d425b84bdaf26..52432b1aeed01 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -420,7 +420,7 @@ where // Iterates over all fields of an array. Much more efficient than doing the // same by repeatedly calling `mplace_array`. - pub(super) fn mplace_array_fields( + pub(crate) fn mplace_array_fields( &self, base: &'a MPlaceTy<'tcx, Tag>, ) -> InterpResult<'tcx, impl Iterator>> + 'a> @@ -1082,7 +1082,7 @@ where /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. /// Also return some more information so drop doesn't have to run the same code twice. - pub(super) fn unpack_dyn_trait( + pub(crate) fn unpack_dyn_trait( &self, mplace: &MPlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> { diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index f308e764e861d..6fbdc0ab4d71f 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -23,6 +23,7 @@ Rust MIR: a lowered representation of Rust. #![feature(trusted_len)] #![feature(trusted_step)] #![feature(try_blocks)] +#![feature(fmt_internals)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 3037996d48bc0..50e26c7664f6a 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -285,7 +285,6 @@ language_item_table! { PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None; PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None; PanicStr, sym::panic_str, panic_str, Target::Fn, GenericRequirement::None; - ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None; PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0); PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None; PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1b4315896321f..39b1bc23b8847 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -465,7 +465,6 @@ symbols! { const_loop, const_mut_refs, const_panic, - const_panic_fmt, const_precise_live_drops, const_ptr, const_raw_ptr_deref, diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 0a4d6b5ccfcc0..f4cd6149f19f4 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -107,6 +107,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { } /// This function is used instead of panic_fmt in const eval. +#[cfg(bootstrap)] #[lang = "const_panic_fmt"] pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if let Some(msg) = fmt.as_str() { From ce8e3272e2e7f7efb9534ab74078df507b6a059f Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 24 Oct 2021 04:56:44 +0100 Subject: [PATCH 5/8] Recursive `Arguments` formatting --- compiler/rustc_const_eval/src/const_eval/panic.rs | 5 +++++ library/core/src/fmt/mod.rs | 1 + 2 files changed, 6 insertions(+) diff --git a/compiler/rustc_const_eval/src/const_eval/panic.rs b/compiler/rustc_const_eval/src/const_eval/panic.rs index 0ab93c0e7914d..d2cb0e6a58ef0 100644 --- a/compiler/rustc_const_eval/src/const_eval/panic.rs +++ b/compiler/rustc_const_eval/src/const_eval/panic.rs @@ -1,5 +1,6 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::{self, layout::LayoutOf, subst::Subst}; +use rustc_span::sym; use std::cell::Cell; use std::fmt::{self, Debug, Formatter}; @@ -154,6 +155,10 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { return self.fmt_arg(place.into(), fmt_trait, f); } + ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::Arguments, adt.did) => { + return self.fmt_arguments(arg, f); + } + // FIXME(nbdd0121): ty::Adt(..) => (), _ => { let _ = write!(f, "", arg.layout.ty); diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 4d0838fa1ebb1..e5ce20cca75cd 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -458,6 +458,7 @@ impl<'a> Arguments<'a> { /// /// [`format()`]: ../../std/fmt/fn.format.html #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "Arguments"] #[derive(Copy, Clone)] pub struct Arguments<'a> { // Format string pieces to print. From b34112c6a8a896dcbf5e0ba80977ed63af026f4a Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 24 Oct 2021 17:44:42 +0100 Subject: [PATCH 6/8] Constify `panic!("{}", non_str)` and `panic!(non_str)` under `const_panic_extra` --- .../rustc_const_eval/src/const_eval/machine.rs | 15 ++++----------- .../rustc_const_eval/src/const_eval/panic.rs | 18 ++++++++++++++++++ .../src/transform/check_consts/check.rs | 4 ++-- .../src/transform/check_consts/ops.rs | 12 ------------ library/core/src/panicking.rs | 4 ++-- library/std/src/panicking.rs | 1 + 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 211ffafe3a271..5cf0af6a925b5 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -48,18 +48,10 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { .unwrap(), )); } - } else if Some(def_id) == self.tcx.lang_items().panic_display() - || Some(def_id) == self.tcx.lang_items().begin_panic_fn() - { - // &str or &&str + } else if Some(def_id) == self.tcx.lang_items().begin_panic_fn() { assert!(args.len() == 1); - - let mut msg_place = self.deref_operand(&args[0])?; - while msg_place.layout.ty.is_ref() { - msg_place = self.deref_operand(&msg_place.into())?; - } - - let msg = Symbol::intern(self.read_str(&msg_place)?); + let msg = self.eval_const_panic_any(args[0])?; + let msg = Symbol::intern(&msg); let span = self.find_closest_untracked_caller_location(); let (file, line, col) = self.location_triple_for_span(span); return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); @@ -71,6 +63,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { let (file, line, col) = self.location_triple_for_span(span); return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); } + Ok(None) } } diff --git a/compiler/rustc_const_eval/src/const_eval/panic.rs b/compiler/rustc_const_eval/src/const_eval/panic.rs index d2cb0e6a58ef0..b3920c75b0a6a 100644 --- a/compiler/rustc_const_eval/src/const_eval/panic.rs +++ b/compiler/rustc_const_eval/src/const_eval/panic.rs @@ -158,6 +158,10 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::Arguments, adt.did) => { return self.fmt_arguments(arg, f); } + ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did) => { + // NOTE(nbdd0121): const `String` can only be empty. + dispatch_fmt!("", Display); + } // FIXME(nbdd0121): ty::Adt(..) => (), _ => { @@ -241,4 +245,18 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { self.fmt_arguments(arguments, &mut formatter)?; Ok(msg) } + + pub(super) fn eval_const_panic_any(&mut self, arg: OpTy<'tcx>) -> InterpResult<'tcx, String> { + match arg.layout.ty.kind() { + ty::Ref(_, ty, _) if ty.is_str() => { + let place = self.deref_operand(&arg)?; + Ok(self.read_str(&place)?.to_string()) + } + ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did) => { + // NOTE(nbdd0121): const `String` can only be empty. + Ok(String::new()) + } + _ => Ok("Box".to_string()), + } + } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 3a5bc37b85ad6..573f997ef1727 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -893,7 +893,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> { if Some(callee) == tcx.lang_items().begin_panic_fn() { match args[0].ty(&self.ccx.body.local_decls, tcx).kind() { ty::Ref(_, ty, _) if ty.is_str() => return, - _ => self.check_op(ops::PanicNonStr), + _ => (), } } @@ -904,7 +904,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> { { return; } - _ => self.check_op(ops::PanicNonStr), + _ => (), } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 230d023efb9fb..7d392d920b4a6 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -368,18 +368,6 @@ impl NonConstOp for MutDeref { } } -/// A call to a `panic()` lang item where the first argument is _not_ a `&str`. -#[derive(Debug)] -pub struct PanicNonStr; -impl NonConstOp for PanicNonStr { - fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { - ccx.tcx.sess.struct_span_err( - span, - "argument to `panic!()` in a const context must have type `&str`", - ) - } -} - /// Comparing raw pointers for equality. /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on /// allocation base addresses that are not known at compile-time. diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index f4cd6149f19f4..1fa00817bcc4a 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -51,13 +51,13 @@ pub const fn panic(expr: &'static str) -> ! { #[track_caller] #[lang = "panic_str"] // needed for `non-fmt-panics` lint pub const fn panic_str(expr: &str) -> ! { - panic_display(&expr); + panic_fmt(format_args!("{}", expr)); } #[inline] #[track_caller] #[lang = "panic_display"] // needed for const-evaluated panics -#[rustc_do_not_const_check] // hooked by const-eval +#[rustc_const_unstable(feature = "const_panic_extra", issue = "none")] pub const fn panic_display(x: &T) -> ! { panic_fmt(format_args!("{}", *x)); } diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 6fc6b8daec0a6..7916f70d3878e 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -515,6 +515,7 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[cold] #[track_caller] +#[rustc_const_unstable(feature = "const_panic_extra", issue = "none")] #[rustc_do_not_const_check] // hooked by const-eval pub const fn begin_panic(msg: M) -> ! { if cfg!(feature = "panic_immediate_abort") { From c3ea64c35d204a855ccd30e89ed418caca309b6a Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 24 Oct 2021 18:54:22 +0100 Subject: [PATCH 7/8] Add tests for const_panic_extra gate --- .../ui/consts/const-eval/const_panic_extra.rs | 24 +++++++ .../const-eval/const_panic_extra.stderr | 63 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/test/ui/consts/const-eval/const_panic_extra.rs create mode 100644 src/test/ui/consts/const-eval/const_panic_extra.stderr diff --git a/src/test/ui/consts/const-eval/const_panic_extra.rs b/src/test/ui/consts/const-eval/const_panic_extra.rs new file mode 100644 index 0000000000000..1930c0744bc87 --- /dev/null +++ b/src/test/ui/consts/const-eval/const_panic_extra.rs @@ -0,0 +1,24 @@ +#![allow(non_fmt_panics)] +#![feature(const_panic_extra)] +#![crate_type = "lib"] + +const _: () = core::panic!("hello {}", "world"); +//~^ ERROR evaluation of constant value failed + +const _: () = core::panic!("answer is {ans}", ans = 21*2); +//~^ ERROR evaluation of constant value failed + +const _: () = core::assert_eq!(42, 43); +//~^ ERROR evaluation of constant value failed + +const _: () = core::assert_ne!(42, 42, "hello {}", "world"); +//~^ ERROR evaluation of constant value failed + +const _: () = core::panic!("{}", 42); +//~^ ERROR evaluation of constant value failed + +const _: () = std::panic!(42); +//~^ ERROR evaluation of constant value failed + +const _: () = std::panic!(String::new()); +//~^ ERROR evaluation of constant value failed diff --git a/src/test/ui/consts/const-eval/const_panic_extra.stderr b/src/test/ui/consts/const-eval/const_panic_extra.stderr new file mode 100644 index 0000000000000..5ea47484e3c9f --- /dev/null +++ b/src/test/ui/consts/const-eval/const_panic_extra.stderr @@ -0,0 +1,63 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_extra.rs:5:15 + | +LL | const _: () = core::panic!("hello {}", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'hello world', $DIR/const_panic_extra.rs:5: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_extra.rs:8:15 + | +LL | const _: () = core::panic!("answer is {ans}", ans = 21*2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'answer is 42', $DIR/const_panic_extra.rs:8: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_extra.rs:11:15 + | +LL | const _: () = core::assert_eq!(42, 43); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: `(left == right)` + left: `42`, + right: `43`', $DIR/const_panic_extra.rs:11:15 + | + = note: this error originates in the macro `core::assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_extra.rs:14:15 + | +LL | const _: () = core::assert_ne!(42, 42, "hello {}", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: `(left != right)` + left: `42`, + right: `42`: hello world', $DIR/const_panic_extra.rs:14:15 + | + = note: this error originates in the macro `core::assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_extra.rs:17:15 + | +LL | const _: () = core::panic!("{}", 42); + | ^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at '42', $DIR/const_panic_extra.rs:17: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_extra.rs:20:15 + | +LL | const _: () = std::panic!(42); + | ^^^^^^^^^^^^^^^ the evaluated program panicked at 'Box', $DIR/const_panic_extra.rs:20: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_extra.rs:23:15 + | +LL | const _: () = std::panic!(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at '', $DIR/const_panic_extra.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: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0080`. From 6e59d709ead40c7c559bfebfc7fc45a12b1df852 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 9 Nov 2021 23:28:38 +0000 Subject: [PATCH 8/8] Fix format_args span --- compiler/rustc_builtin_macros/src/format.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index c7629418ef553..d0be1a4d930e5 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -903,7 +903,7 @@ impl<'a, 'b> Context<'a, 'b> { }; let path = ecx.std_path(&[sym::fmt, sym::ArgumentV1, Symbol::intern(new_fn_name)]); - ecx.expr_call_global(macsp, path, vec![arg]) + ecx.expr_call_global(sp, path, vec![arg]) } }