Auto merge of #116951 - compiler-errors:ir-file-structure, r=jackh726

Restructure `rustc_type_ir` a bit

1. Split `sty` into new `ty_kind`/`region_kind`/`const_kind` modules, so that when we uplift more kinds (e.g. `PredicateKind`, `ClauseKind`, and `ExistentialPredicate`), they can live in their own simple-to-understand files.
2. Split up the `structural_impls` module, which is a kitchen sink of random impls -- move `TypeFoldable` and `TypeVisitable` impls into existing `fold` and `visit` modules, respectively.
3. Move the `DebugWithInfcx` trait and blanket impls into a new `debug` module, and `TypeFlags` definition into a new `flags` module.
5. Move `Interner` trait into a new `interner` module. I expect this file to get a lot larger as we make the interner more powerful for the trait solver refactor.

r? `@ghost` for now, will assign once #116946 lands
This commit is contained in:
bors 2023-10-20 19:11:08 +00:00
commit 96027d945b
22 changed files with 1621 additions and 1593 deletions

View File

@ -8,7 +8,7 @@ use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout}; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut}; use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut};
use rustc_target::abi::Integer; use rustc_target::abi::Integer;
use rustc_type_ir::sty::TyKind::*; use rustc_type_ir::TyKind::*;
use super::{ use super::{
util::ensure_monomorphic_enough, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, util::ensure_monomorphic_enough, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy,
@ -185,7 +185,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
src: &ImmTy<'tcx, M::Provenance>, src: &ImmTy<'tcx, M::Provenance>,
cast_to: TyAndLayout<'tcx>, cast_to: TyAndLayout<'tcx>,
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
use rustc_type_ir::sty::TyKind::*; use rustc_type_ir::TyKind::*;
let val = match src.layout.ty.kind() { let val = match src.layout.ty.kind() {
// Floating point // Floating point
@ -310,7 +310,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
where where
F: Float + Into<Scalar<M::Provenance>> + FloatConvert<Single> + FloatConvert<Double>, F: Float + Into<Scalar<M::Provenance>> + FloatConvert<Single> + FloatConvert<Double>,
{ {
use rustc_type_ir::sty::TyKind::*; use rustc_type_ir::TyKind::*;
fn adjust_nan< fn adjust_nan<
'mir, 'mir,

View File

@ -162,7 +162,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef
// trait, they *do* satisfy the repr(transparent) rules, and then we assume that everything else // trait, they *do* satisfy the repr(transparent) rules, and then we assume that everything else
// in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent) // in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent)
// even if they do not carry that attribute. // even if they do not carry that attribute.
use rustc_type_ir::sty::TyKind::*; use rustc_type_ir::TyKind::*;
match (source.kind(), target.kind()) { match (source.kind(), target.kind()) {
(&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
if infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, r_a, *r_b).is_ok() if infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, r_a, *r_b).is_ok()

View File

@ -20,7 +20,7 @@ use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCtxt}; use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCtxt};
use rustc_type_ir::sty::TyKind::*; use rustc_type_ir::TyKind::*;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> { impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Checks a `a <op>= b` /// Checks a `a <op>= b`

View File

@ -2475,7 +2475,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
ty: Ty<'tcx>, ty: Ty<'tcx>,
init: InitKind, init: InitKind,
) -> Option<InitError> { ) -> Option<InitError> {
use rustc_type_ir::sty::TyKind::*; use rustc_type_ir::TyKind::*;
match ty.kind() { match ty.kind() {
// Primitive types that don't like 0 as a value. // Primitive types that don't like 0 as a value.
Ref(..) => Some("references must be non-null".into()), Ref(..) => Some("references must be non-null".into()),

View File

@ -262,7 +262,7 @@ fn structurally_same_type_impl<'tcx>(
true true
} else { } else {
// Do a full, depth-first comparison between the two. // Do a full, depth-first comparison between the two.
use rustc_type_ir::sty::TyKind::*; use rustc_type_ir::TyKind::*;
let a_kind = a.kind(); let a_kind = a.kind();
let b_kind = b.kind(); let b_kind = b.kind();

View File

@ -65,7 +65,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx}; use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx};
use rustc_target::spec::abi; use rustc_target::spec::abi;
use rustc_type_ir::sty::TyKind::*; use rustc_type_ir::TyKind::*;
use rustc_type_ir::WithCachedTypeInfo; use rustc_type_ir::WithCachedTypeInfo;
use rustc_type_ir::{CollectAndApply, Interner, TypeFlags}; use rustc_type_ir::{CollectAndApply, Interner, TypeFlags};

View File

@ -17,7 +17,7 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::{PredicateOrigin, WherePredicate}; use rustc_hir::{PredicateOrigin, WherePredicate};
use rustc_span::{BytePos, Span}; use rustc_span::{BytePos, Span};
use rustc_type_ir::sty::TyKind::*; use rustc_type_ir::TyKind::*;
impl<'tcx> IntoDiagnosticArg for Ty<'tcx> { impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {

View File

@ -47,7 +47,7 @@ use crate::query::Providers;
use crate::ty::context::TyCtxt; use crate::ty::context::TyCtxt;
use crate::ty::{self, DefId, Ty, VariantDef, Visibility}; use crate::ty::{self, DefId, Ty, VariantDef, Visibility};
use rustc_type_ir::sty::TyKind::*; use rustc_type_ir::TyKind::*;
pub mod inhabited_predicate; pub mod inhabited_predicate;

View File

@ -33,13 +33,13 @@ use std::marker::PhantomData;
use std::ops::{ControlFlow, Deref, Range}; use std::ops::{ControlFlow, Deref, Range};
use ty::util::IntTypeExt; use ty::util::IntTypeExt;
use rustc_type_ir::sty::TyKind::*;
use rustc_type_ir::CollectAndApply; use rustc_type_ir::CollectAndApply;
use rustc_type_ir::ConstKind as IrConstKind; use rustc_type_ir::ConstKind as IrConstKind;
use rustc_type_ir::DebugWithInfcx; use rustc_type_ir::DebugWithInfcx;
use rustc_type_ir::DynKind; use rustc_type_ir::DynKind;
use rustc_type_ir::RegionKind as IrRegionKind; use rustc_type_ir::RegionKind as IrRegionKind;
use rustc_type_ir::TyKind as IrTyKind; use rustc_type_ir::TyKind as IrTyKind;
use rustc_type_ir::TyKind::*;
use super::GenericParamDefKind; use super::GenericParamDefKind;

View File

@ -14,7 +14,7 @@ fn sized_constraint_for_ty<'tcx>(
adtdef: ty::AdtDef<'tcx>, adtdef: ty::AdtDef<'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
) -> Vec<Ty<'tcx>> { ) -> Vec<Ty<'tcx>> {
use rustc_type_ir::sty::TyKind::*; use rustc_type_ir::TyKind::*;
let result = match ty.kind() { let result = match ty.kind() {
Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..) Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..)

View File

@ -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<I: Interner> {
/// 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<I: Interner>(value: &ConstKind<I>) -> usize {
match value {
Param(_) => 0,
Infer(_) => 1,
Bound(_, _) => 2,
Placeholder(_) => 3,
Unevaluated(_) => 4,
Value(_) => 5,
Error(_) => 6,
Expr(_) => 7,
}
}
impl<I: Interner> hash::Hash for ConstKind<I> {
fn hash<H: hash::Hasher>(&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<CTX: HashStableContext, I: Interner> HashStable<CTX> for ConstKind<I>
where
I::ParamConst: HashStable<CTX>,
I::InferConst: HashStable<CTX>,
I::BoundConst: HashStable<CTX>,
I::PlaceholderConst: HashStable<CTX>,
I::AliasConst: HashStable<CTX>,
I::ValueConst: HashStable<CTX>,
I::ErrorGuaranteed: HashStable<CTX>,
I::ExprConst: HashStable<CTX>,
{
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<I: Interner, D: TyDecoder<I = I>> Decodable<D> for ConstKind<I>
where
I::ParamConst: Decodable<D>,
I::InferConst: Decodable<D>,
I::BoundConst: Decodable<D>,
I::PlaceholderConst: Decodable<D>,
I::AliasConst: Decodable<D>,
I::ValueConst: Decodable<D>,
I::ErrorGuaranteed: Decodable<D>,
I::ExprConst: Decodable<D>,
{
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<I: Interner, E: TyEncoder<I = I>> Encodable<E> for ConstKind<I>
where
I::ParamConst: Encodable<E>,
I::InferConst: Encodable<E>,
I::BoundConst: Encodable<E>,
I::PlaceholderConst: Encodable<E>,
I::AliasConst: Encodable<E>,
I::ValueConst: Encodable<E>,
I::ErrorGuaranteed: Encodable<E>,
I::ExprConst: Encodable<E>,
{
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<I: Interner> PartialOrd for ConstKind<I> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<I: Interner> Ord for ConstKind<I> {
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<I: Interner> PartialEq for ConstKind<I> {
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<I: Interner> Eq for ConstKind<I> {}
impl<I: Interner> Clone for ConstKind<I> {
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<I: Interner> fmt::Debug for ConstKind<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
OptWithInfcx::new_no_ctx(self).fmt(f)
}
}
impl<I: Interner> DebugWithInfcx<I> for ConstKind<I> {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
use ConstKind::*;
match this.data {
Param(param) => write!(f, "{param:?}"),
Infer(var) => write!(f, "{:?}", &this.wrap(var)),
Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var.clone()),
Placeholder(placeholder) => write!(f, "{placeholder:?}"),
Unevaluated(uv) => {
write!(f, "{:?}", &this.wrap(uv))
}
Value(valtree) => write!(f, "{valtree:?}"),
Error(_) => write!(f, "{{const error}}"),
Expr(expr) => write!(f, "{:?}", &this.wrap(expr)),
}
}
}

View File

@ -0,0 +1,117 @@
use crate::{Interner, UniverseIndex};
use core::fmt;
use std::marker::PhantomData;
pub trait InferCtxtLike<I: Interner> {
fn universe_of_ty(&self, ty: I::InferTy) -> Option<UniverseIndex>;
fn universe_of_lt(&self, lt: I::InferRegion) -> Option<UniverseIndex>;
fn universe_of_ct(&self, ct: I::InferConst) -> Option<UniverseIndex>;
}
impl<I: Interner> InferCtxtLike<I> for core::convert::Infallible {
fn universe_of_ty(&self, _ty: <I as Interner>::InferTy) -> Option<UniverseIndex> {
match *self {}
}
fn universe_of_ct(&self, _ct: <I as Interner>::InferConst) -> Option<UniverseIndex> {
match *self {}
}
fn universe_of_lt(&self, _lt: <I as Interner>::InferRegion) -> Option<UniverseIndex> {
match *self {}
}
}
pub trait DebugWithInfcx<I: Interner>: fmt::Debug {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result;
}
impl<I: Interner, T: DebugWithInfcx<I> + ?Sized> DebugWithInfcx<I> for &'_ T {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
<T as DebugWithInfcx<I>>::fmt(this.map(|&data| data), f)
}
}
impl<I: Interner, T: DebugWithInfcx<I>> DebugWithInfcx<I> for [T] {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
match f.alternate() {
true => {
write!(f, "[\n")?;
for element in this.data.iter() {
write!(f, "{:?},\n", &this.wrap(element))?;
}
write!(f, "]")
}
false => {
write!(f, "[")?;
if this.data.len() > 0 {
for element in &this.data[..(this.data.len() - 1)] {
write!(f, "{:?}, ", &this.wrap(element))?;
}
if let Some(element) = this.data.last() {
write!(f, "{:?}", &this.wrap(element))?;
}
}
write!(f, "]")
}
}
}
}
pub struct OptWithInfcx<'a, I: Interner, InfCtx: InferCtxtLike<I>, T> {
pub data: T,
pub infcx: Option<&'a InfCtx>,
_interner: PhantomData<I>,
}
impl<I: Interner, InfCtx: InferCtxtLike<I>, T: Copy> Copy for OptWithInfcx<'_, I, InfCtx, T> {}
impl<I: Interner, InfCtx: InferCtxtLike<I>, T: Clone> Clone for OptWithInfcx<'_, I, InfCtx, T> {
fn clone(&self) -> Self {
Self { data: self.data.clone(), infcx: self.infcx, _interner: self._interner }
}
}
impl<'a, I: Interner, T> OptWithInfcx<'a, I, core::convert::Infallible, T> {
pub fn new_no_ctx(data: T) -> Self {
Self { data, infcx: None, _interner: PhantomData }
}
}
impl<'a, I: Interner, InfCtx: InferCtxtLike<I>, T> OptWithInfcx<'a, I, InfCtx, T> {
pub fn new(data: T, infcx: &'a InfCtx) -> Self {
Self { data, infcx: Some(infcx), _interner: PhantomData }
}
pub fn wrap<U>(self, u: U) -> OptWithInfcx<'a, I, InfCtx, U> {
OptWithInfcx { data: u, infcx: self.infcx, _interner: PhantomData }
}
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> OptWithInfcx<'a, I, InfCtx, U> {
OptWithInfcx { data: f(self.data), infcx: self.infcx, _interner: PhantomData }
}
pub fn as_ref(&self) -> OptWithInfcx<'a, I, InfCtx, &T> {
OptWithInfcx { data: &self.data, infcx: self.infcx, _interner: PhantomData }
}
}
impl<I: Interner, InfCtx: InferCtxtLike<I>, T: DebugWithInfcx<I>> fmt::Debug
for OptWithInfcx<'_, I, InfCtx, T>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
DebugWithInfcx::fmt(self.as_ref(), f)
}
}

View File

@ -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;
}
}

