Skip to content

Commit 993deaa

Browse files
committed
Auto merge of #112984 - BoxyUwU:debug_with_infcx, r=compiler-errors
Introduce `trait DebugWithInfcx` to debug format types with universe info Seeing universes of infer vars is valuable for debugging but currently we have no way of easily debug formatting a type with the universes of all the infer vars shown. In the future I hope to augment the new solver's proof tree output with a `DebugWithInfcx` impl so that it can show universes but I left that out of this PR as it would be non trivial and this is already large and complex enough. The goal here is to make the various abstractions taking `T: Debug` able to use the codepath for printing out universes, that way we can do `debug!("{:?}", my_x)` and have `my_x` have universes shown, same for the `write!` macro. It's not possible to put the `Infcx: InferCtxtLike<I>` into the formatter argument to `Debug::fmt` so it has to go into the self ty. For this we introduce the type `OptWithInfcx<I: Interner, Infcx: InferCtxtLike<I>, T>` which has the data `T` optionally coupled with the infcx (more on why it's optional later). Because of coherence/orphan rules it's not possible to write the impl `Debug for OptWithInfcx<..., MyType>` when `OptWithInfcx` is in a upstream crate. This necessitates a blanket impl in the crate defining `OptWithInfcx` like so: `impl<T: DebugWithInfcx> Debug for OptWithInfcx<..., T>`. It is not intended for people to manually call `DebugWithInfcx::fmt`, the `Debug` impl for `OptWithInfcx` should be preferred. The infcx has to be optional in `OptWithInfcx` as otherwise we would end up with a large amount of code duplication. Almost all types that want to be used with `OptWithInfcx` do not themselves need access to the infcx so if we were to not optional we would end up with large `Debug` and `DebugWithInfcx` impls that were practically identical other than that when formatting their fields we wrap the field in `OptWithInfcx` instead of formatting it alone. The only types that need access to the infcx themselves are ty/const/region infer vars, everything else is implemented by having the `Debug` impl defer to `OptWithInfcx` with no infcx available. The `DebugWithInfcx` impl is pretty much just the standard `Debug` impl except that instead of recursively formatting fields with `write!(f, "{x:?}")` we must do `write!(f, "{:?}", opt_infcx.wrap(x))`. This is some pretty rough boilerplate but I could not think of an alternative unfortunately. `OptWithInfcx::wrap` is an eager `Option::map` because 99% of callsites were discarding the existing data in `OptWithInfcx` and did not need lazy evaluation. A trait `InferCtxtLike` was added instead of using `InferCtxt<'tcx>` as we need to implement `DebugWithInfcx` for types living in `rustc_type_ir` which are generic over an interner and do not have access to `InferCtxt` since it lives in `rustc_infer`. Additionally I suspect that adding universe info to new solver proof tree output will require an implementation of `InferCtxtLike` for something that is not an `InferCtxt` although this is not the primary motivaton. --- To summarize: - There is a type `OptWithInfcx` which bundles some data optionally with an infcx with allows us to pass an infcx into a `Debug` impl. It's optional instead of being there unconditionally so that we can share code for `Debug` and `DebugWithInfcx` impls that don't care about whether there is an infcx available but have fields that might care. - There is a trait `DebugWithInfcx` which allows downstream crates to add impls of the form `Debug for OptWithInfcx<...>` which would normally be forbidden by orphan rules/coherence. - There is a trait `InferCtxtLike` to allow us to implement `DebugWithInfcx` for types that live in `rustc_type_ir` This allows debug formatting various `ty::*` structures with universes shown by using the `Debug` impl for `OptWithInfcx::new(ty, infcx)` --- This PR does not add `DebugWithInfcx` impls to absolutely _everything_ that should realistically have them, for example you cannot use `OptWithInfcx<Obligation<Predicate>>`. I am leaving this to a future PR to do so as it would likely be a lot more work to do.
2 parents 48a814d + 3fdb443 commit 993deaa

File tree

10 files changed

+464
-95
lines changed

10 files changed

+464
-95
lines changed

compiler/rustc_infer/src/infer/mod.rs

+38
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,39 @@ pub struct InferCtxt<'tcx> {
332332
next_trait_solver: bool,
333333
}
334334

