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.
This commit is contained in:
bors 2023-07-11 20:54:00 +00:00
commit 993deaa0bf
10 changed files with 474 additions and 105 deletions

View File

@ -332,6 +332,39 @@ pub struct InferCtxt<'tcx> {
next_trait_solver: bool,
}
impl<'tcx> ty::InferCtxtLike<TyCtxt<'tcx>> for InferCtxt<'tcx> {
fn universe_of_ty(&self, ty: ty::InferTy) -> Option<ty::UniverseIndex> {
use InferTy::*;
match ty {
// FIXME(BoxyUwU): this is kind of jank and means that printing unresolved
// ty infers will give you the universe of the var it resolved to not the universe
// it actually had. It also means that if you have a `?0.1` and infer it to `u8` then
// try to print out `?0.1` it will just print `?0`.
TyVar(ty_vid) => match self.probe_ty_var(ty_vid) {
Err(universe) => Some(universe),
Ok(_) => None,
},
IntVar(_) | FloatVar(_) | FreshTy(_) | FreshIntTy(_) | FreshFloatTy(_) => None,
}
}
fn universe_of_ct(&self, ct: ty::InferConst<'tcx>) -> Option<ty::UniverseIndex> {
use ty::InferConst::*;
match ct {
// Same issue as with `universe_of_ty`
Var(ct_vid) => match self.probe_const_var(ct_vid) {
Err(universe) => Some(universe),
Ok(_) => None,
},
Fresh(_) => None,
}
}
fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex> {
Some(self.universe_of_region_vid(lt))
}
}
/// See the `error_reporting` module for more details.
#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)]
pub enum ValuePairs<'tcx> {
@ -1068,6 +1101,11 @@ impl<'tcx> InferCtxt<'tcx> {
self.inner.borrow_mut().unwrap_region_constraints().universe(r)
}
/// Return the universe that the region variable `r` was created in.
pub fn universe_of_region_vid(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
self.inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
}
/// Number of region variables created so far.
pub fn num_region_vars(&self) -> usize {
self.inner.borrow_mut().unwrap_region_constraints().num_region_vars()

View File

@ -8,7 +8,7 @@ use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
/// An unevaluated (potentially generic) constant used in the type-system.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
#[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
pub struct UnevaluatedConst<'tcx> {
pub def: DefId,
@ -35,7 +35,7 @@ impl<'tcx> UnevaluatedConst<'tcx> {
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
pub enum Expr<'tcx> {
Binop(mir::BinOp, Const<'tcx>, Const<'tcx>),

View File

@ -1,6 +1,7 @@
use crate::arena::Arena;
use rustc_data_structures::aligned::{align_of, Aligned};
use rustc_serialize::{Encodable, Encoder};
use rustc_type_ir::{InferCtxtLike, OptWithInfcx};
use std::alloc::Layout;
use std::cmp::Ordering;
use std::fmt;
@ -119,6 +120,14 @@ impl<T: fmt::Debug> fmt::Debug for List<T> {
(**self).fmt(f)
}
}
impl<'tcx, T: super::DebugWithInfcx<TyCtxt<'tcx>>> super::DebugWithInfcx<TyCtxt<'tcx>> for List<T> {
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
fmt::Debug::fmt(&this.map(|this| this.as_slice()), f)
}
}
impl<S: Encoder, T: Encodable<S>> Encodable<S> for List<T> {
#[inline]
@ -202,6 +211,8 @@ unsafe impl<T: Sync> Sync for List<T> {}
// We need this since `List` uses extern type `OpaqueListContents`.
#[cfg(parallel_compiler)]
use rustc_data_structures::sync::DynSync;
use super::TyCtxt;
#[cfg(parallel_compiler)]
unsafe impl<T: DynSync> DynSync for List<T> {}

View File

@ -53,6 +53,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{ExpnId, ExpnKind, Span};
use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx};
pub use rustc_target::abi::{ReprFlags, ReprOptions};
pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, OptWithInfcx};
pub use subst::*;
pub use vtable::*;

View File

