Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ty/print: pretty-print constant aggregates (arrays, tuples and ADTs). #71232

Merged
merged 3 commits into from
Apr 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions src/librustc_middle/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2242,39 +2242,41 @@ impl<'tcx> Debug for Rvalue<'tcx> {
}

Aggregate(ref kind, ref places) => {
fn fmt_tuple(fmt: &mut Formatter<'_>, places: &[Operand<'_>]) -> fmt::Result {
let mut tuple_fmt = fmt.debug_tuple("");
let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| {
let mut tuple_fmt = fmt.debug_tuple(name);
for place in places {
tuple_fmt.field(place);
}
tuple_fmt.finish()
}
};

match **kind {
AggregateKind::Array(_) => write!(fmt, "{:?}", places),

AggregateKind::Tuple => match places.len() {
0 => write!(fmt, "()"),
1 => write!(fmt, "({:?},)", places[0]),
_ => fmt_tuple(fmt, places),
},
AggregateKind::Tuple => {
if places.is_empty() {
write!(fmt, "()")
} else {
fmt_tuple(fmt, "")
}
}

AggregateKind::Adt(adt_def, variant, substs, _user_ty, _) => {
let variant_def = &adt_def.variants[variant];

let f = &mut *fmt;
ty::tls::with(|tcx| {
let name = ty::tls::with(|tcx| {
let mut name = String::new();
let substs = tcx.lift(&substs).expect("could not lift for printing");
FmtPrinter::new(tcx, f, Namespace::ValueNS)
FmtPrinter::new(tcx, &mut name, Namespace::ValueNS)
.print_def_path(variant_def.def_id, substs)?;
Ok(())
Ok(name)
})?;

match variant_def.ctor_kind {
CtorKind::Const => Ok(()),
CtorKind::Fn => fmt_tuple(fmt, places),
CtorKind::Const => fmt.write_str(&name),
CtorKind::Fn => fmt_tuple(fmt, &name),
CtorKind::Fictive => {
let mut struct_fmt = fmt.debug_struct("");
let mut struct_fmt = fmt.debug_struct(&name);
for (field, place) in variant_def.fields.iter().zip(places) {
struct_fmt.field(&field.ident.as_str(), place);
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_middle/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ rustc_queries! {
desc { "extract field of const" }
}

/// Destructure a constant ADT or array into its variant indent and its
/// Destructure a constant ADT or array into its variant index and its
/// field values.
query destructure_const(
key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>>
Expand Down
127 changes: 75 additions & 52 deletions src/librustc_middle/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rustc_apfloat::Float;
use rustc_ast::ast;
use rustc_attr::{SignedInt, UnsignedInt};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace};
use rustc_hir::def::{CtorKind, DefKind, Namespace};
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
use rustc_span::symbol::{kw, Symbol};
Expand Down Expand Up @@ -498,16 +498,9 @@ pub trait PrettyPrinter<'tcx>:
}
ty::Never => p!(write("!")),
ty::Tuple(ref tys) => {
p!(write("("));
let mut tys = tys.iter();
if let Some(&ty) = tys.next() {
p!(print(ty), write(","));
if let Some(&ty) = tys.next() {
p!(write(" "), print(ty));
for &ty in tys {
p!(write(", "), print(ty));
}
}
p!(write("("), comma_sep(tys.iter().copied()));
if tys.len() == 1 {
p!(write(","));
}
p!(write(")"))
}
Expand Down Expand Up @@ -570,15 +563,10 @@ pub trait PrettyPrinter<'tcx>:
let def_key = self.tcx().def_key(def_id);
if let Some(name) = def_key.disambiguated_data.data.get_opt_name() {
p!(write("{}", name));
let mut substs = substs.iter();
// FIXME(eddyb) print this with `print_def_path`.
if let Some(first) = substs.next() {
p!(write("::<"));
p!(print(first));
for subst in substs {
p!(write(", "), print(subst));
}
p!(write(">"));
if !substs.is_empty() {
p!(write("::"));
p!(generic_delimiters(|cx| cx.comma_sep(substs.iter().copied())));
}
return Ok(self);
}
Expand Down Expand Up @@ -854,16 +842,12 @@ pub trait PrettyPrinter<'tcx>:
) -> Result<Self, Self::Error> {
define_scoped_cx!(self);

p!(write("("));
let mut inputs = inputs.iter();
if let Some(&ty) = inputs.next() {
p!(print(ty));
for &ty in inputs {
p!(write(", "), print(ty));
}
if c_variadic {
p!(write(", ..."));
p!(write("("), comma_sep(inputs.iter().copied()));
if c_variadic {
if !inputs.is_empty() {
p!(write(", "));
}
p!(write("..."));
}
p!(write(")"));
if !output.is_unit() {
Expand Down Expand Up @@ -1053,19 +1037,6 @@ pub trait PrettyPrinter<'tcx>:
}
// For function type zsts just printing the path is enough
(Scalar::Raw { size: 0, .. }, ty::FnDef(d, s)) => p!(print_value_path(*d, s)),
// Empty tuples are frequently occurring, so don't print the fallback.
(Scalar::Raw { size: 0, .. }, ty::Tuple(ts)) if ts.is_empty() => p!(write("()")),
// Zero element arrays have a trivial representation.
(
Scalar::Raw { size: 0, .. },
ty::Array(
_,
ty::Const {
val: ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data: 0, .. })),
..
},
),
) => p!(write("[]")),
// Nontrivial types with scalar bit representation
(Scalar::Raw { data, size }, _) => {
let print = |mut this: Self| {
Expand Down Expand Up @@ -1134,14 +1105,14 @@ pub trait PrettyPrinter<'tcx>:
define_scoped_cx!(self);

if self.tcx().sess.verbose() {
p!(write("ConstValue({:?}: {:?})", ct, ty));
p!(write("ConstValue({:?}: ", ct), print(ty), write(")"));
return Ok(self);
}

let u8_type = self.tcx().types.u8;

match (ct, &ty.kind) {
(ConstValue::Scalar(scalar), _) => self.pretty_print_const_scalar(scalar, ty, print_ty),
// Byte/string slices, printed as (byte) string literals.
(
ConstValue::Slice { data, start, end },
ty::Ref(_, ty::TyS { kind: ty::Slice(t), .. }, _),
Expand Down Expand Up @@ -1175,6 +1146,66 @@ pub trait PrettyPrinter<'tcx>:
p!(pretty_print_byte_str(byte_str));
Ok(self)
}

// Aggregates, printed as array/tuple/struct/variant construction syntax.
//
// NB: the `has_param_types_or_consts` 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`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this happen at all? Const evaluation must already have had to know the type in order to produce a ConstValue. While the type may still be generic, in case of polymorphic const eval working out because of e.g. PhantomData<T> being a known ZST irrelevant of the type, any layout_of calls we do during destructure_const should also figure this out.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*mut T has a known layout irrespective of T as long as T: Sized is in the ParamEnv, that's why I used it as an example. You can use size_of::<*mut T>() as an explicit discriminant of an enum, for example.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, but here we already have a ConstValue, which must have gone through some monomorphic evaluation at some point.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, there is nothing requiring monomorphic const-eval, and I have this example to prove it (I made that to prove a point in #70453).

Copy link
Member Author

@eddyb eddyb Apr 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess your point is that the constant's type can't have type parameters? Sorry, I was getting turned around. const generics/array lengths shouldn't hit this.

So this could only be a problem with printing MIR after constant-folding something that uses type parameters but doesn't depend on them layout-wise.

Riiight, which is why I was thinking MIR printing could create a FmtPrinter with a ParamEnv.

// to be able to destructure the tuple into `(0u8, *mut T)
//
// FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the
// correct `ty::ParamEnv` to allow printing *all* constant values.
(_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_param_types_or_consts() => {
let contents = self.tcx().destructure_const(
ty::ParamEnv::reveal_all()
.and(self.tcx().mk_const(ty::Const { val: ty::ConstKind::Value(ct), ty })),
);
let fields = contents.fields.iter().copied();

match ty.kind {
ty::Array(..) => {
p!(write("["), comma_sep(fields), write("]"));
}
ty::Tuple(..) => {
p!(write("("), comma_sep(fields));
if contents.fields.len() == 1 {
p!(write(","));
}
p!(write(")"));
}
ty::Adt(def, substs) => {
let variant_def = &def.variants[contents.variant];
p!(print_value_path(variant_def.def_id, substs));

match variant_def.ctor_kind {
CtorKind::Const => {}
CtorKind::Fn => {
p!(write("("), comma_sep(fields), write(")"));
}
CtorKind::Fictive => {
p!(write(" {{ "));
let mut first = true;
for (field_def, field) in variant_def.fields.iter().zip(fields) {
if !first {
p!(write(", "));
}
p!(write("{}: ", field_def.ident), print(field));
first = false;
}
p!(write(" }}"));
}
}
}
_ => unreachable!(),
}

Ok(self)
}

(ConstValue::Scalar(scalar), _) => self.pretty_print_const_scalar(scalar, ty, print_ty),

// FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading
// their fields instead of just dumping the memory.
_ => {
Expand Down Expand Up @@ -1913,15 +1944,7 @@ define_print_and_forward_display! {
(self, cx):

&'tcx ty::List<Ty<'tcx>> {
p!(write("{{"));
let mut tys = self.iter();
if let Some(&ty) = tys.next() {
p!(print(ty));
for &ty in tys {
p!(write(", "), print(ty));
}
}
p!(write("}}"))
p!(write("{{"), comma_sep(self.iter().copied()), write("}}"))
}

ty::TypeAndMut<'tcx> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
StorageLive(_1); // bb0[0]: scope 0 at $DIR/discriminant.rs:6:9: 6:10
StorageLive(_2); // bb0[1]: scope 0 at $DIR/discriminant.rs:6:13: 6:64
StorageLive(_3); // bb0[2]: scope 0 at $DIR/discriminant.rs:6:34: 6:44
- _3 = std::option::Option::<bool>::Some(const true,); // bb0[3]: scope 0 at $DIR/discriminant.rs:6:34: 6:44
+ _3 = const {transmute(0x01): std::option::Option<bool>}; // bb0[3]: scope 0 at $DIR/discriminant.rs:6:34: 6:44
- _3 = std::option::Option::<bool>::Some(const true); // bb0[3]: scope 0 at $DIR/discriminant.rs:6:34: 6:44
+ _3 = const std::option::Option::<bool>::Some(true); // bb0[3]: scope 0 at $DIR/discriminant.rs:6:34: 6:44
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, yeah I got confused by the double-diff here... makes sense.

// ty::Const
- // + ty: bool
+ // + ty: std::option::Option<bool>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
StorageLive(_1); // bb0[0]: scope 0 at $DIR/discriminant.rs:6:9: 6:10
StorageLive(_2); // bb0[1]: scope 0 at $DIR/discriminant.rs:6:13: 6:64
StorageLive(_3); // bb0[2]: scope 0 at $DIR/discriminant.rs:6:34: 6:44
- _3 = std::option::Option::<bool>::Some(const true,); // bb0[3]: scope 0 at $DIR/discriminant.rs:6:34: 6:44
+ _3 = const {transmute(0x01): std::option::Option<bool>}; // bb0[3]: scope 0 at $DIR/discriminant.rs:6:34: 6:44
- _3 = std::option::Option::<bool>::Some(const true); // bb0[3]: scope 0 at $DIR/discriminant.rs:6:34: 6:44
+ _3 = const std::option::Option::<bool>::Some(true); // bb0[3]: scope 0 at $DIR/discriminant.rs:6:34: 6:44
// ty::Const
- // + ty: bool
+ // + ty: std::option::Option<bool>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
bb1: {
StorageLive(_5); // bb1[0]: scope 0 at $DIR/deaggregator_test_enum_2.rs:13:16: 13:17
_5 = _2; // bb1[1]: scope 0 at $DIR/deaggregator_test_enum_2.rs:13:16: 13:17
- _0 = Foo::B(move _5,); // bb1[2]: scope 0 at $DIR/deaggregator_test_enum_2.rs:13:9: 13:18
- _0 = Foo::B(move _5); // bb1[2]: scope 0 at $DIR/deaggregator_test_enum_2.rs:13:9: 13:18
- StorageDead(_5); // bb1[3]: scope 0 at $DIR/deaggregator_test_enum_2.rs:13:17: 13:18
- goto -> bb3; // bb1[4]: scope 0 at $DIR/deaggregator_test_enum_2.rs:10:5: 14:6
+ ((_0 as B).0: i32) = move _5; // bb1[2]: scope 0 at $DIR/deaggregator_test_enum_2.rs:13:9: 13:18
Expand All @@ -30,7 +30,7 @@
bb2: {
StorageLive(_4); // bb2[0]: scope 0 at $DIR/deaggregator_test_enum_2.rs:11:16: 11:17
_4 = _2; // bb2[1]: scope 0 at $DIR/deaggregator_test_enum_2.rs:11:16: 11:17
- _0 = Foo::A(move _4,); // bb2[2]: scope 0 at $DIR/deaggregator_test_enum_2.rs:11:9: 11:18
- _0 = Foo::A(move _4); // bb2[2]: scope 0 at $DIR/deaggregator_test_enum_2.rs:11:9: 11:18
- StorageDead(_4); // bb2[3]: scope 0 at $DIR/deaggregator_test_enum_2.rs:11:17: 11:18
- goto -> bb3; // bb2[4]: scope 0 at $DIR/deaggregator_test_enum_2.rs:10:5: 14:6
+ ((_0 as A).0: i32) = move _4; // bb2[2]: scope 0 at $DIR/deaggregator_test_enum_2.rs:11:9: 11:18
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
StorageLive(_2); // bb0[0]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:6: 10:15
StorageLive(_3); // bb0[1]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:13: 10:14
_3 = _1; // bb0[2]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:13: 10:14
- _2 = Foo::A(move _3,); // bb0[3]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:6: 10:15
- _2 = Foo::A(move _3); // bb0[3]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:6: 10:15
- StorageDead(_3); // bb0[4]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:14: 10:15
- StorageLive(_4); // bb0[5]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:17: 10:26
- StorageLive(_5); // bb0[6]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:24: 10:25
- _5 = _1; // bb0[7]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:24: 10:25
- _4 = Foo::A(move _5,); // bb0[8]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:17: 10:26
- _4 = Foo::A(move _5); // bb0[8]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:17: 10:26
- StorageDead(_5); // bb0[9]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:25: 10:26
- _0 = [move _2, move _4]; // bb0[10]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:5: 10:27
- StorageDead(_4); // bb0[11]: scope 0 at $DIR/deaggregator_test_multiple.rs:10:26: 10:27
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ yields ()

bb0: {
StorageLive(_3); // bb0[0]: scope 0 at $DIR/generator-storage-dead-unwind.rs:23:13: 23:14
_3 = Foo(const 5i32,); // bb0[1]: scope 0 at $DIR/generator-storage-dead-unwind.rs:23:17: 23:23
_3 = Foo(const 5i32); // bb0[1]: scope 0 at $DIR/generator-storage-dead-unwind.rs:23:17: 23:23
// ty::Const
// + ty: i32
// + val: Value(Scalar(0x00000005))
// mir::Constant
// + span: $DIR/generator-storage-dead-unwind.rs:23:21: 23:22
// + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) }
StorageLive(_4); // bb0[2]: scope 1 at $DIR/generator-storage-dead-unwind.rs:24:13: 24:14
_4 = Bar(const 6i32,); // bb0[3]: scope 1 at $DIR/generator-storage-dead-unwind.rs:24:17: 24:23
_4 = Bar(const 6i32); // bb0[3]: scope 1 at $DIR/generator-storage-dead-unwind.rs:24:17: 24:23
// ty::Const
// + ty: i32
// + val: Value(Scalar(0x00000006))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn main::{{closure}}#0(_1: std::pin::Pin<&mut [generator@$DIR/generator-tiny.rs:
StorageLive(_6); // bb2[0]: scope 1 at $DIR/generator-tiny.rs:21:13: 21:18
StorageLive(_7); // bb2[1]: scope 1 at $DIR/generator-tiny.rs:21:13: 21:18
_7 = (); // bb2[2]: scope 1 at $DIR/generator-tiny.rs:21:13: 21:18
_0 = std::ops::GeneratorState::<(), ()>::Yielded(move _7,); // bb2[3]: scope 1 at $DIR/generator-tiny.rs:21:13: 21:18
_0 = std::ops::GeneratorState::<(), ()>::Yielded(move _7); // bb2[3]: scope 1 at $DIR/generator-tiny.rs:21:13: 21:18
discriminant((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:18:16: 24:6 {u8, HasDrop, ()}]))) = 3; // bb2[4]: scope 1 at $DIR/generator-tiny.rs:21:13: 21:18
return; // bb2[5]: scope 1 at $DIR/generator-tiny.rs:21:13: 21:18
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
_2 = Box(std::vec::Vec<u32>); // bb0[2]: scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43
- (*_2) = const std::vec::Vec::<u32>::new() -> [return: bb2, unwind: bb4]; // bb0[3]: scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43
+ _4 = &mut (*_2); // bb0[3]: scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43
+ ((*_4).0: alloc::raw_vec::RawVec<u32>) = const ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), undef_mask: UndefMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }: alloc::raw_vec::RawVec::<u32>; // bb0[4]: scope 2 at $SRC_DIR/liballoc/vec.rs:LL:COL
+ ((*_4).0: alloc::raw_vec::RawVec<u32>) = const alloc::raw_vec::RawVec::<u32> { ptr: std::ptr::Unique::<u32> { pointer: {0x4 as *const u32}, _marker: std::marker::PhantomData::<u32> }, cap: 0usize, alloc: std::alloc::Global }; // bb0[4]: scope 2 at $SRC_DIR/liballoc/vec.rs:LL:COL
// ty::Const
- // + ty: fn() -> std::vec::Vec<u32> {std::vec::Vec::<u32>::new}
- // + val: Value(Scalar(<ZST>))
Expand Down
Loading