335+
impl<'tcx> ty::InferCtxtLike<TyCtxt<'tcx>> for InferCtxt<'tcx> {
336+
fn universe_of_ty(&self, ty: ty::InferTy) -> Option<ty::UniverseIndex> {
337+
use InferTy::*;
338+
match ty {
339+
// FIXME(BoxyUwU): this is kind of jank and means that printing unresolved
340+
// ty infers will give you the universe of the var it resolved to not the universe
341+
// it actually had. It also means that if you have a `?0.1` and infer it to `u8` then
342+
// try to print out `?0.1` it will just print `?0`.
343+
TyVar(ty_vid) => match self.probe_ty_var(ty_vid) {
344+
Err(universe) => Some(universe),
345+
Ok(_) => None,
346+
},
347+
IntVar(_) | FloatVar(_) | FreshTy(_) | FreshIntTy(_) | FreshFloatTy(_) => None,
348+
}
349+
}
350+
351+
fn universe_of_ct(&self, ct: ty::InferConst<'tcx>) -> Option<ty::UniverseIndex> {
352+
use ty::InferConst::*;
353+
match ct {
354+
// Same issue as with `universe_of_ty`
355+
Var(ct_vid) => match self.probe_const_var(ct_vid) {
356+
Err(universe) => Some(universe),
357+
Ok(_) => None,
358+
},
359+
Fresh(_) => None,
360+
}
361+
}
362+
363+
fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex> {
364+
Some(self.universe_of_region_vid(lt))
365+
}
366+
}
367+
335368
/// See the `error_reporting` module for more details.
336369
#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)]
337370
pub enum ValuePairs<'tcx> {
@@ -1068,6 +1101,11 @@ impl<'tcx> InferCtxt<'tcx> {
10681101
self.inner.borrow_mut().unwrap_region_constraints().universe(r)
10691102
}
10701103

1104+
/// Return the universe that the region variable `r` was created in.
1105+
pub fn universe_of_region_vid(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
1106+
self.inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
1107+
}
1108+
10711109
/// Number of region variables created so far.
10721110
pub fn num_region_vars(&self) -> usize {
10731111
self.inner.borrow_mut().unwrap_region_constraints().num_region_vars()

compiler/rustc_middle/src/ty/consts/kind.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_hir::def_id::DefId;
88
use rustc_macros::HashStable;
99

1010
/// An unevaluated (potentially generic) constant used in the type-system.
11-
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
11+
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
1212
#[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
1313
pub struct UnevaluatedConst<'tcx> {
1414
pub def: DefId,
@@ -35,7 +35,7 @@ impl<'tcx> UnevaluatedConst<'tcx> {
3535
}
3636
}
3737

38-
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
38+
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
3939
#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
4040
pub enum Expr<'tcx> {
4141
Binop(mir::BinOp, Const<'tcx>, Const<'tcx>),

compiler/rustc_middle/src/ty/list.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::arena::Arena;
22
use rustc_data_structures::aligned::{align_of, Aligned};
33
use rustc_serialize::{Encodable, Encoder};
4+
use rustc_type_ir::{InferCtxtLike, OptWithInfcx};
45
use std::alloc::Layout;
56
use std::cmp::Ordering;
67
use std::fmt;
@@ -119,6 +120,14 @@ impl<T: fmt::Debug> fmt::Debug for List<T> {
119120
(**self).fmt(f)
120121
}
121122
}
123+
impl<'tcx, T: super::DebugWithInfcx<TyCtxt<'tcx>>> super::DebugWithInfcx<TyCtxt<'tcx>> for List<T> {
124+
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
125+
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
126+
f: &mut core::fmt::Formatter<'_>,
127+
) -> core::fmt::Result {
128+
fmt::Debug::fmt(&this.map(|this| this.as_slice()), f)
129+
}
130+
}
122131