@ -11,13 +11,15 @@ use crate::ty::{self, AliasTy, InferConst, Lift, Term, TermKind, Ty, TyCtxt};
use rustc_hir::def::Namespace;
use rustc_index::{Idx, IndexVec};
use rustc_target::abi::TyAndLayout;
use rustc_type_ir::ConstKind;
use rustc_type_ir::{ConstKind, DebugWithInfcx, InferCtxtLike, OptWithInfcx};
use std::fmt;
use std::fmt::{self, Debug};
use std::ops::ControlFlow;
use std::rc::Rc;
use std::sync::Arc;
use super::{GenericArg, GenericArgKind, Region};
impl fmt::Debug for ty::TraitDef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ty::tls::with(|tcx| {
@ -89,7 +91,16 @@ impl fmt::Debug for ty::FreeRegion {
impl<'tcx> fmt::Debug for ty::FnSig<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ty::FnSig { inputs_and_output: _, c_variadic, unsafety, abi } = self;
OptWithInfcx::new_no_ctx(self).fmt(f)
}
}
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::FnSig<'tcx> {
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
let sig = this.data;
let ty::FnSig { inputs_and_output: _, c_variadic, unsafety, abi } = sig;
write!(f, "{}", unsafety.prefix_str())?;
match abi {
@ -98,15 +109,15 @@ impl<'tcx> fmt::Debug for ty::FnSig<'tcx> {
};
write!(f, "fn(")?;
let inputs = self.inputs();
let inputs = sig.inputs();
match inputs.len() {
0 if *c_variadic => write!(f, "...)")?,
0 => write!(f, ")")?,
_ => {
for ty in &self.inputs()[0..(self.inputs().len() - 1)] {
write!(f, "{ty:?}, ")?;
for ty in &sig.inputs()[0..(sig.inputs().len() - 1)] {
write!(f, "{:?}, ", &this.wrap(ty))?;
}
write!(f, "{:?}", self.inputs().last().unwrap())?;
write!(f, "{:?}", &this.wrap(sig.inputs().last().unwrap()))?;
if *c_variadic {
write!(f, "...")?;
}
@ -114,9 +125,9 @@ impl<'tcx> fmt::Debug for ty::FnSig<'tcx> {
}
}
match self.output().kind() {
match sig.output().kind() {
ty::Tuple(list) if list.is_empty() => Ok(()),
_ => write!(f, " -> {:?}", self.output()),
_ => write!(f, " -> {:?}", &this.wrap(sig.output())),
}
}
}
@ -133,6 +144,14 @@ impl<'tcx> fmt::Debug for ty::TraitRef<'tcx> {
}
}
impl<'tcx> ty::DebugWithInfcx<TyCtxt<'tcx>> for Ty<'tcx> {
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
this.data.fmt(f)
}
}
impl<'tcx> fmt::Debug for Ty<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
with_no_trimmed_paths!(fmt::Display::fmt(self, f))
@ -217,9 +236,17 @@ impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> {
impl<'tcx> fmt::Debug for AliasTy<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
OptWithInfcx::new_no_ctx(self).fmt(f)
}
}
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for AliasTy<'tcx> {
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
f.debug_struct("AliasTy")
.field("substs", &self.substs)
.field("def_id", &self.def_id)
.field("substs", &this.map(|data| data.substs))
.field("def_id", &this.data.def_id)
.finish()
}
}
@ -232,13 +259,93 @@ impl<'tcx> fmt::Debug for ty::InferConst<'tcx> {
}
}
}
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::InferConst<'tcx> {
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
use ty::InferConst::*;
match this.infcx.and_then(|infcx| infcx.universe_of_ct(*this.data)) {
None => write!(f, "{:?}", this.data),
Some(universe) => match *this.data {
Var(vid) => write!(f, "?{}_{}c", vid.index, universe.index()),
Fresh(_) => {
unreachable!()
}
},
}
}
}
impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
OptWithInfcx::new_no_ctx(self).fmt(f)
}
}
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::consts::Expr<'tcx> {
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
match this.data {
ty::Expr::Binop(op, lhs, rhs) => {
write!(f, "({op:?}: {:?}, {:?})", &this.wrap(lhs), &this.wrap(rhs))
}
ty::Expr::UnOp(op, rhs) => write!(f, "({op:?}: {:?})", &this.wrap(rhs)),
ty::Expr::FunctionCall(func, args) => {
write!(f, "{:?}(", &this.wrap(func))?;
for arg in args.as_slice().iter().rev().skip(1).rev() {
write!(f, "{:?}, ", &this.wrap(arg))?;
}
if let Some(arg) = args.last() {
write!(f, "{:?}", &this.wrap(arg))?;
}
write!(f, ")")
}
ty::Expr::Cast(cast_kind, lhs, rhs) => {
write!(f, "({cast_kind:?}: {:?}, {:?})", &this.wrap(lhs), &this.wrap(rhs))
}
}
}
}
impl<'tcx> fmt::Debug for ty::UnevaluatedConst<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
OptWithInfcx::new_no_ctx(self).fmt(f)
}
}
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::UnevaluatedConst<'tcx> {
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
f.debug_struct("UnevaluatedConst")
.field("def", &this.data.def)
.field("substs", &this.wrap(this.data.substs))
.finish()
}
}
impl<'tcx> fmt::Debug for ty::Const<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
OptWithInfcx::new_no_ctx(self).fmt(f)
}
}
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::Const<'tcx> {
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
// This reflects what `Const` looked liked before `Interned` was
// introduced. We print it like this to avoid having to update expected
// output in a lot of tests.
write!(f, "Const {{ ty: {:?}, kind: {:?} }}", self.ty(), self.kind())
write!(
f,
"Const {{ ty: {:?}, kind: {:?} }}",
&this.map(|data| data.ty()),
&this.map(|data| data.kind())
)
}
}
@ -261,6 +368,66 @@ impl<T: fmt::Debug> fmt::Debug for ty::Placeholder<T> {
}
}
impl<'tcx> fmt::Debug for GenericArg<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.unpack() {
GenericArgKind::Lifetime(lt) => lt.fmt(f),
GenericArgKind::Type(ty) => ty.fmt(f),
GenericArgKind::Const(ct) => ct.fmt(f),
}
}
}
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for GenericArg<'tcx> {
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
match this.data.unpack() {
GenericArgKind::Lifetime(lt) => write!(f, "{:?}", &this.wrap(lt)),
GenericArgKind::Const(ct) => write!(f, "{:?}", &this.wrap(ct)),
GenericArgKind::Type(ty) => write!(f, "{:?}", &this.wrap(ty)),
}
}
}
impl<'tcx> fmt::Debug for Region<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.kind())
}
}
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for Region<'tcx> {
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
write!(f, "{:?}", &this.map(|data| data.kind()))
}
}
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::RegionVid {
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
match this.infcx.and_then(|infcx| infcx.universe_of_lt(*this.data)) {
Some(universe) => write!(f, "'?{}_{}", this.data.index(), universe.index()),
None => write!(f, "{:?}", this.data),
}
}
}
impl<'tcx, T: DebugWithInfcx<TyCtxt<'tcx>>> DebugWithInfcx<TyCtxt<'tcx>> for ty::Binder<'tcx, T> {
fn fmt<InfCtx: InferCtxtLike<TyCtxt<'tcx>>>(
this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
f.debug_tuple("Binder")
.field(&this.map(|data| data.as_ref().skip_binder()))
.field(&this.data.bound_vars())
.finish()
}
}
///////////////////////////////////////////////////////////////////////////
// Atomic structs
//