View File

@ -44,6 +44,11 @@
//! - ty.super_fold_with(folder) //! - ty.super_fold_with(folder)
//! - u.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}; use crate::{visit::TypeVisitable, Interner};
/// This trait is implemented for every type that can be folded, /// This trait is implemented for every type that can be folded,
@ -242,3 +247,101 @@ where
Ok(self.fold_predicate(p)) Ok(self.fold_predicate(p))
} }
} }
///////////////////////////////////////////////////////////////////////////
// Traversal implementations.
impl<I: Interner, T: TypeFoldable<I>, U: TypeFoldable<I>> TypeFoldable<I> for (T, U) {
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<(T, U), F::Error> {
Ok((self.0.try_fold_with(folder)?, self.1.try_fold_with(folder)?))
}
}
impl<I: Interner, A: TypeFoldable<I>, B: TypeFoldable<I>, C: TypeFoldable<I>> TypeFoldable<I>
for (A, B, C)
{
fn try_fold_with<F: FallibleTypeFolder<I>>(
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<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Option<T> {
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
Ok(match self {
Some(v) => Some(v.try_fold_with(folder)?),
None => None,
})
}
}
impl<I: Interner, T: TypeFoldable<I>, E: TypeFoldable<I>> TypeFoldable<I> for Result<T, E> {
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
Ok(match self {
Ok(v) => Ok(v.try_fold_with(folder)?),
Err(e) => Err(e.try_fold_with(folder)?),
})
}
}
impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Lrc<T> {
fn try_fold_with<F: FallibleTypeFolder<I>>(mut self, folder: &mut F) -> Result<Self, F::Error> {
// 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<ManuallyDrop<T>>` so that
// panicking during `make_mut` does not leak the `T`.
Lrc::make_mut(&mut self);
// Casting to `Lrc<ManuallyDrop<T>>` is safe because `ManuallyDrop`
// is `repr(transparent)`.
let ptr = Lrc::into_raw(self).cast::<mem::ManuallyDrop<T>>();
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<T>`.
Ok(Lrc::from_raw(Lrc::into_raw(unique).cast()))
}
}
}
impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Box<T> {
fn try_fold_with<F: FallibleTypeFolder<I>>(mut self, folder: &mut F) -> Result<Self, F::Error> {
*self = (*self).try_fold_with(folder)?;
Ok(self)
}
}
impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Vec<T> {
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
self.into_iter().map(|t| t.try_fold_with(folder)).collect()
}
}
impl<I: Interner, T: TypeFoldable<I>, Ix: Idx> TypeFoldable<I> for IndexVec<Ix, T> {
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
self.raw.try_fold_with(folder).map(IndexVec::from_raw)
}
}

View File

@ -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<Self>
+ Hash
+ Ord
+ IntoIterator<Item = Self::GenericArg>;
type GenericArg: Clone + DebugWithInfcx<Self> + Hash + Ord;
type Binder<T>;
// Predicates
type Predicate;
type PredicateKind: Clone + Debug + Hash + PartialEq + Eq;
type TypeAndMut: Clone + Debug + Hash + Ord;
// Kinds of tys
type Ty: Clone + DebugWithInfcx<Self> + Hash + Ord;
type Tys: Clone + Debug + Hash + Ord + IntoIterator<Item = Self::Ty>;
type AliasTy: Clone + DebugWithInfcx<Self> + Hash + Ord;
type ParamTy: Clone + Debug + Hash + Ord;
type BoundTy: Clone + Debug + Hash + Ord;
type PlaceholderTy: Clone + Debug + Hash + Ord;
type InferTy: Clone + DebugWithInfcx<Self> + Hash + Ord;
// Things stored inside of tys
type ErrorGuaranteed: Clone + Debug + Hash + Ord;
type BoundExistentialPredicates: Clone + DebugWithInfcx<Self> + Hash + Ord;
type PolyFnSig: Clone + DebugWithInfcx<Self> + Hash + Ord;
type AllocId: Clone + Debug + Hash + Ord;
// Kinds of consts
type Const: Clone + DebugWithInfcx<Self> + Hash + Ord;
type InferConst: Clone + DebugWithInfcx<Self> + Hash + Ord;
type AliasConst: Clone + DebugWithInfcx<Self> + Hash + Ord;
type PlaceholderConst: Clone + Debug + Hash + Ord;
type ParamConst: Clone + Debug + Hash + Ord;
type BoundConst: Clone + Debug + Hash + Ord;
type ValueConst: Clone + Debug + Hash + Ord;
type ExprConst: Clone + DebugWithInfcx<Self> + Hash + Ord;
// Kinds of regions
type Region: Clone + DebugWithInfcx<Self> + Hash + Ord;
type EarlyBoundRegion: Clone + Debug + Hash + Ord;
type BoundRegion: Clone + Debug + Hash + Ord;
type FreeRegion: Clone + Debug + Hash + Ord;
type InferRegion: Clone + DebugWithInfcx<Self> + 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::<Vec<_>>())`, 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<T, E>` items.
pub trait CollectAndApply<T, R>: 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<I, F>(iter: I, f: F) -> Self::Output
where
I: Iterator<Item = Self>,
F: FnOnce(&[T]) -> R;
}
/// The blanket impl that always collects all elements and applies `f`.
impl<T, R> CollectAndApply<T, R> for T {
type Output = R;
/// Equivalent to `f(&iter.collect::<Vec<_>>())`.
fn collect_and_apply<I, F>(mut iter: I, f: F) -> R
where
I: Iterator<Item = T>,
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::<SmallVec<[_; 8]>>()),
}
}
}
/// A fallible impl that will fail, without calling `f`, if there are any
/// errors during collection.
impl<T, R, E> CollectAndApply<T, R> for Result<T, E> {
type Output = Result<R, E>;
/// Equivalent to `Ok(f(&iter.collect::<Result<Vec<_>>>()?))`.
fn collect_and_apply<I, F>(mut iter: I, f: F) -> Result<R, E>
where
I: Iterator<Item = Result<T, E>>,
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::<Result<SmallVec<[_; 8]>, _>>()?),
})
}
}

View File

@ -15,302 +15,35 @@ extern crate bitflags;
#[macro_use] #[macro_use]
extern crate rustc_macros; 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;
use std::fmt::Debug;
use std::hash::Hash; use std::hash::Hash;
use std::mem::discriminant;
pub mod codec; pub mod codec;
pub mod fold; pub mod fold;
pub mod sty;
pub mod ty_info; pub mod ty_info;
pub mod ty_kind;
pub mod visit; pub mod visit;
#[macro_use] #[macro_use]
mod macros; mod macros;
mod structural_impls; mod const_kind;
mod debug;
mod flags;
mod interner;
mod region_kind;
pub use codec::*; pub use codec::*;
pub use structural_impls::{DebugWithInfcx, InferCtxtLike, OptWithInfcx}; pub use const_kind::*;
pub use sty::*; pub use debug::{DebugWithInfcx, InferCtxtLike, OptWithInfcx};
pub use flags::*;
pub use interner::*;
pub use region_kind::*;
pub use ty_info::*; pub use ty_info::*;
pub use ty_kind::*;
/// Needed so we can use #[derive(HashStable_Generic)] /// Needed so we can use #[derive(HashStable_Generic)]
pub trait HashStableContext {} pub trait HashStableContext {}
pub trait Interner: Sized {
type DefId: Clone + Debug + Hash + Ord;
type AdtDef: Clone + Debug + Hash + Ord;
type GenericArgs: Clone
+ DebugWithInfcx<Self>
+ Hash
+ Ord
+ IntoIterator<Item = Self::GenericArg>;
type GenericArg: Clone + DebugWithInfcx<Self> + Hash + Ord;
type Binder<T>;
// Predicates
type Predicate;
type PredicateKind: Clone + Debug + Hash + PartialEq + Eq;
type TypeAndMut: Clone + Debug + Hash + Ord;
// Kinds of tys
type Ty: Clone + DebugWithInfcx<Self> + Hash + Ord;
type Tys: Clone + Debug + Hash + Ord + IntoIterator<Item = Self::Ty>;
type AliasTy: Clone + DebugWithInfcx<Self> + Hash + Ord;
type ParamTy: Clone + Debug + Hash + Ord;
type BoundTy: Clone + Debug + Hash + Ord;
type PlaceholderTy: Clone + Debug + Hash + Ord;
type InferTy: Clone + DebugWithInfcx<Self> + Hash + Ord;
// Things stored inside of tys
type ErrorGuaranteed: Clone + Debug + Hash + Ord;
type BoundExistentialPredicates: Clone + DebugWithInfcx<Self> + Hash + Ord;
type PolyFnSig: Clone + DebugWithInfcx<Self> + Hash + Ord;
type AllocId: Clone + Debug + Hash + Ord;
// Kinds of consts
type Const: Clone + DebugWithInfcx<Self> + Hash + Ord;
type InferConst: Clone + DebugWithInfcx<Self> + Hash + Ord;
type AliasConst: Clone + DebugWithInfcx<Self> + Hash + Ord;
type PlaceholderConst: Clone + Debug + Hash + Ord;
type ParamConst: Clone + Debug + Hash + Ord;
type BoundConst: Clone + Debug + Hash + Ord;
type ValueConst: Clone + Debug + Hash + Ord;
type ExprConst: Clone + DebugWithInfcx<Self> + Hash + Ord;
// Kinds of regions
type Region: Clone + DebugWithInfcx<Self> + Hash + Ord;
type EarlyBoundRegion: Clone + Debug + Hash + Ord;
type BoundRegion: Clone + Debug + Hash + Ord;
type FreeRegion: Clone + Debug + Hash + Ord;
type InferRegion: Clone + DebugWithInfcx<Self> + 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::<Vec<_>>())`, 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<T, E>` items.
pub trait CollectAndApply<T, R>: 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<I, F>(iter: I, f: F) -> Self::Output
where
I: Iterator<Item = Self>,
F: FnOnce(&[T]) -> R;
}
/// The blanket impl that always collects all elements and applies `f`.
impl<T, R> CollectAndApply<T, R> for T {
type Output = R;
/// Equivalent to `f(&iter.collect::<Vec<_>>())`.
fn collect_and_apply<I, F>(mut iter: I, f: F) -> R
where
I: Iterator<Item = T>,
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::<SmallVec<[_; 8]>>()),
}
}
}
/// A fallible impl that will fail, without calling `f`, if there are any
/// errors during collection.
impl<T, R, E> CollectAndApply<T, R> for Result<T, E> {
type Output = Result<R, E>;
/// Equivalent to `Ok(f(&iter.collect::<Result<Vec<_>>>()?))`.
fn collect_and_apply<I, F>(mut iter: I, f: F) -> Result<R, E>
where
I: Iterator<Item = Result<T, E>>,
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::<Result<SmallVec<[_; 8]>, _>>()?),
})
}
}
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! { rustc_index::newtype_index! {
/// A [De Bruijn index][dbi] is a standard means of representing /// A [De Bruijn index][dbi] is a standard means of representing
/// regions (and perhaps later types) in a higher-ranked setting. In /// regions (and perhaps later types) in a higher-ranked setting. In
@ -434,259 +167,6 @@ pub fn debug_bound_var<T: std::fmt::Write>(
} }
} }
#[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<u64> {
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<u64> {
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<IntVarValue>;
#[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<FloatVarValue>;
#[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)] #[derive(Copy, Clone, PartialEq, Eq, Decodable, Encodable, Hash, HashStable_Generic)]
#[rustc_pass_by_value] #[rustc_pass_by_value]
pub enum Variance { pub enum Variance {
@ -756,34 +236,6 @@ impl Variance {
} }
} }
impl<CTX> HashStable<CTX> 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 { impl fmt::Debug for Variance {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self { 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! { rustc_index::newtype_index! {
/// "Universes" are used during type- and trait-checking in the /// "Universes" are used during type- and trait-checking in the
/// presence of `for<..>` binders to control what sets of names are /// presence of `for<..>` binders to control what sets of names are

View File

@ -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,
}

View File

@ -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<I: Interner> {
/// 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<I: Interner>(value: &RegionKind<I>) -> 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<I: Interner> Copy for RegionKind<I>
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<I: Interner> Clone for RegionKind<I> {
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<I: Interner> PartialEq for RegionKind<I> {
#[inline]
fn eq(&self, other: &RegionKind<I>) -> 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<I: Interner> Eq for RegionKind<I> {}
// This is manually implemented because a derive would require `I: PartialOrd`
impl<I: Interner> PartialOrd for RegionKind<I> {
#[inline]
fn partial_cmp(&self, other: &RegionKind<I>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
// This is manually implemented because a derive would require `I: Ord`
impl<I: Interner> Ord for RegionKind<I> {
#[inline]
fn cmp(&self, other: &RegionKind<I>) -> Ordering {
regionkind_discriminant(self).cmp(&regionkind_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<I: Interner> hash::Hash for RegionKind<I> {
fn hash<H: hash::Hasher>(&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<I: Interner> DebugWithInfcx<I> for RegionKind<I> {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
match this.data {
ReEarlyBound(data) => write!(f, "ReEarlyBound({data:?})"),
ReLateBound(binder_id, bound_region) => {
write!(f, "ReLateBound({binder_id:?}, {bound_region:?})")
}
ReFree(fr) => 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<I: Interner> fmt::Debug for RegionKind<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
OptWithInfcx::new_no_ctx(self).fmt(f)
}
}
// This is manually implemented because a derive would require `I: Encodable`
impl<I: Interner, E: TyEncoder> Encodable<E> for RegionKind<I>
where
I::EarlyBoundRegion: Encodable<E>,
I::BoundRegion: Encodable<E>,
I::FreeRegion: Encodable<E>,
I::InferRegion: Encodable<E>,
I::PlaceholderRegion: Encodable<E>,
{
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<I: Interner, D: TyDecoder<I = I>> Decodable<D> for RegionKind<I>
where
I::EarlyBoundRegion: Decodable<D>,
I::BoundRegion: Decodable<D>,
I::FreeRegion: Decodable<D>,
I::InferRegion: Decodable<D>,
I::PlaceholderRegion: Decodable<D>,
I::ErrorGuaranteed: Decodable<D>,
{
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<CTX: HashStableContext, I: Interner> HashStable<CTX> for RegionKind<I>
where
I::EarlyBoundRegion: HashStable<CTX>,
I::BoundRegion: HashStable<CTX>,
I::FreeRegion: HashStable<CTX>,
I::InferRegion: HashStable<CTX>,
I::PlaceholderRegion: HashStable<CTX>,
{
#[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:?}")
}
}
}
}

View File

@ -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<I: Interner, T: TypeFoldable<I>, U: TypeFoldable<I>> TypeFoldable<I> for (T, U) {
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<(T, U), F::Error> {
Ok((self.0.try_fold_with(folder)?, self.1.try_fold_with(folder)?))
}
}
impl<I: Interner, T: TypeVisitable<I>, U: TypeVisitable<I>> TypeVisitable<I> for (T, U) {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.0.visit_with(visitor)?;
self.1.visit_with(visitor)
}
}
impl<I: Interner, A: TypeFoldable<I>, B: TypeFoldable<I>, C: TypeFoldable<I>> TypeFoldable<I>
for (A, B, C)
{
fn try_fold_with<F: FallibleTypeFolder<I>>(
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<I: Interner, A: TypeVisitable<I>, B: TypeVisitable<I>, C: TypeVisitable<I>> TypeVisitable<I>
for (A, B, C)
{
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.0.visit_with(visitor)?;
self.1.visit_with(visitor)?;
self.2.visit_with(visitor)
}
}
impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Option<T> {
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
Ok(match self {
Some(v) => Some(v.try_fold_with(folder)?),
None => None,
})
}
}
impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Option<T> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
match self {
Some(v) => v.visit_with(visitor),
None => ControlFlow::Continue(()),
}
}
}
impl<I: Interner, T: TypeFoldable<I>, E: TypeFoldable<I>> TypeFoldable<I> for Result<T, E> {
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
Ok(match self {
Ok(v) => Ok(v.try_fold_with(folder)?),
Err(e) => Err(e.try_fold_with(folder)?),
})
}
}
impl<I: Interner, T: TypeVisitable<I>, E: TypeVisitable<I>> TypeVisitable<I> for Result<T, E> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
match self {
Ok(v) => v.visit_with(visitor),
Err(e) => e.visit_with(visitor),
}
}
}
impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Lrc<T> {
fn try_fold_with<F: FallibleTypeFolder<I>>(mut self, folder: &mut F) -> Result<Self, F::Error> {
// 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<ManuallyDrop<T>>` so that
// panicking during `make_mut` does not leak the `T`.
Lrc::make_mut(&mut self);
// Casting to `Lrc<ManuallyDrop<T>>` is safe because `ManuallyDrop`
// is `repr(transparent)`.
let ptr = Lrc::into_raw(self).cast::<mem::ManuallyDrop<T>>();
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<T>`.
Ok(Lrc::from_raw(Lrc::into_raw(unique).cast()))
}
}
}
impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Lrc<T> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
(**self).visit_with(visitor)
}
}
impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Box<T> {
fn try_fold_with<F: FallibleTypeFolder<I>>(mut self, folder: &mut F) -> Result<Self, F::Error> {
*self = (*self).try_fold_with(folder)?;
Ok(self)
}
}
impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<T> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
(**self).visit_with(visitor)
}
}
impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Vec<T> {
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
self.into_iter().map(|t| t.try_fold_with(folder)).collect()
}
}
impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Vec<T> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
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<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for &[T] {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.iter().try_for_each(|t| t.visit_with(visitor))
}
}
impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<[T]> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.iter().try_for_each(|t| t.visit_with(visitor))
}
}
impl<I: Interner, T: TypeFoldable<I>, Ix: Idx> TypeFoldable<I> for IndexVec<Ix, T> {
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
self.raw.try_fold_with(folder).map(IndexVec::from_raw)
}
}
impl<I: Interner, T: TypeVisitable<I>, Ix: Idx> TypeVisitable<I> for IndexVec<Ix, T> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.iter().try_for_each(|t| t.visit_with(visitor))
}
}
///////////////////////////////////////////////////
// Debug impls
pub trait InferCtxtLike<I: Interner> {
fn universe_of_ty(&self, ty: I::InferTy) -> Option<UniverseIndex>;
fn universe_of_lt(&self, lt: I::InferRegion) -> Option<UniverseIndex>;
fn universe_of_ct(&self, ct: I::InferConst) -> Option<UniverseIndex>;
}
impl<I: Interner> InferCtxtLike<I> for core::convert::Infallible {
fn universe_of_ty(&self, _ty: <I as Interner>::InferTy) -> Option<UniverseIndex> {
match *self {}
}
fn universe_of_ct(&self, _ct: <I as Interner>::InferConst) -> Option<UniverseIndex> {
match *self {}
}
fn universe_of_lt(&self, _lt: <I as Interner>::InferRegion) -> Option<UniverseIndex> {
match *self {}
}
}
pub trait DebugWithInfcx<I: Interner>: fmt::Debug {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result;
}
impl<I: Interner, T: DebugWithInfcx<I> + ?Sized> DebugWithInfcx<I> for &'_ T {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
<T as DebugWithInfcx<I>>::fmt(this.map(|&data| data), f)
}
}
impl<I: Interner, T: DebugWithInfcx<I>> DebugWithInfcx<I> for [T] {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
match f.alternate() {
true => {
write!(f, "[\n")?;
for element in this.data.iter() {
write!(f, "{:?},\n", &this.wrap(element))?;
}
write!(f, "]")
}
false => {
write!(f, "[")?;
if this.data.len() > 0 {
for element in &this.data[..(this.data.len() - 1)] {
write!(f, "{:?}, ", &this.wrap(element))?;
}
if let Some(element) = this.data.last() {
write!(f, "{:?}", &this.wrap(element))?;
}
}
write!(f, "]")
}
}
}
}
pub struct OptWithInfcx<'a, I: Interner, InfCtx: InferCtxtLike<I>, T> {
pub data: T,
pub infcx: Option<&'a InfCtx>,
_interner: PhantomData<I>,
}
impl<I: Interner, InfCtx: InferCtxtLike<I>, T: Copy> Copy for OptWithInfcx<'_, I, InfCtx, T> {}
impl<I: Interner, InfCtx: InferCtxtLike<I>, T: Clone> Clone for OptWithInfcx<'_, I, InfCtx, T> {
fn clone(&self) -> Self {
Self { data: self.data.clone(), infcx: self.infcx, _interner: self._interner }
}
}
impl<'a, I: Interner, T> OptWithInfcx<'a, I, core::convert::Infallible, T> {
pub fn new_no_ctx(data: T) -> Self {
Self { data, infcx: None, _interner: PhantomData }
}
}
impl<'a, I: Interner, InfCtx: InferCtxtLike<I>, T> OptWithInfcx<'a, I, InfCtx, T> {
pub fn new(data: T, infcx: &'a InfCtx) -> Self {
Self { data, infcx: Some(infcx), _interner: PhantomData }
}
pub fn wrap<U>(self, u: U) -> OptWithInfcx<'a, I, InfCtx, U> {
OptWithInfcx { data: u, infcx: self.infcx, _interner: PhantomData }
}
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> OptWithInfcx<'a, I, InfCtx, U> {
OptWithInfcx { data: f(self.data), infcx: self.infcx, _interner: PhantomData }
}
pub fn as_ref(&self) -> OptWithInfcx<'a, I, InfCtx, &T> {
OptWithInfcx { data: &self.data, infcx: self.infcx, _interner: PhantomData }
}
}
impl<I: Interner, InfCtx: InferCtxtLike<I>, T: DebugWithInfcx<I>> fmt::Debug
for OptWithInfcx<'_, I, InfCtx, T>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
DebugWithInfcx::fmt(self.as_ref(), f)
}
}
impl fmt::Debug for IntTy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name_str())
}
}
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<I: Interner<InferTy = InferTy>> DebugWithInfcx<I> for InferTy {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
use InferTy::*;
match this.infcx.and_then(|infcx| infcx.universe_of_ty(*this.data)) {
None => write!(f, "{:?}", this.data),
Some(universe) => match *this.data {
TyVar(ty_vid) => write!(f, "?{}_{}t", ty_vid.index(), universe.index()),
IntVar(_) | FloatVar(_) | FreshTy(_) | FreshIntTy(_) | FreshFloatTy(_) => {
unreachable!()
}
},
}
}
}
impl<I: Interner> fmt::Debug for ConstKind<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
OptWithInfcx::new_no_ctx(self).fmt(f)
}
}
impl<I: Interner> DebugWithInfcx<I> for ConstKind<I> {
fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
use ConstKind::*;
match this.data {
Param(param) => write!(f, "{param:?}"),
Infer(var) => write!(f, "{:?}", &this.wrap(var)),
Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var.clone()),
Placeholder(placeholder) => write!(f, "{placeholder:?}"),
Unevaluated(uv) => {
write!(f, "{:?}", &this.wrap(uv))
}
Value(valtree) => write!(f, "{valtree:?}"),
Error(_) => write!(f, "{{const error}}"),
Expr(expr) => write!(f, "{:?}", &this.wrap(expr)),
}
}
}

View File

@ -1,13 +1,8 @@
use std::{ use rustc_data_structures::fingerprint::Fingerprint;
cmp::Ordering, use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
hash::{Hash, Hasher}, use std::cmp::Ordering;
ops::Deref, use std::hash::{Hash, Hasher};
}; use std::ops::Deref;
use rustc_data_structures::{
fingerprint::Fingerprint,
stable_hasher::{HashStable, StableHasher},
};
use crate::{DebruijnIndex, TypeFlags}; use crate::{DebruijnIndex, TypeFlags};

View File

@ -1,23 +1,20 @@
#![allow(rustc::usage_of_ty_tykind)] #![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::cmp::Ordering;
use std::mem::discriminant;
use std::{fmt, hash}; use std::{fmt, hash};
use crate::FloatTy;
use crate::HashStableContext; use crate::HashStableContext;
use crate::IntTy;
use crate::Interner; use crate::Interner;
use crate::TyDecoder; use crate::TyDecoder;
use crate::TyEncoder; use crate::TyEncoder;
use crate::UintTy;
use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, OptWithInfcx}; use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, OptWithInfcx};
use self::RegionKind::*;
use self::TyKind::*; use self::TyKind::*;
use rustc_data_structures::stable_hasher::HashStable;
use rustc_serialize::{Decodable, Decoder, Encodable};
/// The movability of a generator / closure literal: /// The movability of a generator / closure literal:
/// whether a generator contains self-references, causing it to be `!Unpin`. /// whether a generator contains self-references, causing it to be `!Unpin`.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, Debug, Copy)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, Debug, Copy)]
@ -914,620 +911,347 @@ where
} }
} }
/// Represents a constant in Rust. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
// #[derive(derive_more::From)] #[derive(Encodable, Decodable, HashStable_Generic)]
pub enum ConstKind<I: Interner> { pub enum IntTy {
/// A const generic parameter. Isize,
Param(I::ParamConst), I8,
I16,
/// Infer the value of the const. I32,
Infer(I::InferConst), I64,
I128,
/// 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<I: Interner>(value: &ConstKind<I>) -> usize { impl IntTy {
match value { pub fn name_str(&self) -> &'static str {
ConstKind::Param(_) => 0, match *self {
ConstKind::Infer(_) => 1, IntTy::Isize => "isize",
ConstKind::Bound(_, _) => 2, IntTy::I8 => "i8",
ConstKind::Placeholder(_) => 3, IntTy::I16 => "i16",
ConstKind::Unevaluated(_) => 4, IntTy::I32 => "i32",
ConstKind::Value(_) => 5, IntTy::I64 => "i64",
ConstKind::Error(_) => 6, IntTy::I128 => "i128",
ConstKind::Expr(_) => 7,
}
}
impl<I: Interner> hash::Hash for ConstKind<I> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
const_kind_discriminant(self).hash(state);
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),
} }
} }
}
impl<CTX: HashStableContext, I: Interner> HashStable<CTX> for ConstKind<I> pub fn bit_width(&self) -> Option<u64> {
where Some(match *self {
I::ParamConst: HashStable<CTX>, IntTy::Isize => return None,
I::InferConst: HashStable<CTX>, IntTy::I8 => 8,
I::BoundConst: HashStable<CTX>, IntTy::I16 => 16,
I::PlaceholderConst: HashStable<CTX>, IntTy::I32 => 32,
I::AliasConst: HashStable<CTX>, IntTy::I64 => 64,
I::ValueConst: HashStable<CTX>, IntTy::I128 => 128,
I::ErrorGuaranteed: HashStable<CTX>,
I::ExprConst: HashStable<CTX>,
{
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 {
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),
}
}
}
impl<I: Interner, D: TyDecoder<I = I>> Decodable<D> for ConstKind<I>
where
I::ParamConst: Decodable<D>,
I::InferConst: Decodable<D>,
I::BoundConst: Decodable<D>,
I::PlaceholderConst: Decodable<D>,
I::AliasConst: Decodable<D>,
I::ValueConst: Decodable<D>,
I::ErrorGuaranteed: Decodable<D>,
I::ExprConst: Decodable<D>,
{
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,
)
),
}
}
}
impl<I: Interner, E: TyEncoder<I = I>> Encodable<E> for ConstKind<I>
where
I::ParamConst: Encodable<E>,
I::InferConst: Encodable<E>,
I::BoundConst: Encodable<E>,
I::PlaceholderConst: Encodable<E>,
I::AliasConst: Encodable<E>,
I::ValueConst: Encodable<E>,
I::ErrorGuaranteed: Encodable<E>,
I::ExprConst: Encodable<E>,
{
fn encode(&self, e: &mut E) {
let disc = const_kind_discriminant(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)),
}
}
}
impl<I: Interner> PartialOrd for ConstKind<I> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<I: Interner> Ord for ConstKind<I> {
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
}
})
}
}
impl<I: Interner> PartialEq for ConstKind<I> {
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<I: Interner> Eq for ConstKind<I> {}
impl<I: Interner> Clone for ConstKind<I> {
fn clone(&self) -> Self {
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()),
}
}
}
/// 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<I: Interner> {
/// 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<I: Interner>(value: &RegionKind<I>) -> 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<I: Interner> Copy for RegionKind<I>
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<I: Interner> Clone for RegionKind<I> {
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<I: Interner> PartialEq for RegionKind<I> {
#[inline]
fn eq(&self, other: &RegionKind<I>) -> 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<I: Interner> Eq for RegionKind<I> {}
// This is manually implemented because a derive would require `I: PartialOrd`
impl<I: Interner> PartialOrd for RegionKind<I> {
#[inline]
fn partial_cmp(&self, other: &RegionKind<I>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
// This is manually implemented because a derive would require `I: Ord`
impl<I: Interner> Ord for RegionKind<I> {
#[inline]
fn cmp(&self, other: &RegionKind<I>) -> Ordering {
regionkind_discriminant(self).cmp(&regionkind_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` pub fn normalize(&self, target_width: u32) -> Self {
impl<I: Interner> hash::Hash for RegionKind<I> {
fn hash<H: hash::Hasher>(&self, state: &mut H) -> () {
regionkind_discriminant(self).hash(state);
match self { match self {
ReEarlyBound(r) => r.hash(state), IntTy::Isize => match target_width {
ReLateBound(d, r) => { 16 => IntTy::I16,
d.hash(state); 32 => IntTy::I32,
r.hash(state) 64 => IntTy::I64,
} _ => unreachable!(),
ReFree(r) => r.hash(state), },
ReStatic => (), _ => *self,
ReVar(r) => r.hash(state), }
RePlaceholder(r) => r.hash(state), }
ReErased => (),
ReError(_) => (), 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,
} }
} }
} }
impl<I: Interner> DebugWithInfcx<I> for RegionKind<I> { #[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<u64> {
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<IntVarValue>;
#[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<FloatVarValue>;
#[inline]
fn index(&self) -> u32 {
self.as_u32()
}
#[inline]
fn from_index(i: u32) -> FloatVid {
FloatVid::from_u32(i)
}
fn tag() -> &'static str {
"FloatVid"
}
}
impl<CTX> HashStable<CTX> 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::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 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<I: Interner<InferTy = InferTy>> DebugWithInfcx<I> for InferTy {
fn fmt<InfCtx: InferCtxtLike<I>>( fn fmt<InfCtx: InferCtxtLike<I>>(
this: OptWithInfcx<'_, I, InfCtx, &Self>, this: OptWithInfcx<'_, I, InfCtx, &Self>,
f: &mut core::fmt::Formatter<'_>, f: &mut fmt::Formatter<'_>,
) -> core::fmt::Result { ) -> fmt::Result {
match this.data { use InferTy::*;
ReEarlyBound(data) => write!(f, "ReEarlyBound({data:?})"), match this.infcx.and_then(|infcx| infcx.universe_of_ty(*this.data)) {
None => write!(f, "{:?}", this.data),
ReLateBound(binder_id, bound_region) => { Some(universe) => match *this.data {
write!(f, "ReLateBound({binder_id:?}, {bound_region:?})") TyVar(ty_vid) => write!(f, "?{}_{}t", ty_vid.index(), universe.index()),
} IntVar(_) | FloatVar(_) | FreshTy(_) | FreshIntTy(_) | FreshFloatTy(_) => {
unreachable!()
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<I: Interner> fmt::Debug for RegionKind<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
OptWithInfcx::new_no_ctx(self).fmt(f)
}
}
// This is manually implemented because a derive would require `I: Encodable`
impl<I: Interner, E: TyEncoder> Encodable<E> for RegionKind<I>
where
I::EarlyBoundRegion: Encodable<E>,
I::BoundRegion: Encodable<E>,
I::FreeRegion: Encodable<E>,
I::InferRegion: Encodable<E>,
I::PlaceholderRegion: Encodable<E>,
{
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<I: Interner, D: TyDecoder<I = I>> Decodable<D> for RegionKind<I>
where
I::EarlyBoundRegion: Decodable<D>,
I::BoundRegion: Decodable<D>,
I::FreeRegion: Decodable<D>,
I::InferRegion: Decodable<D>,
I::PlaceholderRegion: Decodable<D>,
I::ErrorGuaranteed: Decodable<D>,
{
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<CTX: HashStableContext, I: Interner> HashStable<CTX> for RegionKind<I>
where
I::EarlyBoundRegion: HashStable<CTX>,
I::BoundRegion: HashStable<CTX>,
I::FreeRegion: HashStable<CTX>,
I::InferRegion: HashStable<CTX>,
I::PlaceholderRegion: HashStable<CTX>,
{
#[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:?}")
}
} }
} }
} }

View File

@ -40,11 +40,14 @@
//! - ty.super_visit_with(visitor) //! - ty.super_visit_with(visitor)
//! - u.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::fmt;
use std::ops::ControlFlow; use std::ops::ControlFlow;
use crate::Interner;
/// This trait is implemented for every type that can be visited, /// This trait is implemented for every type that can be visited,
/// providing the skeleton of the traversal. /// providing the skeleton of the traversal.
/// ///
@ -116,3 +119,80 @@ pub trait TypeVisitor<I: Interner>: Sized {
p.super_visit_with(self) p.super_visit_with(self)
} }
} }
///////////////////////////////////////////////////////////////////////////
// Traversal implementations.
impl<I: Interner, T: TypeVisitable<I>, U: TypeVisitable<I>> TypeVisitable<I> for (T, U) {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.0.visit_with(visitor)?;
self.1.visit_with(visitor)
}
}
impl<I: Interner, A: TypeVisitable<I>, B: TypeVisitable<I>, C: TypeVisitable<I>> TypeVisitable<I>
for (A, B, C)
{
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.0.visit_with(visitor)?;
self.1.visit_with(visitor)?;
self.2.visit_with(visitor)
}
}
impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Option<T> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
match self {
Some(v) => v.visit_with(visitor),
None => ControlFlow::Continue(()),
}
}
}
impl<I: Interner, T: TypeVisitable<I>, E: TypeVisitable<I>> TypeVisitable<I> for Result<T, E> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
match self {
Ok(v) => v.visit_with(visitor),
Err(e) => e.visit_with(visitor),
}
}
}
impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Lrc<T> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
(**self).visit_with(visitor)
}
}
impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<T> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
(**self).visit_with(visitor)
}
}
impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Vec<T> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
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<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for &[T] {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.iter().try_for_each(|t| t.visit_with(visitor))
}
}
impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<[T]> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.iter().try_for_each(|t| t.visit_with(visitor))
}
}
impl<I: Interner, T: TypeVisitable<I>, Ix: Idx> TypeVisitable<I> for IndexVec<Ix, T> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.iter().try_for_each(|t| t.visit_with(visitor))
}
}