123132
impl<S: Encoder, T: Encodable<S>> Encodable<S> for List<T> {
124133
#[inline]
@@ -202,6 +211,8 @@ unsafe impl<T: Sync> Sync for List<T> {}
202211
// We need this since `List` uses extern type `OpaqueListContents`.
203212
#[cfg(parallel_compiler)]
204213
use rustc_data_structures::sync::DynSync;
214+
215+
use super::TyCtxt;
205216
#[cfg(parallel_compiler)]
206217
unsafe impl<T: DynSync> DynSync for List<T> {}
207218

compiler/rustc_middle/src/ty/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
5353
use rustc_span::{ExpnId, ExpnKind, Span};
5454
use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx};
5555
pub use rustc_target::abi::{ReprFlags, ReprOptions};
56+
pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, OptWithInfcx};
5657
pub use subst::*;
5758
pub use vtable::*;
5859

compiler/rustc_middle/src/ty/structural_impls.rs

+179-12
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ use crate::ty::{self, AliasTy, InferConst, Lift, Term, TermKind, Ty, TyCtxt};
1111
use rustc_hir::def::Namespace;
1212
use rustc_index::{Idx, IndexVec};
1313
use rustc_target::abi::TyAndLayout;
14-
use rustc_type_ir::ConstKind;
14+
use rustc_type_ir::{ConstKind, DebugWithInfcx, InferCtxtLike, OptWithInfcx};
1515

16-
use std::fmt;
16+
use std::fmt::{self, Debug};
1717
use std::ops::ControlFlow;
1818
use std::rc::Rc;
1919
use std::sync::Arc;
2020