View File

@ -35,9 +35,12 @@ use std::ops::{ControlFlow, Deref, Range};
use ty::util::IntTypeExt;
use rustc_type_ir::sty::TyKind::*;
use rustc_type_ir::CollectAndApply;
use rustc_type_ir::ConstKind as IrConstKind;
use rustc_type_ir::DebugWithInfcx;
use rustc_type_ir::DynKind;
use rustc_type_ir::RegionKind as IrRegionKind;
use rustc_type_ir::TyKind as IrTyKind;
use rustc_type_ir::{CollectAndApply, ConstKind as IrConstKind};
use rustc_type_ir::{DynKind, RegionKind as IrRegionKind};
use super::GenericParamDefKind;
@ -694,6 +697,15 @@ pub enum ExistentialPredicate<'tcx> {
AutoTrait(DefId),
}
impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ExistentialPredicate<'tcx> {
fn fmt<InfCtx: rustc_type_ir::InferCtxtLike<TyCtxt<'tcx>>>(
this: rustc_type_ir::OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
fmt::Debug::fmt(&this.data, f)
}
}
impl<'tcx> ExistentialPredicate<'tcx> {
/// Compares via an ordering that will not change if modules are reordered or other changes are
/// made to the tree. In particular, this ordering is preserved across incremental compilations.
@ -1571,12 +1583,6 @@ impl<'tcx> Deref for Region<'tcx> {
}
}
impl<'tcx> fmt::Debug for Region<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.kind())
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, PartialOrd, Ord)]
#[derive(HashStable)]
pub struct EarlyBoundRegion {

View File

@ -17,7 +17,6 @@ use smallvec::SmallVec;
use core::intrinsics;
use std::cmp::Ordering;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::num::NonZeroUsize;
@ -80,16 +79,6 @@ impl<'tcx> GenericArgKind<'tcx> {
}
}
impl<'tcx> fmt::Debug for GenericArg<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.unpack() {
GenericArgKind::Lifetime(lt) => lt.fmt(f),
GenericArgKind::Type(ty) => ty.fmt(f),
GenericArgKind::Const(ct) => ct.fmt(f),
}
}
}
impl<'tcx> Ord for GenericArg<'tcx> {
fn cmp(&self, other: &GenericArg<'tcx>) -> Ordering {
self.unpack().cmp(&other.unpack())

View File

@ -31,6 +31,7 @@ mod macros;
mod structural_impls;
pub use codec::*;
pub use structural_impls::{DebugWithInfcx, InferCtxtLike, OptWithInfcx};
pub use sty::*;
pub use ty_info::*;
@ -39,41 +40,41 @@ pub trait HashStableContext {}
pub trait Interner: Sized {
type AdtDef: Clone + Debug + Hash + Ord;
type SubstsRef: Clone + Debug + Hash + Ord;
type SubstsRef: Clone + DebugWithInfcx<Self> + Hash + Ord;
type DefId: Clone + Debug + Hash + Ord;
type Binder<T>;
type Ty: Clone + Debug + Hash + Ord;
type Const: Clone + Debug + Hash + Ord;
type Region: Clone + Debug + Hash + Ord;
type Ty: Clone + DebugWithInfcx<Self> + Hash + Ord;
type Const: Clone + DebugWithInfcx<Self> + Hash + Ord;
type Region: Clone + DebugWithInfcx<Self> + Hash + Ord;
type Predicate;
type TypeAndMut: Clone + Debug + Hash + Ord;
type Mutability: Clone + Debug + Hash + Ord;
type Movability: Clone + Debug + Hash + Ord;
type PolyFnSig: Clone + Debug + Hash + Ord;
type ListBinderExistentialPredicate: Clone + Debug + Hash + Ord;
type BinderListTy: Clone + Debug + Hash + Ord;
type PolyFnSig: Clone + DebugWithInfcx<Self> + Hash + Ord;
type ListBinderExistentialPredicate: Clone + DebugWithInfcx<Self> + Hash + Ord;
type BinderListTy: Clone + DebugWithInfcx<Self> + Hash + Ord;
type ListTy: Clone + Debug + Hash + Ord + IntoIterator<Item = Self::Ty>;
type AliasTy: Clone + Debug + Hash + Ord;
type AliasTy: Clone + DebugWithInfcx<Self> + Hash + Ord;
type ParamTy: Clone + Debug + Hash + Ord;
type BoundTy: Clone + Debug + Hash + Ord;
type PlaceholderType: Clone + Debug + Hash + Ord;
type InferTy: Clone + DebugWithInfcx<Self> + Hash + Ord;
type ErrorGuaranteed: Clone + Debug + Hash + Ord;
type PredicateKind: Clone + Debug + Hash + PartialEq + Eq;
type AllocId: Clone + Debug + Hash + Ord;
type InferConst: Clone + Debug + Hash + Ord;
type AliasConst: Clone + Debug + Hash + Ord;
type InferConst: Clone + DebugWithInfcx<Self> + Hash + Ord;
type AliasConst: Clone + DebugWithInfcx<Self> + Hash + Ord;
type PlaceholderConst: Clone + Debug + Hash + Ord;
type ParamConst: Clone + Debug + Hash + Ord;
type BoundConst: Clone + Debug + Hash + Ord;
type InferTy: Clone + Debug + Hash + Ord;
type ValueConst: Clone + Debug + Hash + Ord;
type ExprConst: Clone + Debug + Hash + Ord;
type ExprConst: Clone + DebugWithInfcx<Self> + Hash + Ord;
type EarlyBoundRegion: Clone + Debug + Hash + Ord;
type BoundRegion: Clone + Debug + Hash + Ord;
type FreeRegion: Clone + Debug + Hash + Ord;
type RegionVid: Clone + Debug + Hash + Ord;
type RegionVid: Clone + DebugWithInfcx<Self> + Hash + Ord;
type PlaceholderRegion: Clone + Debug + Hash + Ord;
fn ty_and_mut_to_parts(ty_and_mut: Self::TypeAndMut) -> (Self::Ty, Self::Mutability);
@ -775,20 +776,6 @@ impl fmt::Debug for FloatVid {
}
}
impl fmt::Debug for InferTy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use InferTy::*;
match *self {
TyVar(ref v) => v.fmt(f),
IntVar(ref v) => v.fmt(f),
FloatVar(ref v) => v.fmt(f),
FreshTy(v) => write!(f, "FreshTy({v:?})"),
FreshIntTy(v) => write!(f, "FreshIntTy({v:?})"),
FreshFloatTy(v) => write!(f, "FreshFloatTy({v:?})"),
}
}
}
impl fmt::Debug for Variance {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self {

View File

@ -4,12 +4,13 @@
use crate::fold::{FallibleTypeFolder, TypeFoldable};
use crate::visit::{TypeVisitable, TypeVisitor};
use crate::{ConstKind, FloatTy, IntTy, Interner, UintTy};
use crate::{ConstKind, FloatTy, InferTy, IntTy, Interner, UintTy, UniverseIndex};
use rustc_data_structures::functor::IdFunctor;
use rustc_data_structures::sync::Lrc;
use rustc_index::{Idx, IndexVec};
use core::fmt;
use std::marker::PhantomData;
use std::ops::ControlFlow;
///////////////////////////////////////////////////////////////////////////
@ -165,6 +166,116 @@ impl<I: Interner, T: TypeVisitable<I>, Ix: Idx> TypeVisitable<I> for IndexVec<Ix
}
}
///////////////////////////////////////////////////
// Debug impls
pub trait InferCtxtLike<I: Interner> {
fn universe_of_ty(&self, ty: I::InferTy) -> Option<UniverseIndex>;
fn universe_of_lt(&self, lt: I::RegionVid) -> Option<UniverseIndex>;
fn universe_of_ct(&self, ct: I::InferConst) -> Option<UniverseIndex>;
}
impl<I: Interner> InferCtxtLike<I> for core::convert::Infallible {
fn universe_of_ty(&self, _ty: <I as Interner>::InferTy) -> Option<UniverseIndex> {
match *self {}
}
fn universe_of_ct(&self, _ct: <I as Interner>::InferConst) -> Option<UniverseIndex> {
match *self {}
}
fn universe_of_lt(&self, _lt: <I as Interner>::RegionVid) -> Option<UniverseIndex> {
match *self {}
}
}
pub trait DebugWithInfcx<I: Interner>: fmt::Debug {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result;
}
impl<I: Interner, T: DebugWithInfcx<I> + ?Sized> DebugWithInfcx<I> for &'_ T {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
<T as DebugWithInfcx<I>>::fmt(this.map(|&data| data), f)
}
}
impl<I: Interner, T: DebugWithInfcx<I>> DebugWithInfcx<I> for [T] {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
match f.alternate() {
true => {
write!(f, "[\n")?;
for element in this.data.iter() {
write!(f, "{:?},\n", &this.wrap(element))?;
}
write!(f, "]")
}
false => {
write!(f, "[")?;
if this.data.len() > 0 {
for element in &this.data[..(this.data.len() - 1)] {
write!(f, "{:?}, ", &this.wrap(element))?;
}
if let Some(element) = this.data.last() {
write!(f, "{:?}", &this.wrap(element))?;
}
}
write!(f, "]")
}
}
}
}
pub struct OptWithInfcx<'a, I: Interner, InfCtx: InferCtxtLike<I>, T> {
pub data: T,
pub infcx: Option<&'a InfCtx>,
_interner: PhantomData<I>,
}
impl<I: Interner, InfCtx: InferCtxtLike<I>, T: Copy> Copy for OptWithInfcx<'_, I, InfCtx, T> {}
impl<I: Interner, InfCtx: InferCtxtLike<I>, T: Clone> Clone for OptWithInfcx<'_, I, InfCtx, T> {
fn clone(&self) -> Self {
Self { data: self.data.clone(), infcx: self.infcx, _interner: self._interner }
}
}
impl<'a, I: Interner, T> OptWithInfcx<'a, I, core::convert::Infallible, T> {
pub fn new_no_ctx(data: T) -> Self {
Self { data, infcx: None, _interner: PhantomData }
}
}
impl<'a, I: Interner, InfCtx: InferCtxtLike<I>, T> OptWithInfcx<'a, I, InfCtx, T> {
pub fn new(data: T, infcx: &'a InfCtx) -> Self {
Self { data, infcx: Some(infcx), _interner: PhantomData }
}
pub fn wrap<U>(self, u: U) -> OptWithInfcx<'a, I, InfCtx, U> {
OptWithInfcx { data: u, infcx: self.infcx, _interner: PhantomData }
}
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> OptWithInfcx<'a, I, InfCtx, U> {
OptWithInfcx { data: f(self.data), infcx: self.infcx, _interner: PhantomData }
}
pub fn as_ref(&self) -> OptWithInfcx<'a, I, InfCtx, &T> {
OptWithInfcx { data: &self.data, infcx: self.infcx, _interner: PhantomData }
}
}
impl<I: Interner, InfCtx: InferCtxtLike<I>, T: DebugWithInfcx<I>> fmt::Debug
for OptWithInfcx<'_, I, InfCtx, T>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
DebugWithInfcx::fmt(self.as_ref(), f)
}
}
impl fmt::Debug for IntTy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name_str())
@ -183,20 +294,60 @@ impl fmt::Debug for FloatTy {
}
}
impl<I: Interner> fmt::Debug for ConstKind<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use ConstKind::*;
match self {
Param(param) => write!(f, "{param:?}"),
Infer(var) => write!(f, "{var:?}"),
Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var.clone()),
Placeholder(placeholder) => write!(f, "{placeholder:?}"),
Unevaluated(uv) => {
write!(f, "{uv:?}")
}
Value(valtree) => write!(f, "{valtree:?}"),
Error(_) => write!(f, "{{const error}}"),
Expr(expr) => write!(f, "{expr:?}"),
impl fmt::Debug for InferTy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use InferTy::*;
match *self {
TyVar(ref v) => v.fmt(f),
IntVar(ref v) => v.fmt(f),
FloatVar(ref v) => v.fmt(f),
FreshTy(v) => write!(f, "FreshTy({v:?})"),
FreshIntTy(v) => write!(f, "FreshIntTy({v:?})"),
FreshFloatTy(v) => write!(f, "FreshFloatTy({v:?})"),
}
}
}
impl<I: Interner<InferTy = InferTy>> DebugWithInfcx<I> for InferTy {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
use InferTy::*;
match this.infcx.and_then(|infcx| infcx.universe_of_ty(*this.data)) {
None => write!(f, "{:?}", this.data),
Some(universe) => match *this.data {
TyVar(ty_vid) => write!(f, "?{}_{}t", ty_vid.index(), universe.index()),
IntVar(_) | FloatVar(_) | FreshTy(_) | FreshIntTy(_) | FreshFloatTy(_) => {
unreachable!()
}
},
}
}
}
impl<I: Interner> fmt::Debug for ConstKind<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
OptWithInfcx::new_no_ctx(self).fmt(f)
}
}
impl<I: Interner> DebugWithInfcx<I> for ConstKind<I> {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
use ConstKind::*;
match this.data {
Param(param) => write!(f, "{param:?}"),
Infer(var) => write!(f, "{:?}", &this.wrap(var)),
Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var.clone()),
Placeholder(placeholder) => write!(f, "{placeholder:?}"),
Unevaluated(uv) => {
write!(f, "{:?}", &this.wrap(uv))
}
Value(valtree) => write!(f, "{valtree:?}"),
Error(_) => write!(f, "{{const error}}"),
Expr(expr) => write!(f, "{:?}", &this.wrap(expr)),
}
}
}

