diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs new file mode 100644 index 0000000000000..f84841c9f6455 --- /dev/null +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -0,0 +1,258 @@ +use rustc_data_structures::stable_hasher::HashStable; +use rustc_serialize::{Decodable, Decoder, Encodable}; +use std::cmp::Ordering; +use std::fmt; +use std::hash; + +use crate::{ + DebruijnIndex, DebugWithInfcx, HashStableContext, InferCtxtLike, Interner, OptWithInfcx, + TyDecoder, TyEncoder, +}; + +use self::ConstKind::*; + +/// Represents a constant in Rust. +// #[derive(derive_more::From)] +pub enum ConstKind { + /// A const generic parameter. + Param(I::ParamConst), + + /// Infer the value of the const. + Infer(I::InferConst), + + /// Bound const variable, used only when preparing a trait query. + Bound(DebruijnIndex, I::BoundConst), + + /// A placeholder const - universally quantified higher-ranked const. + Placeholder(I::PlaceholderConst), + + /// An unnormalized const item such as an anon const or assoc const or free const item. + /// Right now anything other than anon consts does not actually work properly but this + /// should + Unevaluated(I::AliasConst), + + /// Used to hold computed value. + Value(I::ValueConst), + + /// A placeholder for a const which could not be computed; this is + /// propagated to avoid useless error messages. + Error(I::ErrorGuaranteed), + + /// Unevaluated non-const-item, used by `feature(generic_const_exprs)` to represent + /// const arguments such as `N + 1` or `foo(N)` + Expr(I::ExprConst), +} + +const fn const_kind_discriminant(value: &ConstKind) -> usize { + match value { + Param(_) => 0, + Infer(_) => 1, + Bound(_, _) => 2, + Placeholder(_) => 3, + Unevaluated(_) => 4, + Value(_) => 5, + Error(_) => 6, + Expr(_) => 7, + } +} + +impl hash::Hash for ConstKind { + fn hash(&self, state: &mut H) { + const_kind_discriminant(self).hash(state); + match self { + Param(p) => p.hash(state), + Infer(i) => i.hash(state), + Bound(d, b) => { + d.hash(state); + b.hash(state); + } + Placeholder(p) => p.hash(state), + Unevaluated(u) => u.hash(state), + Value(v) => v.hash(state), + Error(e) => e.hash(state), + Expr(e) => e.hash(state), + } + } +} + +impl HashStable for ConstKind +where + I::ParamConst: HashStable, + I::InferConst: HashStable, + I::BoundConst: HashStable, + I::PlaceholderConst: HashStable, + I::AliasConst: HashStable, + I::ValueConst: HashStable, + I::ErrorGuaranteed: HashStable, + I::ExprConst: HashStable, +{ + fn hash_stable( + &self, + hcx: &mut CTX, + hasher: &mut rustc_data_structures::stable_hasher::StableHasher, + ) { + const_kind_discriminant(self).hash_stable(hcx, hasher); + match self { + Param(p) => p.hash_stable(hcx, hasher), + Infer(i) => i.hash_stable(hcx, hasher), + Bound(d, b) => { + d.hash_stable(hcx, hasher); + b.hash_stable(hcx, hasher); + } + Placeholder(p) => p.hash_stable(hcx, hasher), + Unevaluated(u) => u.hash_stable(hcx, hasher), + Value(v) => v.hash_stable(hcx, hasher), + Error(e) => e.hash_stable(hcx, hasher), + Expr(e) => e.hash_stable(hcx, hasher), + } + } +} + +impl> Decodable for ConstKind +where + I::ParamConst: Decodable, + I::InferConst: Decodable, + I::BoundConst: Decodable, + I::PlaceholderConst: Decodable, + I::AliasConst: Decodable, + I::ValueConst: Decodable, + I::ErrorGuaranteed: Decodable, + I::ExprConst: Decodable, +{ + fn decode(d: &mut D) -> Self { + match Decoder::read_usize(d) { + 0 => Param(Decodable::decode(d)), + 1 => Infer(Decodable::decode(d)), + 2 => Bound(Decodable::decode(d), Decodable::decode(d)), + 3 => Placeholder(Decodable::decode(d)), + 4 => Unevaluated(Decodable::decode(d)), + 5 => Value(Decodable::decode(d)), + 6 => Error(Decodable::decode(d)), + 7 => Expr(Decodable::decode(d)), + _ => panic!( + "{}", + format!( + "invalid enum variant tag while decoding `{}`, expected 0..{}", + "ConstKind", 8, + ) + ), + } + } +} + +impl> Encodable for ConstKind +where + I::ParamConst: Encodable, + I::InferConst: Encodable, + I::BoundConst: Encodable, + I::PlaceholderConst: Encodable, + I::AliasConst: Encodable, + I::ValueConst: Encodable, + I::ErrorGuaranteed: Encodable, + I::ExprConst: Encodable, +{ + fn encode(&self, e: &mut E) { + let disc = const_kind_discriminant(self); + match self { + Param(p) => e.emit_enum_variant(disc, |e| p.encode(e)), + Infer(i) => e.emit_enum_variant(disc, |e| i.encode(e)), + Bound(d, b) => e.emit_enum_variant(disc, |e| { + d.encode(e); + b.encode(e); + }), + Placeholder(p) => e.emit_enum_variant(disc, |e| p.encode(e)), + Unevaluated(u) => e.emit_enum_variant(disc, |e| u.encode(e)), + Value(v) => e.emit_enum_variant(disc, |e| v.encode(e)), + Error(er) => e.emit_enum_variant(disc, |e| er.encode(e)), + Expr(ex) => e.emit_enum_variant(disc, |e| ex.encode(e)), + } + } +} + +impl PartialOrd for ConstKind { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ConstKind { + fn cmp(&self, other: &Self) -> Ordering { + const_kind_discriminant(self) + .cmp(&const_kind_discriminant(other)) + .then_with(|| match (self, other) { + (Param(p1), Param(p2)) => p1.cmp(p2), + (Infer(i1), Infer(i2)) => i1.cmp(i2), + (Bound(d1, b1), Bound(d2, b2)) => d1.cmp(d2).then_with(|| b1.cmp(b2)), + (Placeholder(p1), Placeholder(p2)) => p1.cmp(p2), + (Unevaluated(u1), Unevaluated(u2)) => u1.cmp(u2), + (Value(v1), Value(v2)) => v1.cmp(v2), + (Error(e1), Error(e2)) => e1.cmp(e2), + (Expr(e1), Expr(e2)) => e1.cmp(e2), + _ => { + debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}"); + Ordering::Equal + } + }) + } +} + +impl PartialEq for ConstKind { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Param(l0), Param(r0)) => l0 == r0, + (Infer(l0), Infer(r0)) => l0 == r0, + (Bound(l0, l1), Bound(r0, r1)) => l0 == r0 && l1 == r1, + (Placeholder(l0), Placeholder(r0)) => l0 == r0, + (Unevaluated(l0), Unevaluated(r0)) => l0 == r0, + (Value(l0), Value(r0)) => l0 == r0, + (Error(l0), Error(r0)) => l0 == r0, + (Expr(l0), Expr(r0)) => l0 == r0, + _ => false, + } + } +} + +impl Eq for ConstKind {} + +impl Clone for ConstKind { + fn clone(&self) -> Self { + match self { + Param(arg0) => Param(arg0.clone()), + Infer(arg0) => Infer(arg0.clone()), + Bound(arg0, arg1) => Bound(arg0.clone(), arg1.clone()), + Placeholder(arg0) => Placeholder(arg0.clone()), + Unevaluated(arg0) => Unevaluated(arg0.clone()), + Value(arg0) => Value(arg0.clone()), + Error(arg0) => Error(arg0.clone()), + Expr(arg0) => Expr(arg0.clone()), + } + } +} + +impl fmt::Debug for ConstKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + OptWithInfcx::new_no_ctx(self).fmt(f) + } +} + +impl DebugWithInfcx for ConstKind { + fn fmt>( + 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)), + } + } +} diff --git a/compiler/rustc_type_ir/src/debug.rs b/compiler/rustc_type_ir/src/debug.rs new file mode 100644 index 0000000000000..7c6a784690073 --- /dev/null +++ b/compiler/rustc_type_ir/src/debug.rs @@ -0,0 +1,117 @@ +use crate::{Interner, UniverseIndex}; + +use core::fmt; +use std::marker::PhantomData; + +pub trait InferCtxtLike { + fn universe_of_ty(&self, ty: I::InferTy) -> Option; + + fn universe_of_lt(&self, lt: I::InferRegion) -> Option; + + fn universe_of_ct(&self, ct: I::InferConst) -> Option; +} + +impl InferCtxtLike for core::convert::Infallible { + fn universe_of_ty(&self, _ty: ::InferTy) -> Option { + match *self {} + } + + fn universe_of_ct(&self, _ct: ::InferConst) -> Option { + match *self {} + } + + fn universe_of_lt(&self, _lt: ::InferRegion) -> Option { + match *self {} + } +} + +pub trait DebugWithInfcx: fmt::Debug { + fn fmt>( + this: OptWithInfcx<'_, I, InfCtx, &Self>, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result; +} + +impl + ?Sized> DebugWithInfcx for &'_ T { + fn fmt>( + this: OptWithInfcx<'_, I, InfCtx, &Self>, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + >::fmt(this.map(|&data| data), f) + } +} + +impl> DebugWithInfcx for [T] { + fn fmt>( + 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, T> { + pub data: T, + pub infcx: Option<&'a InfCtx>, + _interner: PhantomData, +} + +impl, T: Copy> Copy for OptWithInfcx<'_, I, InfCtx, T> {} + +impl, 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, 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(self, u: U) -> OptWithInfcx<'a, I, InfCtx, U> { + OptWithInfcx { data: u, infcx: self.infcx, _interner: PhantomData } + } + + pub fn map(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, T: DebugWithInfcx> fmt::Debug + for OptWithInfcx<'_, I, InfCtx, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + DebugWithInfcx::fmt(self.as_ref(), f) + } +} diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs new file mode 100644 index 0000000000000..2acb903c217a1 --- /dev/null +++ b/compiler/rustc_type_ir/src/flags.rs @@ -0,0 +1,119 @@ +bitflags! { + /// Flags that we track on types. These flags are propagated upwards + /// through the type during type construction, so that we can quickly check + /// whether the type has various kinds of types in it without recursing + /// over the type itself. + pub struct TypeFlags: u32 { + // Does this have parameters? Used to determine whether substitution is + // required. + /// Does this have `Param`? + const HAS_TY_PARAM = 1 << 0; + /// Does this have `ReEarlyBound`? + const HAS_RE_PARAM = 1 << 1; + /// Does this have `ConstKind::Param`? + const HAS_CT_PARAM = 1 << 2; + + const HAS_PARAM = TypeFlags::HAS_TY_PARAM.bits + | TypeFlags::HAS_RE_PARAM.bits + | TypeFlags::HAS_CT_PARAM.bits; + + /// Does this have `Infer`? + const HAS_TY_INFER = 1 << 3; + /// Does this have `ReVar`? + const HAS_RE_INFER = 1 << 4; + /// Does this have `ConstKind::Infer`? + const HAS_CT_INFER = 1 << 5; + + /// Does this have inference variables? Used to determine whether + /// inference is required. + const HAS_INFER = TypeFlags::HAS_TY_INFER.bits + | TypeFlags::HAS_RE_INFER.bits + | TypeFlags::HAS_CT_INFER.bits; + + /// Does this have `Placeholder`? + const HAS_TY_PLACEHOLDER = 1 << 6; + /// Does this have `RePlaceholder`? + const HAS_RE_PLACEHOLDER = 1 << 7; + /// Does this have `ConstKind::Placeholder`? + const HAS_CT_PLACEHOLDER = 1 << 8; + + /// Does this have placeholders? + const HAS_PLACEHOLDER = TypeFlags::HAS_TY_PLACEHOLDER.bits + | TypeFlags::HAS_RE_PLACEHOLDER.bits + | TypeFlags::HAS_CT_PLACEHOLDER.bits; + + /// `true` if there are "names" of regions and so forth + /// that are local to a particular fn/inferctxt + const HAS_FREE_LOCAL_REGIONS = 1 << 9; + + /// `true` if there are "names" of types and regions and so forth + /// that are local to a particular fn + const HAS_FREE_LOCAL_NAMES = TypeFlags::HAS_TY_PARAM.bits + | TypeFlags::HAS_CT_PARAM.bits + | TypeFlags::HAS_TY_INFER.bits + | TypeFlags::HAS_CT_INFER.bits + | TypeFlags::HAS_TY_PLACEHOLDER.bits + | TypeFlags::HAS_CT_PLACEHOLDER.bits + // We consider 'freshened' types and constants + // to depend on a particular fn. + // The freshening process throws away information, + // which can make things unsuitable for use in a global + // cache. Note that there is no 'fresh lifetime' flag - + // freshening replaces all lifetimes with `ReErased`, + // which is different from how types/const are freshened. + | TypeFlags::HAS_TY_FRESH.bits + | TypeFlags::HAS_CT_FRESH.bits + | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits + | TypeFlags::HAS_RE_ERASED.bits; + + /// Does this have `Projection`? + const HAS_TY_PROJECTION = 1 << 10; + /// Does this have `Inherent`? + const HAS_TY_INHERENT = 1 << 11; + /// Does this have `Opaque`? + const HAS_TY_OPAQUE = 1 << 12; + /// Does this have `ConstKind::Unevaluated`? + const HAS_CT_PROJECTION = 1 << 13; + + /// Could this type be normalized further? + const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits + | TypeFlags::HAS_TY_OPAQUE.bits + | TypeFlags::HAS_TY_INHERENT.bits + | TypeFlags::HAS_CT_PROJECTION.bits; + + /// Is an error type/const reachable? + const HAS_ERROR = 1 << 14; + + /// Does this have any region that "appears free" in the type? + /// Basically anything but `ReLateBound` and `ReErased`. + const HAS_FREE_REGIONS = 1 << 15; + + /// Does this have any `ReLateBound` regions? + const HAS_RE_LATE_BOUND = 1 << 16; + /// Does this have any `Bound` types? + const HAS_TY_LATE_BOUND = 1 << 17; + /// Does this have any `ConstKind::Bound` consts? + const HAS_CT_LATE_BOUND = 1 << 18; + /// Does this have any bound variables? + /// Used to check if a global bound is safe to evaluate. + const HAS_LATE_BOUND = TypeFlags::HAS_RE_LATE_BOUND.bits + | TypeFlags::HAS_TY_LATE_BOUND.bits + | TypeFlags::HAS_CT_LATE_BOUND.bits; + + /// Does this have any `ReErased` regions? + const HAS_RE_ERASED = 1 << 19; + + /// Does this value have parameters/placeholders/inference variables which could be + /// replaced later, in a way that would change the results of `impl` specialization? + const STILL_FURTHER_SPECIALIZABLE = 1 << 20; + + /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`? + const HAS_TY_FRESH = 1 << 21; + + /// Does this value have `InferConst::Fresh`? + const HAS_CT_FRESH = 1 << 22; + + /// Does this have `Generator` or `GeneratorWitness`? + const HAS_TY_GENERATOR = 1 << 23; + } +} diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index e7a6831f5ee35..fc56400df1614 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -44,6 +44,11 @@ //! - ty.super_fold_with(folder) //! - u.fold_with(folder) //! ``` + +use rustc_data_structures::sync::Lrc; +use rustc_index::{Idx, IndexVec}; +use std::mem; + use crate::{visit::TypeVisitable, Interner}; /// This trait is implemented for every type that can be folded, @@ -242,3 +247,101 @@ where Ok(self.fold_predicate(p)) } } + +/////////////////////////////////////////////////////////////////////////// +// Traversal implementations. + +impl, U: TypeFoldable> TypeFoldable for (T, U) { + fn try_fold_with>(self, folder: &mut F) -> Result<(T, U), F::Error> { + Ok((self.0.try_fold_with(folder)?, self.1.try_fold_with(folder)?)) + } +} + +impl, B: TypeFoldable, C: TypeFoldable> TypeFoldable + for (A, B, C) +{ + fn try_fold_with>( + self, + folder: &mut F, + ) -> Result<(A, B, C), F::Error> { + Ok(( + self.0.try_fold_with(folder)?, + self.1.try_fold_with(folder)?, + self.2.try_fold_with(folder)?, + )) + } +} + +impl> TypeFoldable for Option { + fn try_fold_with>(self, folder: &mut F) -> Result { + Ok(match self { + Some(v) => Some(v.try_fold_with(folder)?), + None => None, + }) + } +} + +impl, E: TypeFoldable> TypeFoldable for Result { + fn try_fold_with>(self, folder: &mut F) -> Result { + Ok(match self { + Ok(v) => Ok(v.try_fold_with(folder)?), + Err(e) => Err(e.try_fold_with(folder)?), + }) + } +} + +impl> TypeFoldable for Lrc { + fn try_fold_with>(mut self, folder: &mut F) -> Result { + // We merely want to replace the contained `T`, if at all possible, + // so that we don't needlessly allocate a new `Lrc` or indeed clone + // the contained type. + unsafe { + // First step is to ensure that we have a unique reference to + // the contained type, which `Lrc::make_mut` will accomplish (by + // allocating a new `Lrc` and cloning the `T` only if required). + // This is done *before* casting to `Lrc>` so that + // panicking during `make_mut` does not leak the `T`. + Lrc::make_mut(&mut self); + + // Casting to `Lrc>` is safe because `ManuallyDrop` + // is `repr(transparent)`. + let ptr = Lrc::into_raw(self).cast::>(); + let mut unique = Lrc::from_raw(ptr); + + // Call to `Lrc::make_mut` above guarantees that `unique` is the + // sole reference to the contained value, so we can avoid doing + // a checked `get_mut` here. + let slot = Lrc::get_mut_unchecked(&mut unique); + + // Semantically move the contained type out from `unique`, fold + // it, then move the folded value back into `unique`. Should + // folding fail, `ManuallyDrop` ensures that the "moved-out" + // value is not re-dropped. + let owned = mem::ManuallyDrop::take(slot); + let folded = owned.try_fold_with(folder)?; + *slot = mem::ManuallyDrop::new(folded); + + // Cast back to `Lrc`. + Ok(Lrc::from_raw(Lrc::into_raw(unique).cast())) + } + } +} + +impl> TypeFoldable for Box { + fn try_fold_with>(mut self, folder: &mut F) -> Result { + *self = (*self).try_fold_with(folder)?; + Ok(self) + } +} + +impl> TypeFoldable for Vec { + fn try_fold_with>(self, folder: &mut F) -> Result { + self.into_iter().map(|t| t.try_fold_with(folder)).collect() + } +} + +impl, Ix: Idx> TypeFoldable for IndexVec { + fn try_fold_with>(self, folder: &mut F) -> Result { + self.raw.try_fold_with(folder).map(IndexVec::from_raw) + } +} diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs new file mode 100644 index 0000000000000..60e4c58799358 --- /dev/null +++ b/compiler/rustc_type_ir/src/interner.rs @@ -0,0 +1,155 @@ +use smallvec::SmallVec; +use std::fmt::Debug; +use std::hash::Hash; + +use crate::{DebugWithInfcx, Mutability}; + +pub trait Interner: Sized { + type DefId: Clone + Debug + Hash + Ord; + type AdtDef: Clone + Debug + Hash + Ord; + + type GenericArgs: Clone + + DebugWithInfcx + + Hash + + Ord + + IntoIterator; + type GenericArg: Clone + DebugWithInfcx + Hash + Ord; + + type Binder; + + // Predicates + type Predicate; + type PredicateKind: Clone + Debug + Hash + PartialEq + Eq; + + type TypeAndMut: Clone + Debug + Hash + Ord; + + // Kinds of tys + type Ty: Clone + DebugWithInfcx + Hash + Ord; + type Tys: Clone + Debug + Hash + Ord + IntoIterator; + type AliasTy: Clone + DebugWithInfcx + Hash + Ord; + type ParamTy: Clone + Debug + Hash + Ord; + type BoundTy: Clone + Debug + Hash + Ord; + type PlaceholderTy: Clone + Debug + Hash + Ord; + type InferTy: Clone + DebugWithInfcx + Hash + Ord; + + // Things stored inside of tys + type ErrorGuaranteed: Clone + Debug + Hash + Ord; + type BoundExistentialPredicates: Clone + DebugWithInfcx + Hash + Ord; + type PolyFnSig: Clone + DebugWithInfcx + Hash + Ord; + type AllocId: Clone + Debug + Hash + Ord; + + // Kinds of consts + type Const: Clone + DebugWithInfcx + Hash + Ord; + type InferConst: Clone + DebugWithInfcx + Hash + Ord; + type AliasConst: Clone + DebugWithInfcx + Hash + Ord; + type PlaceholderConst: Clone + Debug + Hash + Ord; + type ParamConst: Clone + Debug + Hash + Ord; + type BoundConst: Clone + Debug + Hash + Ord; + type ValueConst: Clone + Debug + Hash + Ord; + type ExprConst: Clone + DebugWithInfcx + Hash + Ord; + + // Kinds of regions + type Region: Clone + DebugWithInfcx + Hash + Ord; + type EarlyBoundRegion: Clone + Debug + Hash + Ord; + type BoundRegion: Clone + Debug + Hash + Ord; + type FreeRegion: Clone + Debug + Hash + Ord; + type InferRegion: Clone + DebugWithInfcx + Hash + Ord; + type PlaceholderRegion: Clone + Debug + Hash + Ord; + + fn ty_and_mut_to_parts(ty_and_mut: Self::TypeAndMut) -> (Self::Ty, Mutability); +} + +/// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` +/// that produces `T` items. You could combine them with +/// `f(&iter.collect::>())`, but this requires allocating memory for the +/// `Vec`. +/// +/// This trait allows for faster implementations, intended for cases where the +/// number of items produced by the iterator is small. There is a blanket impl +/// for `T` items, but there is also a fallible impl for `Result` items. +pub trait CollectAndApply: Sized { + type Output; + + /// Produce a result of type `Self::Output` from `iter`. The result will + /// typically be produced by applying `f` on the elements produced by + /// `iter`, though this may not happen in some impls, e.g. if an error + /// occurred during iteration. + fn collect_and_apply(iter: I, f: F) -> Self::Output + where + I: Iterator, + F: FnOnce(&[T]) -> R; +} + +/// The blanket impl that always collects all elements and applies `f`. +impl CollectAndApply for T { + type Output = R; + + /// Equivalent to `f(&iter.collect::>())`. + fn collect_and_apply(mut iter: I, f: F) -> R + where + I: Iterator, + F: FnOnce(&[T]) -> R, + { + // This code is hot enough that it's worth specializing for the most + // common length lists, to avoid the overhead of `SmallVec` creation. + // Lengths 0, 1, and 2 typically account for ~95% of cases. If + // `size_hint` is incorrect a panic will occur via an `unwrap` or an + // `assert`. + match iter.size_hint() { + (0, Some(0)) => { + assert!(iter.next().is_none()); + f(&[]) + } + (1, Some(1)) => { + let t0 = iter.next().unwrap(); + assert!(iter.next().is_none()); + f(&[t0]) + } + (2, Some(2)) => { + let t0 = iter.next().unwrap(); + let t1 = iter.next().unwrap(); + assert!(iter.next().is_none()); + f(&[t0, t1]) + } + _ => f(&iter.collect::>()), + } + } +} + +/// A fallible impl that will fail, without calling `f`, if there are any +/// errors during collection. +impl CollectAndApply for Result { + type Output = Result; + + /// Equivalent to `Ok(f(&iter.collect::>>()?))`. + fn collect_and_apply(mut iter: I, f: F) -> Result + where + I: Iterator>, + F: FnOnce(&[T]) -> R, + { + // This code is hot enough that it's worth specializing for the most + // common length lists, to avoid the overhead of `SmallVec` creation. + // Lengths 0, 1, and 2 typically account for ~95% of cases. If + // `size_hint` is incorrect a panic will occur via an `unwrap` or an + // `assert`, unless a failure happens first, in which case the result + // will be an error anyway. + Ok(match iter.size_hint() { + (0, Some(0)) => { + assert!(iter.next().is_none()); + f(&[]) + } + (1, Some(1)) => { + let t0 = iter.next().unwrap()?; + assert!(iter.next().is_none()); + f(&[t0]) + } + (2, Some(2)) => { + let t0 = iter.next().unwrap()?; + let t1 = iter.next().unwrap()?; + assert!(iter.next().is_none()); + f(&[t0, t1]) + } + _ => f(&iter.collect::, _>>()?), + }) + } +} diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 0a35e8d3a90c2..d4ca9da96e4ed 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -15,302 +15,35 @@ extern crate bitflags; #[macro_use] extern crate rustc_macros; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::unify::{EqUnifyValue, UnifyKey}; -use smallvec::SmallVec; use std::fmt; -use std::fmt::Debug; use std::hash::Hash; -use std::mem::discriminant; pub mod codec; pub mod fold; -pub mod sty; pub mod ty_info; +pub mod ty_kind; pub mod visit; #[macro_use] mod macros; -mod structural_impls; +mod const_kind; +mod debug; +mod flags; +mod interner; +mod region_kind; pub use codec::*; -pub use structural_impls::{DebugWithInfcx, InferCtxtLike, OptWithInfcx}; -pub use sty::*; +pub use const_kind::*; +pub use debug::{DebugWithInfcx, InferCtxtLike, OptWithInfcx}; +pub use flags::*; +pub use interner::*; +pub use region_kind::*; pub use ty_info::*; +pub use ty_kind::*; /// Needed so we can use #[derive(HashStable_Generic)] pub trait HashStableContext {} -pub trait Interner: Sized { - type DefId: Clone + Debug + Hash + Ord; - type AdtDef: Clone + Debug + Hash + Ord; - - type GenericArgs: Clone - + DebugWithInfcx - + Hash - + Ord - + IntoIterator; - type GenericArg: Clone + DebugWithInfcx + Hash + Ord; - - type Binder; - - // Predicates - type Predicate; - type PredicateKind: Clone + Debug + Hash + PartialEq + Eq; - - type TypeAndMut: Clone + Debug + Hash + Ord; - - // Kinds of tys - type Ty: Clone + DebugWithInfcx + Hash + Ord; - type Tys: Clone + Debug + Hash + Ord + IntoIterator; - type AliasTy: Clone + DebugWithInfcx + Hash + Ord; - type ParamTy: Clone + Debug + Hash + Ord; - type BoundTy: Clone + Debug + Hash + Ord; - type PlaceholderTy: Clone + Debug + Hash + Ord; - type InferTy: Clone + DebugWithInfcx + Hash + Ord; - - // Things stored inside of tys - type ErrorGuaranteed: Clone + Debug + Hash + Ord; - type BoundExistentialPredicates: Clone + DebugWithInfcx + Hash + Ord; - type PolyFnSig: Clone + DebugWithInfcx + Hash + Ord; - type AllocId: Clone + Debug + Hash + Ord; - - // Kinds of consts - type Const: Clone + DebugWithInfcx + Hash + Ord; - type InferConst: Clone + DebugWithInfcx + Hash + Ord; - type AliasConst: Clone + DebugWithInfcx + Hash + Ord; - type PlaceholderConst: Clone + Debug + Hash + Ord; - type ParamConst: Clone + Debug + Hash + Ord; - type BoundConst: Clone + Debug + Hash + Ord; - type ValueConst: Clone + Debug + Hash + Ord; - type ExprConst: Clone + DebugWithInfcx + Hash + Ord; - - // Kinds of regions - type Region: Clone + DebugWithInfcx + Hash + Ord; - type EarlyBoundRegion: Clone + Debug + Hash + Ord; - type BoundRegion: Clone + Debug + Hash + Ord; - type FreeRegion: Clone + Debug + Hash + Ord; - type InferRegion: Clone + DebugWithInfcx + Hash + Ord; - type PlaceholderRegion: Clone + Debug + Hash + Ord; - - fn ty_and_mut_to_parts(ty_and_mut: Self::TypeAndMut) -> (Self::Ty, Mutability); -} - -/// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` -/// that produces `T` items. You could combine them with -/// `f(&iter.collect::>())`, but this requires allocating memory for the -/// `Vec`. -/// -/// This trait allows for faster implementations, intended for cases where the -/// number of items produced by the iterator is small. There is a blanket impl -/// for `T` items, but there is also a fallible impl for `Result` items. -pub trait CollectAndApply: Sized { - type Output; - - /// Produce a result of type `Self::Output` from `iter`. The result will - /// typically be produced by applying `f` on the elements produced by - /// `iter`, though this may not happen in some impls, e.g. if an error - /// occurred during iteration. - fn collect_and_apply(iter: I, f: F) -> Self::Output - where - I: Iterator, - F: FnOnce(&[T]) -> R; -} - -/// The blanket impl that always collects all elements and applies `f`. -impl CollectAndApply for T { - type Output = R; - - /// Equivalent to `f(&iter.collect::>())`. - fn collect_and_apply(mut iter: I, f: F) -> R - where - I: Iterator, - F: FnOnce(&[T]) -> R, - { - // This code is hot enough that it's worth specializing for the most - // common length lists, to avoid the overhead of `SmallVec` creation. - // Lengths 0, 1, and 2 typically account for ~95% of cases. If - // `size_hint` is incorrect a panic will occur via an `unwrap` or an - // `assert`. - match iter.size_hint() { - (0, Some(0)) => { - assert!(iter.next().is_none()); - f(&[]) - } - (1, Some(1)) => { - let t0 = iter.next().unwrap(); - assert!(iter.next().is_none()); - f(&[t0]) - } - (2, Some(2)) => { - let t0 = iter.next().unwrap(); - let t1 = iter.next().unwrap(); - assert!(iter.next().is_none()); - f(&[t0, t1]) - } - _ => f(&iter.collect::>()), - } - } -} - -/// A fallible impl that will fail, without calling `f`, if there are any -/// errors during collection. -impl CollectAndApply for Result { - type Output = Result; - - /// Equivalent to `Ok(f(&iter.collect::>>()?))`. - fn collect_and_apply(mut iter: I, f: F) -> Result - where - I: Iterator>, - F: FnOnce(&[T]) -> R, - { - // This code is hot enough that it's worth specializing for the most - // common length lists, to avoid the overhead of `SmallVec` creation. - // Lengths 0, 1, and 2 typically account for ~95% of cases. If - // `size_hint` is incorrect a panic will occur via an `unwrap` or an - // `assert`, unless a failure happens first, in which case the result - // will be an error anyway. - Ok(match iter.size_hint() { - (0, Some(0)) => { - assert!(iter.next().is_none()); - f(&[]) - } - (1, Some(1)) => { - let t0 = iter.next().unwrap()?; - assert!(iter.next().is_none()); - f(&[t0]) - } - (2, Some(2)) => { - let t0 = iter.next().unwrap()?; - let t1 = iter.next().unwrap()?; - assert!(iter.next().is_none()); - f(&[t0, t1]) - } - _ => f(&iter.collect::, _>>()?), - }) - } -} - -bitflags! { - /// Flags that we track on types. These flags are propagated upwards - /// through the type during type construction, so that we can quickly check - /// whether the type has various kinds of types in it without recursing - /// over the type itself. - pub struct TypeFlags: u32 { - // Does this have parameters? Used to determine whether substitution is - // required. - /// Does this have `Param`? - const HAS_TY_PARAM = 1 << 0; - /// Does this have `ReEarlyBound`? - const HAS_RE_PARAM = 1 << 1; - /// Does this have `ConstKind::Param`? - const HAS_CT_PARAM = 1 << 2; - - const HAS_PARAM = TypeFlags::HAS_TY_PARAM.bits - | TypeFlags::HAS_RE_PARAM.bits - | TypeFlags::HAS_CT_PARAM.bits; - - /// Does this have `Infer`? - const HAS_TY_INFER = 1 << 3; - /// Does this have `ReVar`? - const HAS_RE_INFER = 1 << 4; - /// Does this have `ConstKind::Infer`? - const HAS_CT_INFER = 1 << 5; - - /// Does this have inference variables? Used to determine whether - /// inference is required. - const HAS_INFER = TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_RE_INFER.bits - | TypeFlags::HAS_CT_INFER.bits; - - /// Does this have `Placeholder`? - const HAS_TY_PLACEHOLDER = 1 << 6; - /// Does this have `RePlaceholder`? - const HAS_RE_PLACEHOLDER = 1 << 7; - /// Does this have `ConstKind::Placeholder`? - const HAS_CT_PLACEHOLDER = 1 << 8; - - /// Does this have placeholders? - const HAS_PLACEHOLDER = TypeFlags::HAS_TY_PLACEHOLDER.bits - | TypeFlags::HAS_RE_PLACEHOLDER.bits - | TypeFlags::HAS_CT_PLACEHOLDER.bits; - - /// `true` if there are "names" of regions and so forth - /// that are local to a particular fn/inferctxt - const HAS_FREE_LOCAL_REGIONS = 1 << 9; - - /// `true` if there are "names" of types and regions and so forth - /// that are local to a particular fn - const HAS_FREE_LOCAL_NAMES = TypeFlags::HAS_TY_PARAM.bits - | TypeFlags::HAS_CT_PARAM.bits - | TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_CT_INFER.bits - | TypeFlags::HAS_TY_PLACEHOLDER.bits - | TypeFlags::HAS_CT_PLACEHOLDER.bits - // We consider 'freshened' types and constants - // to depend on a particular fn. - // The freshening process throws away information, - // which can make things unsuitable for use in a global - // cache. Note that there is no 'fresh lifetime' flag - - // freshening replaces all lifetimes with `ReErased`, - // which is different from how types/const are freshened. - | TypeFlags::HAS_TY_FRESH.bits - | TypeFlags::HAS_CT_FRESH.bits - | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits - | TypeFlags::HAS_RE_ERASED.bits; - - /// Does this have `Projection`? - const HAS_TY_PROJECTION = 1 << 10; - /// Does this have `Inherent`? - const HAS_TY_INHERENT = 1 << 11; - /// Does this have `Opaque`? - const HAS_TY_OPAQUE = 1 << 12; - /// Does this have `ConstKind::Unevaluated`? - const HAS_CT_PROJECTION = 1 << 13; - - /// Could this type be normalized further? - const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits - | TypeFlags::HAS_TY_OPAQUE.bits - | TypeFlags::HAS_TY_INHERENT.bits - | TypeFlags::HAS_CT_PROJECTION.bits; - - /// Is an error type/const reachable? - const HAS_ERROR = 1 << 14; - - /// Does this have any region that "appears free" in the type? - /// Basically anything but `ReLateBound` and `ReErased`. - const HAS_FREE_REGIONS = 1 << 15; - - /// Does this have any `ReLateBound` regions? - const HAS_RE_LATE_BOUND = 1 << 16; - /// Does this have any `Bound` types? - const HAS_TY_LATE_BOUND = 1 << 17; - /// Does this have any `ConstKind::Bound` consts? - const HAS_CT_LATE_BOUND = 1 << 18; - /// Does this have any bound variables? - /// Used to check if a global bound is safe to evaluate. - const HAS_LATE_BOUND = TypeFlags::HAS_RE_LATE_BOUND.bits - | TypeFlags::HAS_TY_LATE_BOUND.bits - | TypeFlags::HAS_CT_LATE_BOUND.bits; - - /// Does this have any `ReErased` regions? - const HAS_RE_ERASED = 1 << 19; - - /// Does this value have parameters/placeholders/inference variables which could be - /// replaced later, in a way that would change the results of `impl` specialization? - const STILL_FURTHER_SPECIALIZABLE = 1 << 20; - - /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`? - const HAS_TY_FRESH = 1 << 21; - - /// Does this value have `InferConst::Fresh`? - const HAS_CT_FRESH = 1 << 22; - - /// Does this have `Generator` or `GeneratorWitness`? - const HAS_TY_GENERATOR = 1 << 23; - } -} - rustc_index::newtype_index! { /// A [De Bruijn index][dbi] is a standard means of representing /// regions (and perhaps later types) in a higher-ranked setting. In @@ -434,259 +167,6 @@ pub fn debug_bound_var( } } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[derive(Encodable, Decodable, HashStable_Generic)] -pub enum IntTy { - Isize, - I8, - I16, - I32, - I64, - I128, -} - -impl IntTy { - pub fn name_str(&self) -> &'static str { - match *self { - IntTy::Isize => "isize", - IntTy::I8 => "i8", - IntTy::I16 => "i16", - IntTy::I32 => "i32", - IntTy::I64 => "i64", - IntTy::I128 => "i128", - } - } - - pub fn bit_width(&self) -> Option { - Some(match *self { - IntTy::Isize => return None, - IntTy::I8 => 8, - IntTy::I16 => 16, - IntTy::I32 => 32, - IntTy::I64 => 64, - IntTy::I128 => 128, - }) - } - - pub fn normalize(&self, target_width: u32) -> Self { - match self { - IntTy::Isize => match target_width { - 16 => IntTy::I16, - 32 => IntTy::I32, - 64 => IntTy::I64, - _ => unreachable!(), - }, - _ => *self, - } - } - - pub fn to_unsigned(self) -> UintTy { - match self { - IntTy::Isize => UintTy::Usize, - IntTy::I8 => UintTy::U8, - IntTy::I16 => UintTy::U16, - IntTy::I32 => UintTy::U32, - IntTy::I64 => UintTy::U64, - IntTy::I128 => UintTy::U128, - } - } -} - -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] -#[derive(Encodable, Decodable, HashStable_Generic)] -pub enum UintTy { - Usize, - U8, - U16, - U32, - U64, - U128, -} - -impl UintTy { - pub fn name_str(&self) -> &'static str { - match *self { - UintTy::Usize => "usize", - UintTy::U8 => "u8", - UintTy::U16 => "u16", - UintTy::U32 => "u32", - UintTy::U64 => "u64", - UintTy::U128 => "u128", - } - } - - pub fn bit_width(&self) -> Option { - Some(match *self { - UintTy::Usize => return None, - UintTy::U8 => 8, - UintTy::U16 => 16, - UintTy::U32 => 32, - UintTy::U64 => 64, - UintTy::U128 => 128, - }) - } - - pub fn normalize(&self, target_width: u32) -> Self { - match self { - UintTy::Usize => match target_width { - 16 => UintTy::U16, - 32 => UintTy::U32, - 64 => UintTy::U64, - _ => unreachable!(), - }, - _ => *self, - } - } - - pub fn to_signed(self) -> IntTy { - match self { - UintTy::Usize => IntTy::Isize, - UintTy::U8 => IntTy::I8, - UintTy::U16 => IntTy::I16, - UintTy::U32 => IntTy::I32, - UintTy::U64 => IntTy::I64, - UintTy::U128 => IntTy::I128, - } - } -} - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[derive(Encodable, Decodable, HashStable_Generic)] -pub enum FloatTy { - F32, - F64, -} - -impl FloatTy { - pub fn name_str(self) -> &'static str { - match self { - FloatTy::F32 => "f32", - FloatTy::F64 => "f64", - } - } - - pub fn bit_width(self) -> u64 { - match self { - FloatTy::F32 => 32, - FloatTy::F64 => 64, - } - } -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum IntVarValue { - IntType(IntTy), - UintType(UintTy), -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct FloatVarValue(pub FloatTy); - -rustc_index::newtype_index! { - /// A **ty**pe **v**ariable **ID**. - #[debug_format = "?{}t"] - pub struct TyVid {} -} - -rustc_index::newtype_index! { - /// An **int**egral (`u32`, `i32`, `usize`, etc.) type **v**ariable **ID**. - #[debug_format = "?{}i"] - pub struct IntVid {} -} - -rustc_index::newtype_index! { - /// A **float**ing-point (`f32` or `f64`) type **v**ariable **ID**. - #[debug_format = "?{}f"] - pub struct FloatVid {} -} - -/// A placeholder for a type that hasn't been inferred yet. -/// -/// E.g., if we have an empty array (`[]`), then we create a fresh -/// type variable for the element type since we won't know until it's -/// used what the element type is supposed to be. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] -pub enum InferTy { - /// A type variable. - TyVar(TyVid), - /// An integral type variable (`{integer}`). - /// - /// These are created when the compiler sees an integer literal like - /// `1` that could be several different types (`u8`, `i32`, `u32`, etc.). - /// We don't know until it's used what type it's supposed to be, so - /// we create a fresh type variable. - IntVar(IntVid), - /// A floating-point type variable (`{float}`). - /// - /// These are created when the compiler sees an float literal like - /// `1.0` that could be either an `f32` or an `f64`. - /// We don't know until it's used what type it's supposed to be, so - /// we create a fresh type variable. - FloatVar(FloatVid), - - /// A [`FreshTy`][Self::FreshTy] is one that is generated as a replacement - /// for an unbound type variable. This is convenient for caching etc. See - /// `rustc_infer::infer::freshen` for more details. - /// - /// Compare with [`TyVar`][Self::TyVar]. - FreshTy(u32), - /// Like [`FreshTy`][Self::FreshTy], but as a replacement for [`IntVar`][Self::IntVar]. - FreshIntTy(u32), - /// Like [`FreshTy`][Self::FreshTy], but as a replacement for [`FloatVar`][Self::FloatVar]. - FreshFloatTy(u32), -} - -/// Raw `TyVid` are used as the unification key for `sub_relations`; -/// they carry no values. -impl UnifyKey for TyVid { - type Value = (); - #[inline] - fn index(&self) -> u32 { - self.as_u32() - } - #[inline] - fn from_index(i: u32) -> TyVid { - TyVid::from_u32(i) - } - fn tag() -> &'static str { - "TyVid" - } -} - -impl EqUnifyValue for IntVarValue {} - -impl UnifyKey for IntVid { - type Value = Option; - #[inline] // make this function eligible for inlining - it is quite hot. - fn index(&self) -> u32 { - self.as_u32() - } - #[inline] - fn from_index(i: u32) -> IntVid { - IntVid::from_u32(i) - } - fn tag() -> &'static str { - "IntVid" - } -} - -impl EqUnifyValue for FloatVarValue {} - -impl UnifyKey for FloatVid { - type Value = Option; - #[inline] - fn index(&self) -> u32 { - self.as_u32() - } - #[inline] - fn from_index(i: u32) -> FloatVid { - FloatVid::from_u32(i) - } - fn tag() -> &'static str { - "FloatVid" - } -} - #[derive(Copy, Clone, PartialEq, Eq, Decodable, Encodable, Hash, HashStable_Generic)] #[rustc_pass_by_value] pub enum Variance { @@ -756,34 +236,6 @@ impl Variance { } } -impl HashStable for InferTy { - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - use InferTy::*; - discriminant(self).hash_stable(ctx, hasher); - match self { - TyVar(_) | IntVar(_) | FloatVar(_) => { - panic!("type variables should not be hashed: {self:?}") - } - FreshTy(v) | FreshIntTy(v) | FreshFloatTy(v) => v.hash_stable(ctx, hasher), - } - } -} - -impl fmt::Debug for IntVarValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - IntVarValue::IntType(ref v) => v.fmt(f), - IntVarValue::UintType(ref v) => v.fmt(f), - } - } -} - -impl fmt::Debug for FloatVarValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - impl fmt::Debug for Variance { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match *self { @@ -795,20 +247,6 @@ impl fmt::Debug for Variance { } } -impl fmt::Display for InferTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use InferTy::*; - match *self { - TyVar(_) => write!(f, "_"), - IntVar(_) => write!(f, "{}", "{integer}"), - FloatVar(_) => write!(f, "{}", "{float}"), - FreshTy(v) => write!(f, "FreshTy({v})"), - FreshIntTy(v) => write!(f, "FreshIntTy({v})"), - FreshFloatTy(v) => write!(f, "FreshFloatTy({v})"), - } - } -} - rustc_index::newtype_index! { /// "Universes" are used during type- and trait-checking in the /// presence of `for<..>` binders to control what sets of names are diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs index 8c3cb22832299..9e10c65c20dc4 100644 --- a/compiler/rustc_type_ir/src/macros.rs +++ b/compiler/rustc_type_ir/src/macros.rs @@ -33,3 +33,20 @@ macro_rules! TrivialTypeTraversalImpls { )+ }; } + +/////////////////////////////////////////////////////////////////////////// +// Atomic structs +// +// For things that don't carry any arena-allocated data (and are +// copy...), just add them to this list. + +TrivialTypeTraversalImpls! { + (), + bool, + usize, + u16, + u32, + u64, + String, + crate::DebruijnIndex, +} diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs new file mode 100644 index 0000000000000..0006eec4d3007 --- /dev/null +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -0,0 +1,412 @@ +use rustc_data_structures::stable_hasher::HashStable; +use rustc_serialize::{Decodable, Decoder, Encodable}; +use std::cmp::Ordering; +use std::fmt; +use std::hash; + +use crate::{ + DebruijnIndex, DebugWithInfcx, HashStableContext, InferCtxtLike, Interner, OptWithInfcx, + TyDecoder, TyEncoder, +}; + +use self::RegionKind::*; + +/// Representation of regions. Note that the NLL checker uses a distinct +/// representation of regions. For this reason, it internally replaces all the +/// regions with inference variables -- the index of the variable is then used +/// to index into internal NLL data structures. See `rustc_const_eval::borrow_check` +/// module for more information. +/// +/// Note: operations are on the wrapper `Region` type, which is interned, +/// rather than this type. +/// +/// ## The Region lattice within a given function +/// +/// In general, the region lattice looks like +/// +/// ```text +/// static ----------+-----...------+ (greatest) +/// | | | +/// early-bound and | | +/// free regions | | +/// | | | +/// | | | +/// empty(root) placeholder(U1) | +/// | / | +/// | / placeholder(Un) +/// empty(U1) -- / +/// | / +/// ... / +/// | / +/// empty(Un) -------- (smallest) +/// ``` +/// +/// Early-bound/free regions are the named lifetimes in scope from the +/// function declaration. They have relationships to one another +/// determined based on the declared relationships from the +/// function. +/// +/// Note that inference variables and bound regions are not included +/// in this diagram. In the case of inference variables, they should +/// be inferred to some other region from the diagram. In the case of +/// bound regions, they are excluded because they don't make sense to +/// include -- the diagram indicates the relationship between free +/// regions. +/// +/// ## Inference variables +/// +/// During region inference, we sometimes create inference variables, +/// represented as `ReVar`. These will be inferred by the code in +/// `infer::lexical_region_resolve` to some free region from the +/// lattice above (the minimal region that meets the +/// constraints). +/// +/// During NLL checking, where regions are defined differently, we +/// also use `ReVar` -- in that case, the index is used to index into +/// the NLL region checker's data structures. The variable may in fact +/// represent either a free region or an inference variable, in that +/// case. +/// +/// ## Bound Regions +/// +/// These are regions that are stored behind a binder and must be substituted +/// with some concrete region before being used. There are two kind of +/// bound regions: early-bound, which are bound in an item's `Generics`, +/// and are substituted by an `GenericArgs`, and late-bound, which are part of +/// higher-ranked types (e.g., `for<'a> fn(&'a ())`), and are substituted by +/// the likes of `liberate_late_bound_regions`. The distinction exists +/// because higher-ranked lifetimes aren't supported in all places. See [1][2]. +/// +/// Unlike `Param`s, bound regions are not supposed to exist "in the wild" +/// outside their binder, e.g., in types passed to type inference, and +/// should first be substituted (by placeholder regions, free regions, +/// or region variables). +/// +/// ## Placeholder and Free Regions +/// +/// One often wants to work with bound regions without knowing their precise +/// identity. For example, when checking a function, the lifetime of a borrow +/// can end up being assigned to some region parameter. In these cases, +/// it must be ensured that bounds on the region can't be accidentally +/// assumed without being checked. +/// +/// To do this, we replace the bound regions with placeholder markers, +/// which don't satisfy any relation not explicitly provided. +/// +/// There are two kinds of placeholder regions in rustc: `ReFree` and +/// `RePlaceholder`. When checking an item's body, `ReFree` is supposed +/// to be used. These also support explicit bounds: both the internally-stored +/// *scope*, which the region is assumed to outlive, as well as other +/// relations stored in the `FreeRegionMap`. Note that these relations +/// aren't checked when you `make_subregion` (or `eq_types`), only by +/// `resolve_regions_and_report_errors`. +/// +/// When working with higher-ranked types, some region relations aren't +/// yet known, so you can't just call `resolve_regions_and_report_errors`. +/// `RePlaceholder` is designed for this purpose. In these contexts, +/// there's also the risk that some inference variable laying around will +/// get unified with your placeholder region: if you want to check whether +/// `for<'a> Foo<'_>: 'a`, and you substitute your bound region `'a` +/// with a placeholder region `'%a`, the variable `'_` would just be +/// instantiated to the placeholder region `'%a`, which is wrong because +/// the inference variable is supposed to satisfy the relation +/// *for every value of the placeholder region*. To ensure that doesn't +/// happen, you can use `leak_check`. This is more clearly explained +/// by the [rustc dev guide]. +/// +/// [1]: https://smallcultfollowing.com/babysteps/blog/2013/10/29/intermingled-parameter-lists/ +/// [2]: https://smallcultfollowing.com/babysteps/blog/2013/11/04/intermingled-parameter-lists/ +/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html +pub enum RegionKind { + /// Region bound in a type or fn declaration which will be + /// substituted 'early' -- that is, at the same time when type + /// parameters are substituted. + ReEarlyBound(I::EarlyBoundRegion), + + /// Region bound in a function scope, which will be substituted when the + /// function is called. + ReLateBound(DebruijnIndex, I::BoundRegion), + + /// When checking a function body, the types of all arguments and so forth + /// that refer to bound region parameters are modified to refer to free + /// region parameters. + ReFree(I::FreeRegion), + + /// Static data that has an "infinite" lifetime. Top in the region lattice. + ReStatic, + + /// A region variable. Should not exist outside of type inference. + ReVar(I::InferRegion), + + /// A placeholder region -- basically, the higher-ranked version of `ReFree`. + /// Should not exist outside of type inference. + RePlaceholder(I::PlaceholderRegion), + + /// Erased region, used by trait selection, in MIR and during codegen. + ReErased, + + /// A region that resulted from some other error. Used exclusively for diagnostics. + ReError(I::ErrorGuaranteed), +} + +// This is manually implemented for `RegionKind` because `std::mem::discriminant` +// returns an opaque value that is `PartialEq` but not `PartialOrd` +#[inline] +const fn regionkind_discriminant(value: &RegionKind) -> usize { + match value { + ReEarlyBound(_) => 0, + ReLateBound(_, _) => 1, + ReFree(_) => 2, + ReStatic => 3, + ReVar(_) => 4, + RePlaceholder(_) => 5, + ReErased => 6, + ReError(_) => 7, + } +} + +// This is manually implemented because a derive would require `I: Copy` +impl Copy for RegionKind +where + I::EarlyBoundRegion: Copy, + I::BoundRegion: Copy, + I::FreeRegion: Copy, + I::InferRegion: Copy, + I::PlaceholderRegion: Copy, + I::ErrorGuaranteed: Copy, +{ +} + +// This is manually implemented because a derive would require `I: Clone` +impl Clone for RegionKind { + fn clone(&self) -> Self { + match self { + ReEarlyBound(r) => ReEarlyBound(r.clone()), + ReLateBound(d, r) => ReLateBound(*d, r.clone()), + ReFree(r) => ReFree(r.clone()), + ReStatic => ReStatic, + ReVar(r) => ReVar(r.clone()), + RePlaceholder(r) => RePlaceholder(r.clone()), + ReErased => ReErased, + ReError(r) => ReError(r.clone()), + } + } +} + +// This is manually implemented because a derive would require `I: PartialEq` +impl PartialEq for RegionKind { + #[inline] + fn eq(&self, other: &RegionKind) -> bool { + regionkind_discriminant(self) == regionkind_discriminant(other) + && match (self, other) { + (ReEarlyBound(a_r), ReEarlyBound(b_r)) => a_r == b_r, + (ReLateBound(a_d, a_r), ReLateBound(b_d, b_r)) => a_d == b_d && a_r == b_r, + (ReFree(a_r), ReFree(b_r)) => a_r == b_r, + (ReStatic, ReStatic) => true, + (ReVar(a_r), ReVar(b_r)) => a_r == b_r, + (RePlaceholder(a_r), RePlaceholder(b_r)) => a_r == b_r, + (ReErased, ReErased) => true, + (ReError(_), ReError(_)) => true, + _ => { + debug_assert!( + false, + "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}" + ); + true + } + } + } +} + +// This is manually implemented because a derive would require `I: Eq` +impl Eq for RegionKind {} + +// This is manually implemented because a derive would require `I: PartialOrd` +impl PartialOrd for RegionKind { + #[inline] + fn partial_cmp(&self, other: &RegionKind) -> Option { + Some(self.cmp(other)) + } +} + +// This is manually implemented because a derive would require `I: Ord` +impl Ord for RegionKind { + #[inline] + fn cmp(&self, other: &RegionKind) -> Ordering { + regionkind_discriminant(self).cmp(®ionkind_discriminant(other)).then_with(|| { + match (self, other) { + (ReEarlyBound(a_r), ReEarlyBound(b_r)) => a_r.cmp(b_r), + (ReLateBound(a_d, a_r), ReLateBound(b_d, b_r)) => { + a_d.cmp(b_d).then_with(|| a_r.cmp(b_r)) + } + (ReFree(a_r), ReFree(b_r)) => a_r.cmp(b_r), + (ReStatic, ReStatic) => Ordering::Equal, + (ReVar(a_r), ReVar(b_r)) => a_r.cmp(b_r), + (RePlaceholder(a_r), RePlaceholder(b_r)) => a_r.cmp(b_r), + (ReErased, ReErased) => Ordering::Equal, + _ => { + debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}"); + Ordering::Equal + } + } + }) + } +} + +// This is manually implemented because a derive would require `I: Hash` +impl hash::Hash for RegionKind { + fn hash(&self, state: &mut H) -> () { + regionkind_discriminant(self).hash(state); + match self { + ReEarlyBound(r) => r.hash(state), + ReLateBound(d, r) => { + d.hash(state); + r.hash(state) + } + ReFree(r) => r.hash(state), + ReStatic => (), + ReVar(r) => r.hash(state), + RePlaceholder(r) => r.hash(state), + ReErased => (), + ReError(_) => (), + } + } +} + +impl DebugWithInfcx for RegionKind { + fn fmt>( + 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) => write!(f, "{fr:?}"), + + ReStatic => f.write_str("ReStatic"), + + ReVar(vid) => write!(f, "{:?}", &this.wrap(vid)), + + RePlaceholder(placeholder) => write!(f, "RePlaceholder({placeholder:?})"), + + ReErased => f.write_str("ReErased"), + + ReError(_) => f.write_str("ReError"), + } + } +} +impl fmt::Debug for RegionKind { + 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 Encodable for RegionKind +where + I::EarlyBoundRegion: Encodable, + I::BoundRegion: Encodable, + I::FreeRegion: Encodable, + I::InferRegion: Encodable, + I::PlaceholderRegion: Encodable, +{ + fn encode(&self, e: &mut E) { + let disc = regionkind_discriminant(self); + match self { + ReEarlyBound(a) => e.emit_enum_variant(disc, |e| { + a.encode(e); + }), + ReLateBound(a, b) => e.emit_enum_variant(disc, |e| { + a.encode(e); + b.encode(e); + }), + ReFree(a) => e.emit_enum_variant(disc, |e| { + a.encode(e); + }), + ReStatic => e.emit_enum_variant(disc, |_| {}), + ReVar(a) => e.emit_enum_variant(disc, |e| { + a.encode(e); + }), + RePlaceholder(a) => e.emit_enum_variant(disc, |e| { + a.encode(e); + }), + ReErased => e.emit_enum_variant(disc, |_| {}), + ReError(_) => e.emit_enum_variant(disc, |_| {}), + } + } +} + +// This is manually implemented because a derive would require `I: Decodable` +impl> Decodable for RegionKind +where + I::EarlyBoundRegion: Decodable, + I::BoundRegion: Decodable, + I::FreeRegion: Decodable, + I::InferRegion: Decodable, + I::PlaceholderRegion: Decodable, + I::ErrorGuaranteed: Decodable, +{ + fn decode(d: &mut D) -> Self { + match Decoder::read_usize(d) { + 0 => ReEarlyBound(Decodable::decode(d)), + 1 => ReLateBound(Decodable::decode(d), Decodable::decode(d)), + 2 => ReFree(Decodable::decode(d)), + 3 => ReStatic, + 4 => ReVar(Decodable::decode(d)), + 5 => RePlaceholder(Decodable::decode(d)), + 6 => ReErased, + 7 => ReError(Decodable::decode(d)), + _ => panic!( + "{}", + format!( + "invalid enum variant tag while decoding `{}`, expected 0..{}", + "RegionKind", 8, + ) + ), + } + } +} + +// This is not a derived impl because a derive would require `I: HashStable` +impl HashStable for RegionKind +where + I::EarlyBoundRegion: HashStable, + I::BoundRegion: HashStable, + I::FreeRegion: HashStable, + I::InferRegion: HashStable, + I::PlaceholderRegion: HashStable, +{ + #[inline] + fn hash_stable( + &self, + hcx: &mut CTX, + hasher: &mut rustc_data_structures::stable_hasher::StableHasher, + ) { + std::mem::discriminant(self).hash_stable(hcx, hasher); + match self { + ReErased | ReStatic | ReError(_) => { + // No variant fields to hash for these ... + } + ReLateBound(d, r) => { + d.hash_stable(hcx, hasher); + r.hash_stable(hcx, hasher); + } + ReEarlyBound(r) => { + r.hash_stable(hcx, hasher); + } + ReFree(r) => { + r.hash_stable(hcx, hasher); + } + RePlaceholder(r) => { + r.hash_stable(hcx, hasher); + } + ReVar(_) => { + panic!("region variables should not be hashed: {self:?}") + } + } + } +} diff --git a/compiler/rustc_type_ir/src/structural_impls.rs b/compiler/rustc_type_ir/src/structural_impls.rs deleted file mode 100644 index 93b3674b51a56..0000000000000 --- a/compiler/rustc_type_ir/src/structural_impls.rs +++ /dev/null @@ -1,390 +0,0 @@ -//! This module contains implementations of the `TypeFoldable` and `TypeVisitable` -//! traits for various types in the Rust compiler. Most are written by hand, though -//! we've recently added some macros and proc-macros to help with the tedium. - -use crate::fold::{FallibleTypeFolder, TypeFoldable}; -use crate::visit::{TypeVisitable, TypeVisitor}; -use crate::{ConstKind, FloatTy, InferTy, IntTy, Interner, UintTy, UniverseIndex}; -use rustc_data_structures::sync::Lrc; -use rustc_index::{Idx, IndexVec}; - -use core::fmt; -use std::marker::PhantomData; -use std::mem; -use std::ops::ControlFlow; - -/////////////////////////////////////////////////////////////////////////// -// Atomic structs -// -// For things that don't carry any arena-allocated data (and are -// copy...), just add them to this list. - -TrivialTypeTraversalImpls! { - (), - bool, - usize, - u16, - u32, - u64, - String, - crate::DebruijnIndex, -} - -/////////////////////////////////////////////////////////////////////////// -// Traversal implementations. - -impl, U: TypeFoldable> TypeFoldable for (T, U) { - fn try_fold_with>(self, folder: &mut F) -> Result<(T, U), F::Error> { - Ok((self.0.try_fold_with(folder)?, self.1.try_fold_with(folder)?)) - } -} - -impl, U: TypeVisitable> TypeVisitable for (T, U) { - fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - self.0.visit_with(visitor)?; - self.1.visit_with(visitor) - } -} - -impl, B: TypeFoldable, C: TypeFoldable> TypeFoldable - for (A, B, C) -{ - fn try_fold_with>( - self, - folder: &mut F, - ) -> Result<(A, B, C), F::Error> { - Ok(( - self.0.try_fold_with(folder)?, - self.1.try_fold_with(folder)?, - self.2.try_fold_with(folder)?, - )) - } -} - -impl, B: TypeVisitable, C: TypeVisitable> TypeVisitable - for (A, B, C) -{ - fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - self.0.visit_with(visitor)?; - self.1.visit_with(visitor)?; - self.2.visit_with(visitor) - } -} - -impl> TypeFoldable for Option { - fn try_fold_with>(self, folder: &mut F) -> Result { - Ok(match self { - Some(v) => Some(v.try_fold_with(folder)?), - None => None, - }) - } -} - -impl> TypeVisitable for Option { - fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - match self { - Some(v) => v.visit_with(visitor), - None => ControlFlow::Continue(()), - } - } -} - -impl, E: TypeFoldable> TypeFoldable for Result { - fn try_fold_with>(self, folder: &mut F) -> Result { - Ok(match self { - Ok(v) => Ok(v.try_fold_with(folder)?), - Err(e) => Err(e.try_fold_with(folder)?), - }) - } -} - -impl, E: TypeVisitable> TypeVisitable for Result { - fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - match self { - Ok(v) => v.visit_with(visitor), - Err(e) => e.visit_with(visitor), - } - } -} - -impl> TypeFoldable for Lrc { - fn try_fold_with>(mut self, folder: &mut F) -> Result { - // We merely want to replace the contained `T`, if at all possible, - // so that we don't needlessly allocate a new `Lrc` or indeed clone - // the contained type. - unsafe { - // First step is to ensure that we have a unique reference to - // the contained type, which `Lrc::make_mut` will accomplish (by - // allocating a new `Lrc` and cloning the `T` only if required). - // This is done *before* casting to `Lrc>` so that - // panicking during `make_mut` does not leak the `T`. - Lrc::make_mut(&mut self); - - // Casting to `Lrc>` is safe because `ManuallyDrop` - // is `repr(transparent)`. - let ptr = Lrc::into_raw(self).cast::>(); - let mut unique = Lrc::from_raw(ptr); - - // Call to `Lrc::make_mut` above guarantees that `unique` is the - // sole reference to the contained value, so we can avoid doing - // a checked `get_mut` here. - let slot = Lrc::get_mut_unchecked(&mut unique); - - // Semantically move the contained type out from `unique`, fold - // it, then move the folded value back into `unique`. Should - // folding fail, `ManuallyDrop` ensures that the "moved-out" - // value is not re-dropped. - let owned = mem::ManuallyDrop::take(slot); - let folded = owned.try_fold_with(folder)?; - *slot = mem::ManuallyDrop::new(folded); - - // Cast back to `Lrc`. - Ok(Lrc::from_raw(Lrc::into_raw(unique).cast())) - } - } -} - -impl> TypeVisitable for Lrc { - fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - (**self).visit_with(visitor) - } -} - -impl> TypeFoldable for Box { - fn try_fold_with>(mut self, folder: &mut F) -> Result { - *self = (*self).try_fold_with(folder)?; - Ok(self) - } -} - -impl> TypeVisitable for Box { - fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - (**self).visit_with(visitor) - } -} - -impl> TypeFoldable for Vec { - fn try_fold_with>(self, folder: &mut F) -> Result { - self.into_iter().map(|t| t.try_fold_with(folder)).collect() - } -} - -impl> TypeVisitable for Vec { - fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - self.iter().try_for_each(|t| t.visit_with(visitor)) - } -} - -// `TypeFoldable` isn't impl'd for `&[T]`. It doesn't make sense in the general -// case, because we can't return a new slice. But note that there are a couple -// of trivial impls of `TypeFoldable` for specific slice types elsewhere. - -impl> TypeVisitable for &[T] { - fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - self.iter().try_for_each(|t| t.visit_with(visitor)) - } -} - -impl> TypeVisitable for Box<[T]> { - fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - self.iter().try_for_each(|t| t.visit_with(visitor)) - } -} - -impl, Ix: Idx> TypeFoldable for IndexVec { - fn try_fold_with>(self, folder: &mut F) -> Result { - self.raw.try_fold_with(folder).map(IndexVec::from_raw) - } -} - -impl, Ix: Idx> TypeVisitable for IndexVec { - fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - self.iter().try_for_each(|t| t.visit_with(visitor)) - } -} - -/////////////////////////////////////////////////// -// Debug impls - -pub trait InferCtxtLike { - fn universe_of_ty(&self, ty: I::InferTy) -> Option; - fn universe_of_lt(&self, lt: I::InferRegion) -> Option; - fn universe_of_ct(&self, ct: I::InferConst) -> Option; -} - -impl InferCtxtLike for core::convert::Infallible { - fn universe_of_ty(&self, _ty: ::InferTy) -> Option { - match *self {} - } - fn universe_of_ct(&self, _ct: ::InferConst) -> Option { - match *self {} - } - fn universe_of_lt(&self, _lt: ::InferRegion) -> Option { - match *self {} - } -} - -pub trait DebugWithInfcx: fmt::Debug { - fn fmt>( - this: OptWithInfcx<'_, I, InfCtx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result; -} - -impl + ?Sized> DebugWithInfcx for &'_ T { - fn fmt>( - this: OptWithInfcx<'_, I, InfCtx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - >::fmt(this.map(|&data| data), f) - } -} -impl> DebugWithInfcx for [T] { - fn fmt>( - 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, T> { - pub data: T, - pub infcx: Option<&'a InfCtx>, - _interner: PhantomData, -} - -impl, T: Copy> Copy for OptWithInfcx<'_, I, InfCtx, T> {} -impl, 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, 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(self, u: U) -> OptWithInfcx<'a, I, InfCtx, U> { - OptWithInfcx { data: u, infcx: self.infcx, _interner: PhantomData } - } - - pub fn map(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, T: DebugWithInfcx> 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()) - } -} - -impl fmt::Debug for UintTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.name_str()) - } -} - -impl fmt::Debug for FloatTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.name_str()) - } -} - -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> DebugWithInfcx for InferTy { - fn fmt>( - 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 fmt::Debug for ConstKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - OptWithInfcx::new_no_ctx(self).fmt(f) - } -} -impl DebugWithInfcx for ConstKind { - fn fmt>( - 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)), - } - } -} diff --git a/compiler/rustc_type_ir/src/ty_info.rs b/compiler/rustc_type_ir/src/ty_info.rs index 4e5d424886ad8..d986e310c3297 100644 --- a/compiler/rustc_type_ir/src/ty_info.rs +++ b/compiler/rustc_type_ir/src/ty_info.rs @@ -1,13 +1,8 @@ -use std::{ - cmp::Ordering, - hash::{Hash, Hasher}, - ops::Deref, -}; - -use rustc_data_structures::{ - fingerprint::Fingerprint, - stable_hasher::{HashStable, StableHasher}, -}; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use std::cmp::Ordering; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; use crate::{DebruijnIndex, TypeFlags}; diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/ty_kind.rs similarity index 60% rename from compiler/rustc_type_ir/src/sty.rs rename to compiler/rustc_type_ir/src/ty_kind.rs index 5ca77701eb01a..064645740e34c 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -1,23 +1,20 @@ #![allow(rustc::usage_of_ty_tykind)] +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::unify::{EqUnifyValue, UnifyKey}; +use rustc_serialize::{Decodable, Decoder, Encodable}; use std::cmp::Ordering; +use std::mem::discriminant; use std::{fmt, hash}; -use crate::FloatTy; use crate::HashStableContext; -use crate::IntTy; use crate::Interner; use crate::TyDecoder; use crate::TyEncoder; -use crate::UintTy; use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, OptWithInfcx}; -use self::RegionKind::*; use self::TyKind::*; -use rustc_data_structures::stable_hasher::HashStable; -use rustc_serialize::{Decodable, Decoder, Encodable}; - /// The movability of a generator / closure literal: /// whether a generator contains self-references, causing it to be `!Unpin`. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, Debug, Copy)] @@ -914,620 +911,347 @@ where } } -/// Represents a constant in Rust. -// #[derive(derive_more::From)] -pub enum ConstKind { - /// A const generic parameter. - Param(I::ParamConst), - - /// Infer the value of the const. - Infer(I::InferConst), - - /// Bound const variable, used only when preparing a trait query. - Bound(DebruijnIndex, I::BoundConst), - - /// A placeholder const - universally quantified higher-ranked const. - Placeholder(I::PlaceholderConst), - - /// An unnormalized const item such as an anon const or assoc const or free const item. - /// Right now anything other than anon consts does not actually work properly but this - /// should - Unevaluated(I::AliasConst), - - /// Used to hold computed value. - Value(I::ValueConst), - - /// A placeholder for a const which could not be computed; this is - /// propagated to avoid useless error messages. - Error(I::ErrorGuaranteed), - - /// Unevaluated non-const-item, used by `feature(generic_const_exprs)` to represent - /// const arguments such as `N + 1` or `foo(N)` - Expr(I::ExprConst), +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Encodable, Decodable, HashStable_Generic)] +pub enum IntTy { + Isize, + I8, + I16, + I32, + I64, + I128, } -const fn const_kind_discriminant(value: &ConstKind) -> usize { - match value { - ConstKind::Param(_) => 0, - ConstKind::Infer(_) => 1, - ConstKind::Bound(_, _) => 2, - ConstKind::Placeholder(_) => 3, - ConstKind::Unevaluated(_) => 4, - ConstKind::Value(_) => 5, - ConstKind::Error(_) => 6, - ConstKind::Expr(_) => 7, +impl IntTy { + pub fn name_str(&self) -> &'static str { + match *self { + IntTy::Isize => "isize", + IntTy::I8 => "i8", + IntTy::I16 => "i16", + IntTy::I32 => "i32", + IntTy::I64 => "i64", + IntTy::I128 => "i128", + } } -} -impl hash::Hash for ConstKind { - fn hash(&self, state: &mut H) { - const_kind_discriminant(self).hash(state); + pub fn bit_width(&self) -> Option { + Some(match *self { + IntTy::Isize => return None, + IntTy::I8 => 8, + IntTy::I16 => 16, + IntTy::I32 => 32, + IntTy::I64 => 64, + IntTy::I128 => 128, + }) + } + + pub fn normalize(&self, target_width: u32) -> Self { match self { - ConstKind::Param(p) => p.hash(state), - ConstKind::Infer(i) => i.hash(state), - ConstKind::Bound(d, b) => { - d.hash(state); - b.hash(state); - } - ConstKind::Placeholder(p) => p.hash(state), - ConstKind::Unevaluated(u) => u.hash(state), - ConstKind::Value(v) => v.hash(state), - ConstKind::Error(e) => e.hash(state), - ConstKind::Expr(e) => e.hash(state), + IntTy::Isize => match target_width { + 16 => IntTy::I16, + 32 => IntTy::I32, + 64 => IntTy::I64, + _ => unreachable!(), + }, + _ => *self, } } -} -impl HashStable for ConstKind -where - I::ParamConst: HashStable, - I::InferConst: HashStable, - I::BoundConst: HashStable, - I::PlaceholderConst: HashStable, - I::AliasConst: HashStable, - I::ValueConst: HashStable, - I::ErrorGuaranteed: HashStable, - I::ExprConst: HashStable, -{ - fn hash_stable( - &self, - hcx: &mut CTX, - hasher: &mut rustc_data_structures::stable_hasher::StableHasher, - ) { - const_kind_discriminant(self).hash_stable(hcx, hasher); + pub fn to_unsigned(self) -> UintTy { match self { - ConstKind::Param(p) => p.hash_stable(hcx, hasher), - ConstKind::Infer(i) => i.hash_stable(hcx, hasher), - ConstKind::Bound(d, b) => { - d.hash_stable(hcx, hasher); - b.hash_stable(hcx, hasher); - } - ConstKind::Placeholder(p) => p.hash_stable(hcx, hasher), - ConstKind::Unevaluated(u) => u.hash_stable(hcx, hasher), - ConstKind::Value(v) => v.hash_stable(hcx, hasher), - ConstKind::Error(e) => e.hash_stable(hcx, hasher), - ConstKind::Expr(e) => e.hash_stable(hcx, hasher), + IntTy::Isize => UintTy::Usize, + IntTy::I8 => UintTy::U8, + IntTy::I16 => UintTy::U16, + IntTy::I32 => UintTy::U32, + IntTy::I64 => UintTy::U64, + IntTy::I128 => UintTy::U128, } } } -impl> Decodable for ConstKind -where - I::ParamConst: Decodable, - I::InferConst: Decodable, - I::BoundConst: Decodable, - I::PlaceholderConst: Decodable, - I::AliasConst: Decodable, - I::ValueConst: Decodable, - I::ErrorGuaranteed: Decodable, - I::ExprConst: Decodable, -{ - fn decode(d: &mut D) -> Self { - match Decoder::read_usize(d) { - 0 => ConstKind::Param(Decodable::decode(d)), - 1 => ConstKind::Infer(Decodable::decode(d)), - 2 => ConstKind::Bound(Decodable::decode(d), Decodable::decode(d)), - 3 => ConstKind::Placeholder(Decodable::decode(d)), - 4 => ConstKind::Unevaluated(Decodable::decode(d)), - 5 => ConstKind::Value(Decodable::decode(d)), - 6 => ConstKind::Error(Decodable::decode(d)), - 7 => ConstKind::Expr(Decodable::decode(d)), - _ => panic!( - "{}", - format!( - "invalid enum variant tag while decoding `{}`, expected 0..{}", - "ConstKind", 8, - ) - ), +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] +#[derive(Encodable, Decodable, HashStable_Generic)] +pub enum UintTy { + Usize, + U8, + U16, + U32, + U64, + U128, +} + +impl UintTy { + pub fn name_str(&self) -> &'static str { + match *self { + UintTy::Usize => "usize", + UintTy::U8 => "u8", + UintTy::U16 => "u16", + UintTy::U32 => "u32", + UintTy::U64 => "u64", + UintTy::U128 => "u128", } } -} -impl> Encodable for ConstKind -where - I::ParamConst: Encodable, - I::InferConst: Encodable, - I::BoundConst: Encodable, - I::PlaceholderConst: Encodable, - I::AliasConst: Encodable, - I::ValueConst: Encodable, - I::ErrorGuaranteed: Encodable, - I::ExprConst: Encodable, -{ - fn encode(&self, e: &mut E) { - let disc = const_kind_discriminant(self); + pub fn bit_width(&self) -> Option { + Some(match *self { + UintTy::Usize => return None, + UintTy::U8 => 8, + UintTy::U16 => 16, + UintTy::U32 => 32, + UintTy::U64 => 64, + UintTy::U128 => 128, + }) + } + + pub fn normalize(&self, target_width: u32) -> Self { match self { - ConstKind::Param(p) => e.emit_enum_variant(disc, |e| p.encode(e)), - ConstKind::Infer(i) => e.emit_enum_variant(disc, |e| i.encode(e)), - ConstKind::Bound(d, b) => e.emit_enum_variant(disc, |e| { - d.encode(e); - b.encode(e); - }), - ConstKind::Placeholder(p) => e.emit_enum_variant(disc, |e| p.encode(e)), - ConstKind::Unevaluated(u) => e.emit_enum_variant(disc, |e| u.encode(e)), - ConstKind::Value(v) => e.emit_enum_variant(disc, |e| v.encode(e)), - ConstKind::Error(er) => e.emit_enum_variant(disc, |e| er.encode(e)), - ConstKind::Expr(ex) => e.emit_enum_variant(disc, |e| ex.encode(e)), + UintTy::Usize => match target_width { + 16 => UintTy::U16, + 32 => UintTy::U32, + 64 => UintTy::U64, + _ => unreachable!(), + }, + _ => *self, } } -} -impl PartialOrd for ConstKind { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) + pub fn to_signed(self) -> IntTy { + match self { + UintTy::Usize => IntTy::Isize, + UintTy::U8 => IntTy::I8, + UintTy::U16 => IntTy::I16, + UintTy::U32 => IntTy::I32, + UintTy::U64 => IntTy::I64, + UintTy::U128 => IntTy::I128, + } } } -impl Ord for ConstKind { - fn cmp(&self, other: &Self) -> Ordering { - const_kind_discriminant(self) - .cmp(&const_kind_discriminant(other)) - .then_with(|| match (self, other) { - (ConstKind::Param(p1), ConstKind::Param(p2)) => p1.cmp(p2), - (ConstKind::Infer(i1), ConstKind::Infer(i2)) => i1.cmp(i2), - (ConstKind::Bound(d1, b1), ConstKind::Bound(d2, b2)) => d1.cmp(d2).then_with(|| b1.cmp(b2)), - (ConstKind::Placeholder(p1), ConstKind::Placeholder(p2)) => p1.cmp(p2), - (ConstKind::Unevaluated(u1), ConstKind::Unevaluated(u2)) => u1.cmp(u2), - (ConstKind::Value(v1), ConstKind::Value(v2)) => v1.cmp(v2), - (ConstKind::Error(e1), ConstKind::Error(e2)) => e1.cmp(e2), - (ConstKind::Expr(e1), ConstKind::Expr(e2)) => e1.cmp(e2), - _ => { - debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}"); - Ordering::Equal - } - }) - } +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Encodable, Decodable, HashStable_Generic)] +pub enum FloatTy { + F32, + F64, } -impl PartialEq for ConstKind { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::Param(l0), Self::Param(r0)) => l0 == r0, - (Self::Infer(l0), Self::Infer(r0)) => l0 == r0, - (Self::Bound(l0, l1), Self::Bound(r0, r1)) => l0 == r0 && l1 == r1, - (Self::Placeholder(l0), Self::Placeholder(r0)) => l0 == r0, - (Self::Unevaluated(l0), Self::Unevaluated(r0)) => l0 == r0, - (Self::Value(l0), Self::Value(r0)) => l0 == r0, - (Self::Error(l0), Self::Error(r0)) => l0 == r0, - (Self::Expr(l0), Self::Expr(r0)) => l0 == r0, - _ => false, +impl FloatTy { + pub fn name_str(self) -> &'static str { + match self { + FloatTy::F32 => "f32", + FloatTy::F64 => "f64", } } -} - -impl Eq for ConstKind {} -impl Clone for ConstKind { - fn clone(&self) -> Self { + pub fn bit_width(self) -> u64 { match self { - Self::Param(arg0) => Self::Param(arg0.clone()), - Self::Infer(arg0) => Self::Infer(arg0.clone()), - Self::Bound(arg0, arg1) => Self::Bound(arg0.clone(), arg1.clone()), - Self::Placeholder(arg0) => Self::Placeholder(arg0.clone()), - Self::Unevaluated(arg0) => Self::Unevaluated(arg0.clone()), - Self::Value(arg0) => Self::Value(arg0.clone()), - Self::Error(arg0) => Self::Error(arg0.clone()), - Self::Expr(arg0) => Self::Expr(arg0.clone()), + FloatTy::F32 => 32, + FloatTy::F64 => 64, } } } -/// Representation of regions. Note that the NLL checker uses a distinct -/// representation of regions. For this reason, it internally replaces all the -/// regions with inference variables -- the index of the variable is then used -/// to index into internal NLL data structures. See `rustc_const_eval::borrow_check` -/// module for more information. -/// -/// Note: operations are on the wrapper `Region` type, which is interned, -/// rather than this type. -/// -/// ## The Region lattice within a given function -/// -/// In general, the region lattice looks like -/// -/// ```text -/// static ----------+-----...------+ (greatest) -/// | | | -/// early-bound and | | -/// free regions | | -/// | | | -/// | | | -/// empty(root) placeholder(U1) | -/// | / | -/// | / placeholder(Un) -/// empty(U1) -- / -/// | / -/// ... / -/// | / -/// empty(Un) -------- (smallest) -/// ``` -/// -/// Early-bound/free regions are the named lifetimes in scope from the -/// function declaration. They have relationships to one another -/// determined based on the declared relationships from the -/// function. -/// -/// Note that inference variables and bound regions are not included -/// in this diagram. In the case of inference variables, they should -/// be inferred to some other region from the diagram. In the case of -/// bound regions, they are excluded because they don't make sense to -/// include -- the diagram indicates the relationship between free -/// regions. -/// -/// ## Inference variables -/// -/// During region inference, we sometimes create inference variables, -/// represented as `ReVar`. These will be inferred by the code in -/// `infer::lexical_region_resolve` to some free region from the -/// lattice above (the minimal region that meets the -/// constraints). -/// -/// During NLL checking, where regions are defined differently, we -/// also use `ReVar` -- in that case, the index is used to index into -/// the NLL region checker's data structures. The variable may in fact -/// represent either a free region or an inference variable, in that -/// case. -/// -/// ## Bound Regions -/// -/// These are regions that are stored behind a binder and must be substituted -/// with some concrete region before being used. There are two kind of -/// bound regions: early-bound, which are bound in an item's `Generics`, -/// and are substituted by an `GenericArgs`, and late-bound, which are part of -/// higher-ranked types (e.g., `for<'a> fn(&'a ())`), and are substituted by -/// the likes of `liberate_late_bound_regions`. The distinction exists -/// because higher-ranked lifetimes aren't supported in all places. See [1][2]. -/// -/// Unlike `Param`s, bound regions are not supposed to exist "in the wild" -/// outside their binder, e.g., in types passed to type inference, and -/// should first be substituted (by placeholder regions, free regions, -/// or region variables). -/// -/// ## Placeholder and Free Regions -/// -/// One often wants to work with bound regions without knowing their precise -/// identity. For example, when checking a function, the lifetime of a borrow -/// can end up being assigned to some region parameter. In these cases, -/// it must be ensured that bounds on the region can't be accidentally -/// assumed without being checked. -/// -/// To do this, we replace the bound regions with placeholder markers, -/// which don't satisfy any relation not explicitly provided. -/// -/// There are two kinds of placeholder regions in rustc: `ReFree` and -/// `RePlaceholder`. When checking an item's body, `ReFree` is supposed -/// to be used. These also support explicit bounds: both the internally-stored -/// *scope*, which the region is assumed to outlive, as well as other -/// relations stored in the `FreeRegionMap`. Note that these relations -/// aren't checked when you `make_subregion` (or `eq_types`), only by -/// `resolve_regions_and_report_errors`. -/// -/// When working with higher-ranked types, some region relations aren't -/// yet known, so you can't just call `resolve_regions_and_report_errors`. -/// `RePlaceholder` is designed for this purpose. In these contexts, -/// there's also the risk that some inference variable laying around will -/// get unified with your placeholder region: if you want to check whether -/// `for<'a> Foo<'_>: 'a`, and you substitute your bound region `'a` -/// with a placeholder region `'%a`, the variable `'_` would just be -/// instantiated to the placeholder region `'%a`, which is wrong because -/// the inference variable is supposed to satisfy the relation -/// *for every value of the placeholder region*. To ensure that doesn't -/// happen, you can use `leak_check`. This is more clearly explained -/// by the [rustc dev guide]. -/// -/// [1]: https://smallcultfollowing.com/babysteps/blog/2013/10/29/intermingled-parameter-lists/ -/// [2]: https://smallcultfollowing.com/babysteps/blog/2013/11/04/intermingled-parameter-lists/ -/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html -pub enum RegionKind { - /// Region bound in a type or fn declaration which will be - /// substituted 'early' -- that is, at the same time when type - /// parameters are substituted. - ReEarlyBound(I::EarlyBoundRegion), - - /// Region bound in a function scope, which will be substituted when the - /// function is called. - ReLateBound(DebruijnIndex, I::BoundRegion), - - /// When checking a function body, the types of all arguments and so forth - /// that refer to bound region parameters are modified to refer to free - /// region parameters. - ReFree(I::FreeRegion), - - /// Static data that has an "infinite" lifetime. Top in the region lattice. - ReStatic, - - /// A region variable. Should not exist outside of type inference. - ReVar(I::InferRegion), - - /// A placeholder region -- basically, the higher-ranked version of `ReFree`. - /// Should not exist outside of type inference. - RePlaceholder(I::PlaceholderRegion), - - /// Erased region, used by trait selection, in MIR and during codegen. - ReErased, - - /// A region that resulted from some other error. Used exclusively for diagnostics. - ReError(I::ErrorGuaranteed), +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum IntVarValue { + IntType(IntTy), + UintType(UintTy), } -// This is manually implemented for `RegionKind` because `std::mem::discriminant` -// returns an opaque value that is `PartialEq` but not `PartialOrd` -#[inline] -const fn regionkind_discriminant(value: &RegionKind) -> usize { - match value { - ReEarlyBound(_) => 0, - ReLateBound(_, _) => 1, - ReFree(_) => 2, - ReStatic => 3, - ReVar(_) => 4, - RePlaceholder(_) => 5, - ReErased => 6, - ReError(_) => 7, - } +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct FloatVarValue(pub FloatTy); + +rustc_index::newtype_index! { + /// A **ty**pe **v**ariable **ID**. + #[debug_format = "?{}t"] + pub struct TyVid {} } -// This is manually implemented because a derive would require `I: Copy` -impl Copy for RegionKind -where - I::EarlyBoundRegion: Copy, - I::BoundRegion: Copy, - I::FreeRegion: Copy, - I::InferRegion: Copy, - I::PlaceholderRegion: Copy, - I::ErrorGuaranteed: Copy, -{ +rustc_index::newtype_index! { + /// An **int**egral (`u32`, `i32`, `usize`, etc.) type **v**ariable **ID**. + #[debug_format = "?{}i"] + pub struct IntVid {} } -// This is manually implemented because a derive would require `I: Clone` -impl Clone for RegionKind { - fn clone(&self) -> Self { - match self { - ReEarlyBound(r) => ReEarlyBound(r.clone()), - ReLateBound(d, r) => ReLateBound(*d, r.clone()), - ReFree(r) => ReFree(r.clone()), - ReStatic => ReStatic, - ReVar(r) => ReVar(r.clone()), - RePlaceholder(r) => RePlaceholder(r.clone()), - ReErased => ReErased, - ReError(r) => ReError(r.clone()), - } - } +rustc_index::newtype_index! { + /// A **float**ing-point (`f32` or `f64`) type **v**ariable **ID**. + #[debug_format = "?{}f"] + pub struct FloatVid {} } -// This is manually implemented because a derive would require `I: PartialEq` -impl PartialEq for RegionKind { +/// A placeholder for a type that hasn't been inferred yet. +/// +/// E.g., if we have an empty array (`[]`), then we create a fresh +/// type variable for the element type since we won't know until it's +/// used what the element type is supposed to be. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] +pub enum InferTy { + /// A type variable. + TyVar(TyVid), + /// An integral type variable (`{integer}`). + /// + /// These are created when the compiler sees an integer literal like + /// `1` that could be several different types (`u8`, `i32`, `u32`, etc.). + /// We don't know until it's used what type it's supposed to be, so + /// we create a fresh type variable. + IntVar(IntVid), + /// A floating-point type variable (`{float}`). + /// + /// These are created when the compiler sees an float literal like + /// `1.0` that could be either an `f32` or an `f64`. + /// We don't know until it's used what type it's supposed to be, so + /// we create a fresh type variable. + FloatVar(FloatVid), + + /// A [`FreshTy`][Self::FreshTy] is one that is generated as a replacement + /// for an unbound type variable. This is convenient for caching etc. See + /// `rustc_infer::infer::freshen` for more details. + /// + /// Compare with [`TyVar`][Self::TyVar]. + FreshTy(u32), + /// Like [`FreshTy`][Self::FreshTy], but as a replacement for [`IntVar`][Self::IntVar]. + FreshIntTy(u32), + /// Like [`FreshTy`][Self::FreshTy], but as a replacement for [`FloatVar`][Self::FloatVar]. + FreshFloatTy(u32), +} + +/// Raw `TyVid` are used as the unification key for `sub_relations`; +/// they carry no values. +impl UnifyKey for TyVid { + type Value = (); #[inline] - fn eq(&self, other: &RegionKind) -> bool { - regionkind_discriminant(self) == regionkind_discriminant(other) - && match (self, other) { - (ReEarlyBound(a_r), ReEarlyBound(b_r)) => a_r == b_r, - (ReLateBound(a_d, a_r), ReLateBound(b_d, b_r)) => a_d == b_d && a_r == b_r, - (ReFree(a_r), ReFree(b_r)) => a_r == b_r, - (ReStatic, ReStatic) => true, - (ReVar(a_r), ReVar(b_r)) => a_r == b_r, - (RePlaceholder(a_r), RePlaceholder(b_r)) => a_r == b_r, - (ReErased, ReErased) => true, - (ReError(_), ReError(_)) => true, - _ => { - debug_assert!( - false, - "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}" - ); - true - } - } + fn index(&self) -> u32 { + self.as_u32() + } + #[inline] + fn from_index(i: u32) -> TyVid { + TyVid::from_u32(i) + } + fn tag() -> &'static str { + "TyVid" } } -// This is manually implemented because a derive would require `I: Eq` -impl Eq for RegionKind {} +impl EqUnifyValue for IntVarValue {} -// This is manually implemented because a derive would require `I: PartialOrd` -impl PartialOrd for RegionKind { +impl UnifyKey for IntVid { + type Value = Option; + #[inline] // make this function eligible for inlining - it is quite hot. + fn index(&self) -> u32 { + self.as_u32() + } #[inline] - fn partial_cmp(&self, other: &RegionKind) -> Option { - Some(self.cmp(other)) + fn from_index(i: u32) -> IntVid { + IntVid::from_u32(i) + } + fn tag() -> &'static str { + "IntVid" } } -// This is manually implemented because a derive would require `I: Ord` -impl Ord for RegionKind { +impl EqUnifyValue for FloatVarValue {} + +impl UnifyKey for FloatVid { + type Value = Option; #[inline] - fn cmp(&self, other: &RegionKind) -> Ordering { - regionkind_discriminant(self).cmp(®ionkind_discriminant(other)).then_with(|| { - match (self, other) { - (ReEarlyBound(a_r), ReEarlyBound(b_r)) => a_r.cmp(b_r), - (ReLateBound(a_d, a_r), ReLateBound(b_d, b_r)) => { - a_d.cmp(b_d).then_with(|| a_r.cmp(b_r)) - } - (ReFree(a_r), ReFree(b_r)) => a_r.cmp(b_r), - (ReStatic, ReStatic) => Ordering::Equal, - (ReVar(a_r), ReVar(b_r)) => a_r.cmp(b_r), - (RePlaceholder(a_r), RePlaceholder(b_r)) => a_r.cmp(b_r), - (ReErased, ReErased) => Ordering::Equal, - _ => { - debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}"); - Ordering::Equal - } - } - }) + fn index(&self) -> u32 { + self.as_u32() + } + #[inline] + fn from_index(i: u32) -> FloatVid { + FloatVid::from_u32(i) + } + fn tag() -> &'static str { + "FloatVid" } } -// This is manually implemented because a derive would require `I: Hash` -impl hash::Hash for RegionKind { - fn hash(&self, state: &mut H) -> () { - regionkind_discriminant(self).hash(state); +impl HashStable for InferTy { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + use InferTy::*; + discriminant(self).hash_stable(ctx, hasher); match self { - ReEarlyBound(r) => r.hash(state), - ReLateBound(d, r) => { - d.hash(state); - r.hash(state) + TyVar(_) | IntVar(_) | FloatVar(_) => { + panic!("type variables should not be hashed: {self:?}") } - ReFree(r) => r.hash(state), - ReStatic => (), - ReVar(r) => r.hash(state), - RePlaceholder(r) => r.hash(state), - ReErased => (), - ReError(_) => (), + FreshTy(v) | FreshIntTy(v) | FreshFloatTy(v) => v.hash_stable(ctx, hasher), } } } -impl DebugWithInfcx for RegionKind { - fn fmt>( - 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) => write!(f, "{fr:?}"), - - ReStatic => f.write_str("ReStatic"), - - ReVar(vid) => write!(f, "{:?}", &this.wrap(vid)), - - RePlaceholder(placeholder) => write!(f, "RePlaceholder({placeholder:?})"), +impl fmt::Debug for IntVarValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + IntVarValue::IntType(ref v) => v.fmt(f), + IntVarValue::UintType(ref v) => v.fmt(f), + } + } +} - ReErased => f.write_str("ReErased"), +impl fmt::Debug for FloatVarValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} - ReError(_) => f.write_str("ReError"), +impl fmt::Display for InferTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use InferTy::*; + match *self { + TyVar(_) => write!(f, "_"), + IntVar(_) => write!(f, "{}", "{integer}"), + FloatVar(_) => write!(f, "{}", "{float}"), + FreshTy(v) => write!(f, "FreshTy({v})"), + FreshIntTy(v) => write!(f, "FreshIntTy({v})"), + FreshFloatTy(v) => write!(f, "FreshFloatTy({v})"), } } } -impl fmt::Debug for RegionKind { + +impl fmt::Debug for IntTy { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - OptWithInfcx::new_no_ctx(self).fmt(f) + write!(f, "{}", self.name_str()) } } -// This is manually implemented because a derive would require `I: Encodable` -impl Encodable for RegionKind -where - I::EarlyBoundRegion: Encodable, - I::BoundRegion: Encodable, - I::FreeRegion: Encodable, - I::InferRegion: Encodable, - I::PlaceholderRegion: Encodable, -{ - fn encode(&self, e: &mut E) { - let disc = regionkind_discriminant(self); - match self { - ReEarlyBound(a) => e.emit_enum_variant(disc, |e| { - a.encode(e); - }), - ReLateBound(a, b) => e.emit_enum_variant(disc, |e| { - a.encode(e); - b.encode(e); - }), - ReFree(a) => e.emit_enum_variant(disc, |e| { - a.encode(e); - }), - ReStatic => e.emit_enum_variant(disc, |_| {}), - ReVar(a) => e.emit_enum_variant(disc, |e| { - a.encode(e); - }), - RePlaceholder(a) => e.emit_enum_variant(disc, |e| { - a.encode(e); - }), - ReErased => e.emit_enum_variant(disc, |_| {}), - ReError(_) => e.emit_enum_variant(disc, |_| {}), - } +impl fmt::Debug for UintTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name_str()) } } -// This is manually implemented because a derive would require `I: Decodable` -impl> Decodable for RegionKind -where - I::EarlyBoundRegion: Decodable, - I::BoundRegion: Decodable, - I::FreeRegion: Decodable, - I::InferRegion: Decodable, - I::PlaceholderRegion: Decodable, - I::ErrorGuaranteed: Decodable, -{ - fn decode(d: &mut D) -> Self { - match Decoder::read_usize(d) { - 0 => ReEarlyBound(Decodable::decode(d)), - 1 => ReLateBound(Decodable::decode(d), Decodable::decode(d)), - 2 => ReFree(Decodable::decode(d)), - 3 => ReStatic, - 4 => ReVar(Decodable::decode(d)), - 5 => RePlaceholder(Decodable::decode(d)), - 6 => ReErased, - 7 => ReError(Decodable::decode(d)), - _ => panic!( - "{}", - format!( - "invalid enum variant tag while decoding `{}`, expected 0..{}", - "RegionKind", 8, - ) - ), +impl fmt::Debug for FloatTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name_str()) + } +} + +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:?})"), } } } -// This is not a derived impl because a derive would require `I: HashStable` -impl HashStable for RegionKind -where - I::EarlyBoundRegion: HashStable, - I::BoundRegion: HashStable, - I::FreeRegion: HashStable, - I::InferRegion: HashStable, - I::PlaceholderRegion: HashStable, -{ - #[inline] - fn hash_stable( - &self, - hcx: &mut CTX, - hasher: &mut rustc_data_structures::stable_hasher::StableHasher, - ) { - std::mem::discriminant(self).hash_stable(hcx, hasher); - match self { - ReErased | ReStatic | ReError(_) => { - // No variant fields to hash for these ... - } - ReLateBound(d, r) => { - d.hash_stable(hcx, hasher); - r.hash_stable(hcx, hasher); - } - ReEarlyBound(r) => { - r.hash_stable(hcx, hasher); - } - ReFree(r) => { - r.hash_stable(hcx, hasher); - } - RePlaceholder(r) => { - r.hash_stable(hcx, hasher); - } - ReVar(_) => { - panic!("region variables should not be hashed: {self:?}") - } +impl> DebugWithInfcx for InferTy { + fn fmt>( + 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!() + } + }, } } } diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 891a4dda22fef..9c7b8156b0437 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -40,11 +40,14 @@ //! - ty.super_visit_with(visitor) //! - u.visit_with(visitor) //! ``` -use crate::Interner; +use rustc_data_structures::sync::Lrc; +use rustc_index::{Idx, IndexVec}; use std::fmt; use std::ops::ControlFlow; +use crate::Interner; + /// This trait is implemented for every type that can be visited, /// providing the skeleton of the traversal. /// @@ -116,3 +119,80 @@ pub trait TypeVisitor: Sized { p.super_visit_with(self) } } + +/////////////////////////////////////////////////////////////////////////// +// Traversal implementations. + +impl, U: TypeVisitable> TypeVisitable for (T, U) { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + self.0.visit_with(visitor)?; + self.1.visit_with(visitor) + } +} + +impl, B: TypeVisitable, C: TypeVisitable> TypeVisitable + for (A, B, C) +{ + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + self.0.visit_with(visitor)?; + self.1.visit_with(visitor)?; + self.2.visit_with(visitor) + } +} + +impl> TypeVisitable for Option { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + match self { + Some(v) => v.visit_with(visitor), + None => ControlFlow::Continue(()), + } + } +} + +impl, E: TypeVisitable> TypeVisitable for Result { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + match self { + Ok(v) => v.visit_with(visitor), + Err(e) => e.visit_with(visitor), + } + } +} + +impl> TypeVisitable for Lrc { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + (**self).visit_with(visitor) + } +} + +impl> TypeVisitable for Box { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + (**self).visit_with(visitor) + } +} + +impl> TypeVisitable for Vec { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + self.iter().try_for_each(|t| t.visit_with(visitor)) + } +} + +// `TypeFoldable` isn't impl'd for `&[T]`. It doesn't make sense in the general +// case, because we can't return a new slice. But note that there are a couple +// of trivial impls of `TypeFoldable` for specific slice types elsewhere. +impl> TypeVisitable for &[T] { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + self.iter().try_for_each(|t| t.visit_with(visitor)) + } +} + +impl> TypeVisitable for Box<[T]> { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + self.iter().try_for_each(|t| t.visit_with(visitor)) + } +} + +impl, Ix: Idx> TypeVisitable for IndexVec { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + self.iter().try_for_each(|t| t.visit_with(visitor)) + } +}