21+
use super::{GenericArg, GenericArgKind, Region};
22+
2123
impl fmt::Debug for ty::TraitDef {
2224
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2325
ty::tls::with(|tcx| {
@@ -89,7 +91,16 @@ impl fmt::Debug for ty::FreeRegion {
8991

9092
impl<'tcx> fmt::Debug for ty::FnSig<'tcx> {
9193
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92-
let ty::FnSig { inputs_and_output: _, c_variadic, unsafety, abi } = self;
94+
OptWithInfcx::new_no_ctx(self).fmt(f)
95+
}
96+
}
97+
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::FnSig<'tcx> {
98+
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
99+
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
100+
f: &mut core::fmt::Formatter<'_>,
101+
) -> core::fmt::Result {
102+
let sig = this.data;
103+
let ty::FnSig { inputs_and_output: _, c_variadic, unsafety, abi } = sig;
93104

94105
write!(f, "{}", unsafety.prefix_str())?;
95106
match abi {
@@ -98,25 +109,25 @@ impl<'tcx> fmt::Debug for ty::FnSig<'tcx> {
98109
};
99110

100111
write!(f, "fn(")?;
101-
let inputs = self.inputs();
112+
let inputs = sig.inputs();
102113
match inputs.len() {
103114
0 if *c_variadic => write!(f, "...)")?,
104115
0 => write!(f, ")")?,
105116
_ => {
106-
for ty in &self.inputs()[0..(self.inputs().len() - 1)] {
107-
write!(f, "{ty:?}, ")?;
117+
for ty in &sig.inputs()[0..(sig.inputs().len() - 1)] {
118+
write!(f, "{:?}, ", &this.wrap(ty))?;
108119
}
109-
write!(f, "{:?}", self.inputs().last().unwrap())?;
120+
write!(f, "{:?}", &this.wrap(sig.inputs().last().unwrap()))?;
110121
if *c_variadic {
111122
write!(f, "...")?;
112123
}
113124
write!(f, ")")?;
114125
}
115126
}
116127

117-
match self.output().kind() {
128+
match sig.output().kind() {
118129
ty::Tuple(list) if list.is_empty() => Ok(()),
119-
_ => write!(f, " -> {:?}", self.output()),
130+
_ => write!(f, " -> {:?}", &this.wrap(sig.output())),
120131
}
121132
}
122133
}
@@ -133,6 +144,14 @@ impl<'tcx> fmt::Debug for ty::TraitRef<'tcx> {
133144
}
134145
}
135146

147+
impl<'tcx> ty::DebugWithInfcx<TyCtxt<'tcx>> for Ty<'tcx> {
148+
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
149+
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
150+
f: &mut core::fmt::Formatter<'_>,
151+
) -> core::fmt::Result {
152+
this.data.fmt(f)
153+
}
154+
}
136155
impl<'tcx> fmt::Debug for Ty<'tcx> {
137156
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138157
with_no_trimmed_paths!(fmt::Display::fmt(self, f))
@@ -217,9 +236,17 @@ impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> {
217236

218237
impl<'tcx> fmt::Debug for AliasTy<'tcx> {
219238
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239+
OptWithInfcx::new_no_ctx(self).fmt(f)
240+
}
241+
}
242+
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for AliasTy<'tcx> {
243+
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
244+
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
245+
f: &mut core::fmt::Formatter<'_>,
246+
) -> core::fmt::Result {
220247
f.debug_struct("AliasTy")
221-
.field("substs", &self.substs)
222-
.field("def_id", &self.def_id)
248+
.field("substs", &this.map(|data| data.substs))
249+
.field("def_id", &this.data.def_id)
223250
.finish()
224251
}
225252
}
@@ -232,13 +259,93 @@ impl<'tcx> fmt::Debug for ty::InferConst<'tcx> {
232259
}
233260
}
234261
}
262+
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::InferConst<'tcx> {
263+
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
264+
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
265+
f: &mut core::fmt::Formatter<'_>,
266+
) -> core::fmt::Result {
267+
use ty::InferConst::*;
268+
match this.infcx.and_then(|infcx| infcx.universe_of_ct(*this.data)) {
269+
None => write!(f, "{:?}", this.data),
270+
Some(universe) => match *this.data {
271+
Var(vid) => write!(f, "?{}_{}c", vid.index, universe.index()),
272+
Fresh(_) => {
273+
unreachable!()
274+
}
275+
},
276+
}
277+
}
278+
}
279+
280+
impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> {
281+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282+
OptWithInfcx::new_no_ctx(self).fmt(f)
283+
}
284+
}
285+
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::consts::Expr<'tcx> {
286+
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
287+
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
288+
f: &mut core::fmt::Formatter<'_>,
289+
) -> core::fmt::Result {
290+
match this.data {
291+
ty::Expr::Binop(op, lhs, rhs) => {
292+
write!(f, "({op:?}: {:?}, {:?})", &this.wrap(lhs), &this.wrap(rhs))
293+
}
294+
ty::Expr::UnOp(op, rhs) => write!(f, "({op:?}: {:?})", &this.wrap(rhs)),
295+
ty::Expr::FunctionCall(func, args) => {
296+
write!(f, "{:?}(", &this.wrap(func))?;
297+
for arg in args.as_slice().iter().rev().skip(1).rev() {
298+
write!(f, "{:?}, ", &this.wrap(arg))?;
299+
}
300+
if let Some(arg) = args.last() {
301+
write!(f, "{:?}", &this.wrap(arg))?;
302+
}
303+
304+
write!(f, ")")
305+
}
306+
ty::Expr::Cast(cast_kind, lhs, rhs) => {
307+
write!(f, "({cast_kind:?}: {:?}, {:?})", &this.wrap(lhs), &this.wrap(rhs))
308+
}
309+
}
310+
}
311+
}
312+
313+
impl<'tcx> fmt::Debug for ty::UnevaluatedConst<'tcx> {
314+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315+
OptWithInfcx::new_no_ctx(self).fmt(f)
316+
}
317+
}
318+
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::UnevaluatedConst<'tcx> {
319+
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
320+
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
321+
f: &mut core::fmt::Formatter<'_>,
322+
) -> core::fmt::Result {
323+
f.debug_struct("UnevaluatedConst")
324+
.field("def", &this.data.def)
325+
.field("substs", &this.wrap(this.data.substs))
326+
.finish()
327+
}
328+
}
235329

