diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index bee5a89c4c7..7d037ddfa98 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -47,6 +47,8 @@ extern crate cfg_if; #[macro_use] extern crate rustc_macros; +use std::fmt; + pub use rustc_index::static_assert_size; #[inline(never)] @@ -126,6 +128,23 @@ impl Drop for OnDrop { } } +/// Turns a closure that takes an `&mut Formatter` into something that can be display-formatted. +pub fn make_display(f: impl Fn(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Display { + struct Printer { + f: F, + } + impl fmt::Display for Printer + where + F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result, + { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + (self.f)(fmt) + } + } + + Printer { f } +} + // See comments in src/librustc_middle/lib.rs #[doc(hidden)] pub fn __noop_fix_for_27438() {} diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index fb6dbe34a89..7707c424703 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -70,7 +70,7 @@ pub use self::pretty::{ create_dump_file, display_allocation, dump_enabled, dump_mir, write_mir_pretty, PassWhere, }; pub use consts::*; -pub use pretty::pretty_print_const_value; +use pretty::pretty_print_const_value; pub use syntax::*; pub use terminator::*; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 56b59fe5204..39af95d62f8 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -455,26 +455,27 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { self.push(&format!("+ user_ty: {user_ty:?}")); } - // FIXME: this is a poor version of `pretty_print_const_value`. - let fmt_val = |val: &ConstValue<'tcx>| match val { - ConstValue::ZeroSized => "".to_string(), - ConstValue::Scalar(s) => format!("Scalar({s:?})"), - ConstValue::Slice { .. } => "Slice(..)".to_string(), - ConstValue::Indirect { .. } => "ByRef(..)".to_string(), + let fmt_val = |val: ConstValue<'tcx>, ty: Ty<'tcx>| { + let tcx = self.tcx; + rustc_data_structures::make_display(move |fmt| { + pretty_print_const_value_tcx(tcx, val, ty, fmt) + }) }; + // FIXME: call pretty_print_const_valtree? let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree { - ty::ValTree::Leaf(leaf) => format!("ValTree::Leaf({leaf:?})"), - ty::ValTree::Branch(_) => "ValTree::Branch(..)".to_string(), + ty::ValTree::Leaf(leaf) => format!("Leaf({leaf:?})"), + ty::ValTree::Branch(_) => "Branch(..)".to_string(), }; let val = match literal { ConstantKind::Ty(ct) => match ct.kind() { - ty::ConstKind::Param(p) => format!("Param({p})"), + ty::ConstKind::Param(p) => format!("ty::Param({p})"), ty::ConstKind::Unevaluated(uv) => { - format!("Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,) + format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,) } - ty::ConstKind::Value(val) => format!("Value({})", fmt_valtree(&val)), + ty::ConstKind::Value(val) => format!("ty::Valtree({})", fmt_valtree(&val)), + // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`. ty::ConstKind::Error(_) => "Error".to_string(), // These variants shouldn't exist in the MIR. ty::ConstKind::Placeholder(_) @@ -490,10 +491,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { uv.promoted, ) } - // To keep the diffs small, we render this like we render `ty::Const::Value`. - // - // This changes once `ty::Const::Value` is represented using valtrees. - ConstantKind::Val(val, _) => format!("Value({})", fmt_val(&val)), + ConstantKind::Val(val, ty) => format!("Value({})", fmt_val(*val, *ty)), }; // This reflects what `Const` looked liked before `val` was renamed @@ -1090,6 +1088,7 @@ fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Resul } fn comma_sep<'tcx>( + tcx: TyCtxt<'tcx>, fmt: &mut Formatter<'_>, elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>, ) -> fmt::Result { @@ -1098,143 +1097,150 @@ fn comma_sep<'tcx>( if !first { fmt.write_str(", ")?; } - pretty_print_const_value(ct, ty, fmt)?; + pretty_print_const_value_tcx(tcx, ct, ty, fmt)?; first = false; } Ok(()) } -pub fn pretty_print_const_value<'tcx>( +fn pretty_print_const_value_tcx<'tcx>( + tcx: TyCtxt<'tcx>, ct: ConstValue<'tcx>, ty: Ty<'tcx>, fmt: &mut Formatter<'_>, ) -> fmt::Result { use crate::ty::print::PrettyPrinter; + if tcx.sess.verbose() { + fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?; + return Ok(()); + } + + let u8_type = tcx.types.u8; + match (ct, ty.kind()) { + // Byte/string slices, printed as (byte) string literals. + (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => { + if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) { + fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?; + return Ok(()); + } + } + (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => { + if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) { + pretty_print_byte_str(fmt, data)?; + return Ok(()); + } + } + (ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => { + let n = n.try_to_target_usize(tcx).unwrap(); + let alloc = tcx.global_alloc(alloc_id).unwrap_memory(); + // cast is ok because we already checked for pointer size (32 or 64 bit) above + let range = AllocRange { start: offset, size: Size::from_bytes(n) }; + let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap(); + fmt.write_str("*")?; + pretty_print_byte_str(fmt, byte_str)?; + return Ok(()); + } + // Aggregates, printed as array/tuple/struct/variant construction syntax. + // + // NB: the `has_non_region_param` check ensures that we can use + // the `destructure_const` query with an empty `ty::ParamEnv` without + // introducing ICEs (e.g. via `layout_of`) from missing bounds. + // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized` + // to be able to destructure the tuple into `(0u8, *mut T)` + (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => { + let ct = tcx.lift(ct).unwrap(); + let ty = tcx.lift(ty).unwrap(); + if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics((ct, ty)) { + let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec(); + match *ty.kind() { + ty::Array(..) => { + fmt.write_str("[")?; + comma_sep(tcx, fmt, fields)?; + fmt.write_str("]")?; + } + ty::Tuple(..) => { + fmt.write_str("(")?; + comma_sep(tcx, fmt, fields)?; + if contents.fields.len() == 1 { + fmt.write_str(",")?; + } + fmt.write_str(")")?; + } + ty::Adt(def, _) if def.variants().is_empty() => { + fmt.write_str(&format!("{{unreachable(): {ty}}}"))?; + } + ty::Adt(def, args) => { + let variant_idx = contents + .variant + .expect("destructed mir constant of adt without variant idx"); + let variant_def = &def.variant(variant_idx); + let args = tcx.lift(args).unwrap(); + let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); + cx.print_alloc_ids = true; + let cx = cx.print_value_path(variant_def.def_id, args)?; + fmt.write_str(&cx.into_buffer())?; + + match variant_def.ctor_kind() { + Some(CtorKind::Const) => {} + Some(CtorKind::Fn) => { + fmt.write_str("(")?; + comma_sep(tcx, fmt, fields)?; + fmt.write_str(")")?; + } + None => { + fmt.write_str(" {{ ")?; + let mut first = true; + for (field_def, (ct, ty)) in iter::zip(&variant_def.fields, fields) + { + if !first { + fmt.write_str(", ")?; + } + write!(fmt, "{}: ", field_def.name)?; + pretty_print_const_value_tcx(tcx, ct, ty, fmt)?; + first = false; + } + fmt.write_str(" }}")?; + } + } + } + _ => unreachable!(), + } + return Ok(()); + } + } + (ConstValue::Scalar(scalar), _) => { + let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); + cx.print_alloc_ids = true; + let ty = tcx.lift(ty).unwrap(); + cx = cx.pretty_print_const_scalar(scalar, ty)?; + fmt.write_str(&cx.into_buffer())?; + return Ok(()); + } + (ConstValue::ZeroSized, ty::FnDef(d, s)) => { + let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); + cx.print_alloc_ids = true; + let cx = cx.print_value_path(*d, s)?; + fmt.write_str(&cx.into_buffer())?; + return Ok(()); + } + // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading + // their fields instead of just dumping the memory. + _ => {} + } + // Fall back to debug pretty printing for invalid constants. + write!(fmt, "{ct:?}: {ty}") +} + +pub(crate) fn pretty_print_const_value<'tcx>( + ct: ConstValue<'tcx>, + ty: Ty<'tcx>, + fmt: &mut Formatter<'_>, +) -> fmt::Result { ty::tls::with(|tcx| { let ct = tcx.lift(ct).unwrap(); let ty = tcx.lift(ty).unwrap(); - - if tcx.sess.verbose() { - fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?; - return Ok(()); - } - - let u8_type = tcx.types.u8; - match (ct, ty.kind()) { - // Byte/string slices, printed as (byte) string literals. - (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => { - if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) { - fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?; - return Ok(()); - } - } - (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => { - if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) { - pretty_print_byte_str(fmt, data)?; - return Ok(()); - } - } - (ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => { - let n = n.try_to_target_usize(tcx).unwrap(); - let alloc = tcx.global_alloc(alloc_id).unwrap_memory(); - // cast is ok because we already checked for pointer size (32 or 64 bit) above - let range = AllocRange { start: offset, size: Size::from_bytes(n) }; - let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap(); - fmt.write_str("*")?; - pretty_print_byte_str(fmt, byte_str)?; - return Ok(()); - } - // Aggregates, printed as array/tuple/struct/variant construction syntax. - // - // NB: the `has_non_region_param` check ensures that we can use - // the `destructure_const` query with an empty `ty::ParamEnv` without - // introducing ICEs (e.g. via `layout_of`) from missing bounds. - // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized` - // to be able to destructure the tuple into `(0u8, *mut T)` - (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => { - let ct = tcx.lift(ct).unwrap(); - let ty = tcx.lift(ty).unwrap(); - if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics((ct, ty)) { - let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec(); - match *ty.kind() { - ty::Array(..) => { - fmt.write_str("[")?; - comma_sep(fmt, fields)?; - fmt.write_str("]")?; - } - ty::Tuple(..) => { - fmt.write_str("(")?; - comma_sep(fmt, fields)?; - if contents.fields.len() == 1 { - fmt.write_str(",")?; - } - fmt.write_str(")")?; - } - ty::Adt(def, _) if def.variants().is_empty() => { - fmt.write_str(&format!("{{unreachable(): {ty}}}"))?; - } - ty::Adt(def, args) => { - let variant_idx = contents - .variant - .expect("destructed mir constant of adt without variant idx"); - let variant_def = &def.variant(variant_idx); - let args = tcx.lift(args).unwrap(); - let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.print_alloc_ids = true; - let cx = cx.print_value_path(variant_def.def_id, args)?; - fmt.write_str(&cx.into_buffer())?; - - match variant_def.ctor_kind() { - Some(CtorKind::Const) => {} - Some(CtorKind::Fn) => { - fmt.write_str("(")?; - comma_sep(fmt, fields)?; - fmt.write_str(")")?; - } - None => { - fmt.write_str(" {{ ")?; - let mut first = true; - for (field_def, (ct, ty)) in - iter::zip(&variant_def.fields, fields) - { - if !first { - fmt.write_str(", ")?; - } - write!(fmt, "{}: ", field_def.name)?; - pretty_print_const_value(ct, ty, fmt)?; - first = false; - } - fmt.write_str(" }}")?; - } - } - } - _ => unreachable!(), - } - return Ok(()); - } - } - (ConstValue::Scalar(scalar), _) => { - let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.print_alloc_ids = true; - let ty = tcx.lift(ty).unwrap(); - cx = cx.pretty_print_const_scalar(scalar, ty)?; - fmt.write_str(&cx.into_buffer())?; - return Ok(()); - } - (ConstValue::ZeroSized, ty::FnDef(d, s)) => { - let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.print_alloc_ids = true; - let cx = cx.print_value_path(*d, s)?; - fmt.write_str(&cx.into_buffer())?; - return Ok(()); - } - // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading - // their fields instead of just dumping the memory. - _ => {} - } - // Fall back to debug pretty printing for invalid constants. - write!(fmt, "{ct:?}: {ty}") + pretty_print_const_value_tcx(tcx, ct, ty, fmt) }) } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index d958e25e629..0ee63e92848 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1713,7 +1713,7 @@ pub trait PrettyPrinter<'tcx>: } } -pub fn pretty_print_const<'tcx>( +pub(crate) fn pretty_print_const<'tcx>( c: ty::Const<'tcx>, fmt: &mut fmt::Formatter<'_>, print_types: bool, diff --git a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir index 1d3405a9afe..18a663d9f9e 100644 --- a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir @@ -11,7 +11,7 @@ fn outer(_1: u8) -> u8 { _0 = inner(move _2) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/spans.rs:10:5: 10:14 // mir::Constant // + span: $DIR/spans.rs:10:5: 10:10 - // + literal: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value() } + // + literal: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value(inner) } } bb1: { diff --git a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir index 285b6e41314..1c02fb72bcd 100644 --- a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir @@ -11,7 +11,7 @@ fn outer(_1: u8) -> u8 { _0 = inner(move _2) -> [return: bb1, unwind continue]; // scope 0 at $DIR/spans.rs:10:5: 10:14 // mir::Constant // + span: $DIR/spans.rs:10:5: 10:10 - // + literal: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value() } + // + literal: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value(inner) } } bb1: {