View File

@ -3,7 +3,6 @@
use std::cmp::Ordering;
use std::{fmt, hash};
use crate::DebruijnIndex;
use crate::FloatTy;
use crate::HashStableContext;
use crate::IntTy;
@ -11,6 +10,7 @@ use crate::Interner;
use crate::TyDecoder;
use crate::TyEncoder;
use crate::UintTy;
use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, OptWithInfcx};
use self::RegionKind::*;
use self::TyKind::*;
@ -503,42 +503,48 @@ impl<I: Interner> hash::Hash for TyKind<I> {
}
}
// This is manually implemented because a derive would require `I: Debug`
impl<I: Interner> fmt::Debug for TyKind<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
impl<I: Interner> DebugWithInfcx<I> for TyKind<I> {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> fmt::Result {
match this.data {
Bool => write!(f, "bool"),
Char => write!(f, "char"),
Int(i) => write!(f, "{i:?}"),
Uint(u) => write!(f, "{u:?}"),
Float(float) => write!(f, "{float:?}"),
Adt(d, s) => f.debug_tuple_field2_finish("Adt", d, s),
Adt(d, s) => f.debug_tuple_field2_finish("Adt", d, &this.wrap(s)),
Foreign(d) => f.debug_tuple_field1_finish("Foreign", d),
Str => write!(f, "str"),
Array(t, c) => write!(f, "[{t:?}; {c:?}]"),
Slice(t) => write!(f, "[{t:?}]"),
Array(t, c) => write!(f, "[{:?}; {:?}]", &this.wrap(t), &this.wrap(c)),
Slice(t) => write!(f, "[{:?}]", &this.wrap(t)),
RawPtr(p) => {
let (ty, mutbl) = I::ty_and_mut_to_parts(p.clone());
match I::mutability_is_mut(mutbl) {
true => write!(f, "*mut "),
false => write!(f, "*const "),
}?;
write!(f, "{ty:?}")
write!(f, "{:?}", &this.wrap(ty))
}
Ref(r, t, m) => match I::mutability_is_mut(m.clone()) {
true => write!(f, "&{r:?} mut {t:?}"),
false => write!(f, "&{r:?} {t:?}"),
true => write!(f, "&{:?} mut {:?}", &this.wrap(r), &this.wrap(t)),
false => write!(f, "&{:?} {:?}", &this.wrap(r), &this.wrap(t)),
},
FnDef(d, s) => f.debug_tuple_field2_finish("FnDef", d, s),
FnPtr(s) => write!(f, "{s:?}"),
FnDef(d, s) => f.debug_tuple_field2_finish("FnDef", d, &this.wrap(s)),
FnPtr(s) => write!(f, "{:?}", &this.wrap(s)),
Dynamic(p, r, repr) => match repr {
DynKind::Dyn => write!(f, "dyn {p:?} + {r:?}"),
DynKind::DynStar => write!(f, "dyn* {p:?} + {r:?}"),
DynKind::Dyn => write!(f, "dyn {:?} + {:?}", &this.wrap(p), &this.wrap(r)),
DynKind::DynStar => {
write!(f, "dyn* {:?} + {:?}", &this.wrap(p), &this.wrap(r))
}
},
Closure(d, s) => f.debug_tuple_field2_finish("Closure", d, s),
Generator(d, s, m) => f.debug_tuple_field3_finish("Generator", d, s, m),
GeneratorWitness(g) => f.debug_tuple_field1_finish("GeneratorWitness", g),
GeneratorWitnessMIR(d, s) => f.debug_tuple_field2_finish("GeneratorWitnessMIR", d, s),
Closure(d, s) => f.debug_tuple_field2_finish("Closure", d, &this.wrap(s)),
Generator(d, s, m) => f.debug_tuple_field3_finish("Generator", d, &this.wrap(s), m),
GeneratorWitness(g) => f.debug_tuple_field1_finish("GeneratorWitness", &this.wrap(g)),
GeneratorWitnessMIR(d, s) => {
f.debug_tuple_field2_finish("GeneratorWitnessMIR", d, &this.wrap(s))
}
Never => write!(f, "!"),
Tuple(t) => {
let mut iter = t.clone().into_iter();
@ -547,28 +553,34 @@ impl<I: Interner> fmt::Debug for TyKind<I> {
match iter.next() {
None => return write!(f, ")"),
Some(ty) => write!(f, "{ty:?}")?,
Some(ty) => write!(f, "{:?}", &this.wrap(ty))?,
};
match iter.next() {
None => return write!(f, ",)"),
Some(ty) => write!(f, "{ty:?})")?,
Some(ty) => write!(f, "{:?})", &this.wrap(ty))?,
}
for ty in iter {
write!(f, ", {ty:?}")?;
write!(f, ", {:?}", &this.wrap(ty))?;
}
write!(f, ")")
}
Alias(i, a) => f.debug_tuple_field2_finish("Alias", i, a),
Alias(i, a) => f.debug_tuple_field2_finish("Alias", i, &this.wrap(a)),
Param(p) => write!(f, "{p:?}"),
Bound(d, b) => crate::debug_bound_var(f, *d, b),
Placeholder(p) => write!(f, "{p:?}"),
Infer(t) => write!(f, "{t:?}"),
Infer(t) => write!(f, "{:?}", this.wrap(t)),
TyKind::Error(_) => write!(f, "{{type error}}"),
}
}
}
// This is manually implemented because a derive would require `I: Debug`
impl<I: Interner> fmt::Debug for TyKind<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
OptWithInfcx::new_no_ctx(self).fmt(f)
}
}
// This is manually implemented because a derive would require `I: Encodable`
impl<I: Interner, E: TyEncoder> Encodable<E> for TyKind<I>
@ -1356,21 +1368,23 @@ impl<I: Interner> hash::Hash for RegionKind<I> {
}
}
// This is manually implemented because a derive would require `I: Debug`
impl<I: Interner> fmt::Debug for RegionKind<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
impl<I: Interner> DebugWithInfcx<I> for RegionKind<I> {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
match this.data {
ReEarlyBound(data) => write!(f, "ReEarlyBound({data:?})"),
ReLateBound(binder_id, bound_region) => {
write!(f, "ReLateBound({binder_id:?}, {bound_region:?})")
}
ReFree(fr) => fr.fmt(f),
ReFree(fr) => write!(f, "{fr:?}"),
ReStatic => f.write_str("ReStatic"),
ReVar(vid) => vid.fmt(f),
ReVar(vid) => write!(f, "{:?}", &this.wrap(vid)),
RePlaceholder(placeholder) => write!(f, "RePlaceholder({placeholder:?})"),
@ -1380,6 +1394,11 @@ impl<I: Interner> fmt::Debug for RegionKind<I> {
}
}
}
impl<I: Interner> fmt::Debug for RegionKind<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
OptWithInfcx::new_no_ctx(self).fmt(f)
}
}
// This is manually implemented because a derive would require `I: Encodable`
impl<I: Interner, E: TyEncoder> Encodable<E> for RegionKind<I>