236330
impl<'tcx> fmt::Debug for ty::Const<'tcx> {
237331
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332+
OptWithInfcx::new_no_ctx(self).fmt(f)
333+
}
334+
}
335+
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::Const<'tcx> {
336+
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
337+
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
338+
f: &mut core::fmt::Formatter<'_>,
339+
) -> core::fmt::Result {
238340
// This reflects what `Const` looked liked before `Interned` was
239341
// introduced. We print it like this to avoid having to update expected
240342
// output in a lot of tests.
241-
write!(f, "Const {{ ty: {:?}, kind: {:?} }}", self.ty(), self.kind())
343+
write!(
344+
f,
345+
"Const {{ ty: {:?}, kind: {:?} }}",
346+
&this.map(|data| data.ty()),
347+
&this.map(|data| data.kind())
348+
)
242349
}
243350
}
244351

@@ -261,6 +368,66 @@ impl<T: fmt::Debug> fmt::Debug for ty::Placeholder<T> {
261368
}
262369
}
263370

371+
impl<'tcx> fmt::Debug for GenericArg<'tcx> {
372+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373+
match self.unpack() {
374+
GenericArgKind::Lifetime(lt) => lt.fmt(f),
375+
GenericArgKind::Type(ty) => ty.fmt(f),
376+
GenericArgKind::Const(ct) => ct.fmt(f),
377+
}
378+
}
379+
}
380+
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for GenericArg<'tcx> {
381+
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
382+
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
383+
f: &mut core::fmt::Formatter<'_>,
384+
) -> core::fmt::Result {
385+
match this.data.unpack() {
386+
GenericArgKind::Lifetime(lt) => write!(f, "{:?}", &this.wrap(lt)),
387+
GenericArgKind::Const(ct) => write!(f, "{:?}", &this.wrap(ct)),
388+
GenericArgKind::Type(ty) => write!(f, "{:?}", &this.wrap(ty)),
389+
}
390+
}
391+
}
392+
393+
impl<'tcx> fmt::Debug for Region<'tcx> {
394+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395+
write!(f, "{:?}", self.kind())
396+
}
397+
}
398+
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for Region<'tcx> {
399+
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
400+
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
401+
f: &mut core::fmt::Formatter<'_>,
402+
) -> core::fmt::Result {
403+
write!(f, "{:?}", &this.map(|data| data.kind()))
404+
}
405+
}
406+
407+
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::RegionVid {
408+
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
409+
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
410+
f: &mut core::fmt::Formatter<'_>,
411+
) -> core::fmt::Result {
412+
match this.infcx.and_then(|infcx| infcx.universe_of_lt(*this.data)) {
413+
Some(universe) => write!(f, "'?{}_{}", this.data.index(), universe.index()),
414+
None => write!(f, "{:?}", this.data),
415+
}
416+
}
417+
}
418+
419+
impl<'tcx, T: DebugWithInfcx<TyCtxt<'tcx>>> DebugWithInfcx<TyCtxt<'tcx>> for ty::Binder<'tcx, T> {
420+
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
421+
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
422+
f: &mut core::fmt::Formatter<'_>,
423+
) -> core::fmt::Result {
424+
f.debug_tuple("Binder")
425+
.field(&this.map(|data| data.as_ref().skip_binder()))
426+
.field(&this.data.bound_vars())
427+
.finish()
428+
}
429+
}
430+
264431
///////////////////////////////////////////////////////////////////////////
265432
// Atomic structs
266433
//

0 commit comments

Comments
 (0)