mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 07:14:28 +00:00
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:
commit
96027d945b
@ -8,7 +8,7 @@ use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut};
|
||||
use rustc_target::abi::Integer;
|
||||
use rustc_type_ir::sty::TyKind::*;
|
||||
use rustc_type_ir::TyKind::*;
|
||||
|
||||
use super::{
|
||||
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>,
|
||||
cast_to: TyAndLayout<'tcx>,
|
||||
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
||||
use rustc_type_ir::sty::TyKind::*;
|
||||
use rustc_type_ir::TyKind::*;
|
||||
|
||||
let val = match src.layout.ty.kind() {
|
||||
// Floating point
|
||||
@ -310,7 +310,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
where
|
||||
F: Float + Into<Scalar<M::Provenance>> + FloatConvert<Single> + FloatConvert<Double>,
|
||||
{
|
||||
use rustc_type_ir::sty::TyKind::*;
|
||||
use rustc_type_ir::TyKind::*;
|
||||
|
||||
fn adjust_nan<
|
||||
'mir,
|
||||
|
@ -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
|
||||
// in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent)
|
||||
// even if they do not carry that attribute.
|
||||
use rustc_type_ir::sty::TyKind::*;
|
||||
use rustc_type_ir::TyKind::*;
|
||||
match (source.kind(), target.kind()) {
|
||||
(&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()
|
||||
|
@ -20,7 +20,7 @@ use rustc_span::Span;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
|
||||
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> {
|
||||
/// Checks a `a <op>= b`
|
||||
|
@ -2475,7 +2475,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
||||
ty: Ty<'tcx>,
|
||||
init: InitKind,
|
||||
) -> Option<InitError> {
|
||||
use rustc_type_ir::sty::TyKind::*;
|
||||
use rustc_type_ir::TyKind::*;
|
||||
match ty.kind() {
|
||||
// Primitive types that don't like 0 as a value.
|
||||
Ref(..) => Some("references must be non-null".into()),
|
||||
|
@ -262,7 +262,7 @@ fn structurally_same_type_impl<'tcx>(
|
||||
true
|
||||
} else {
|
||||
// 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 b_kind = b.kind();
|
||||
|
||||
|
@ -65,7 +65,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx};
|
||||
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::{CollectAndApply, Interner, TypeFlags};
|
||||
|
||||
|
@ -17,7 +17,7 @@ use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{PredicateOrigin, WherePredicate};
|
||||
use rustc_span::{BytePos, Span};
|
||||
use rustc_type_ir::sty::TyKind::*;
|
||||
use rustc_type_ir::TyKind::*;
|
||||
|
||||
impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
|
@ -47,7 +47,7 @@ use crate::query::Providers;
|
||||
use crate::ty::context::TyCtxt;
|
||||
use crate::ty::{self, DefId, Ty, VariantDef, Visibility};
|
||||
|
||||
use rustc_type_ir::sty::TyKind::*;
|
||||
use rustc_type_ir::TyKind::*;
|
||||
|
||||
pub mod inhabited_predicate;
|
||||
|
||||
|
@ -33,13 +33,13 @@ use std::marker::PhantomData;
|
||||
use std::ops::{ControlFlow, Deref, Range};
|
||||
use ty::util::IntTypeExt;
|
||||
|
||||
use rustc_type_ir::sty::TyKind::*;
|
||||
use rustc_type_ir::CollectAndApply;
|
||||
use rustc_type_ir::ConstKind as IrConstKind;
|
||||
use rustc_type_ir::DebugWithInfcx;
|
||||
use rustc_type_ir::DynKind;
|
||||
use rustc_type_ir::RegionKind as IrRegionKind;
|
||||
use rustc_type_ir::TyKind as IrTyKind;
|
||||
use rustc_type_ir::TyKind::*;
|
||||
|
||||
use super::GenericParamDefKind;
|
||||
|
||||
|
@ -14,7 +14,7 @@ fn sized_constraint_for_ty<'tcx>(
|
||||
adtdef: ty::AdtDef<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Vec<Ty<'tcx>> {
|
||||
use rustc_type_ir::sty::TyKind::*;
|
||||
use rustc_type_ir::TyKind::*;
|
||||
|
||||
let result = match ty.kind() {
|
||||
Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..)
|
||||
|
258
compiler/rustc_type_ir/src/const_kind.rs
Normal file
258
compiler/rustc_type_ir/src/const_kind.rs
Normal 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)),
|
||||
}
|
||||
}
|
||||
}
|
117
compiler/rustc_type_ir/src/debug.rs
Normal file
117
compiler/rustc_type_ir/src/debug.rs
Normal 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)
|
||||
}
|
||||
}
|
119
compiler/rustc_type_ir/src/flags.rs
Normal file
119
compiler/rustc_type_ir/src/flags.rs
Normal 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;
|
||||
}
|
||||
}
|
@ -44,6 +44,11 @@
|
||||
//! - ty.super_fold_with(folder)
|
||||
//! - u.fold_with(folder)
|
||||
//! ```
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use std::mem;
|
||||
|
||||
use crate::{visit::TypeVisitable, Interner};
|
||||
|
||||
/// This trait is implemented for every type that can be folded,
|
||||
@ -242,3 +247,101 @@ where
|
||||
Ok(self.fold_predicate(p))
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Traversal implementations.
|
||||
|
||||
impl<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)
|
||||
}
|
||||
}
|
||||
|
155
compiler/rustc_type_ir/src/interner.rs
Normal file
155
compiler/rustc_type_ir/src/interner.rs
Normal 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]>, _>>()?),
|
||||
})
|
||||
}
|
||||
}
|
@ -15,302 +15,35 @@ extern crate bitflags;
|
||||
#[macro_use]
|
||||
extern crate rustc_macros;
|
||||
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::unify::{EqUnifyValue, UnifyKey};
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::mem::discriminant;
|
||||
|
||||
pub mod codec;
|
||||
pub mod fold;
|
||||
pub mod sty;
|
||||
pub mod ty_info;
|
||||
pub mod ty_kind;
|
||||
pub mod visit;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod structural_impls;
|
||||
mod const_kind;
|
||||
mod debug;
|
||||
mod flags;
|
||||
mod interner;
|
||||
mod region_kind;
|
||||
|
||||
pub use codec::*;
|
||||
pub use structural_impls::{DebugWithInfcx, InferCtxtLike, OptWithInfcx};
|
||||
pub use sty::*;
|
||||
pub use const_kind::*;
|
||||
pub use debug::{DebugWithInfcx, InferCtxtLike, OptWithInfcx};
|
||||
pub use flags::*;
|
||||
pub use interner::*;
|
||||
pub use region_kind::*;
|
||||
pub use ty_info::*;
|
||||
pub use ty_kind::*;
|
||||
|
||||
/// Needed so we can use #[derive(HashStable_Generic)]
|
||||
pub trait HashStableContext {}
|
||||
|
||||
pub trait Interner: Sized {
|
||||
type DefId: Clone + Debug + Hash + Ord;
|
||||
type AdtDef: Clone + Debug + Hash + Ord;
|
||||
|
||||
type GenericArgs: Clone
|
||||
+ DebugWithInfcx<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! {
|
||||
/// A [De Bruijn index][dbi] is a standard means of representing
|
||||
/// regions (and perhaps later types) in a higher-ranked setting. In
|
||||
@ -434,259 +167,6 @@ pub fn debug_bound_var<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)]
|
||||
#[rustc_pass_by_value]
|
||||
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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match *self {
|
||||
@ -795,20 +247,6 @@ impl fmt::Debug for Variance {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InferTy {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use InferTy::*;
|
||||
match *self {
|
||||
TyVar(_) => write!(f, "_"),
|
||||
IntVar(_) => write!(f, "{}", "{integer}"),
|
||||
FloatVar(_) => write!(f, "{}", "{float}"),
|
||||
FreshTy(v) => write!(f, "FreshTy({v})"),
|
||||
FreshIntTy(v) => write!(f, "FreshIntTy({v})"),
|
||||
FreshFloatTy(v) => write!(f, "FreshFloatTy({v})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// "Universes" are used during type- and trait-checking in the
|
||||
/// presence of `for<..>` binders to control what sets of names are
|
||||
|
@ -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,
|
||||
}
|
||||
|
412
compiler/rustc_type_ir/src/region_kind.rs
Normal file
412
compiler/rustc_type_ir/src/region_kind.rs
Normal 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(®ionkind_discriminant(other)).then_with(|| {
|
||||
match (self, other) {
|
||||
(ReEarlyBound(a_r), ReEarlyBound(b_r)) => a_r.cmp(b_r),
|
||||
(ReLateBound(a_d, a_r), ReLateBound(b_d, b_r)) => {
|
||||
a_d.cmp(b_d).then_with(|| a_r.cmp(b_r))
|
||||
}
|
||||
(ReFree(a_r), ReFree(b_r)) => a_r.cmp(b_r),
|
||||
(ReStatic, ReStatic) => Ordering::Equal,
|
||||
(ReVar(a_r), ReVar(b_r)) => a_r.cmp(b_r),
|
||||
(RePlaceholder(a_r), RePlaceholder(b_r)) => a_r.cmp(b_r),
|
||||
(ReErased, ReErased) => Ordering::Equal,
|
||||
_ => {
|
||||
debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}");
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// This is manually implemented because a derive would require `I: Hash`
|
||||
impl<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:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,8 @@
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
hash::{Hash, Hasher},
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
use rustc_data_structures::{
|
||||
fingerprint::Fingerprint,
|
||||
stable_hasher::{HashStable, StableHasher},
|
||||
};
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::{DebruijnIndex, TypeFlags};
|
||||
|
||||
|
@ -1,23 +1,20 @@
|
||||
#![allow(rustc::usage_of_ty_tykind)]
|
||||
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::unify::{EqUnifyValue, UnifyKey};
|
||||
use rustc_serialize::{Decodable, Decoder, Encodable};
|
||||
use std::cmp::Ordering;
|
||||
use std::mem::discriminant;
|
||||
use std::{fmt, hash};
|
||||
|
||||
use crate::FloatTy;
|
||||
use crate::HashStableContext;
|
||||
use crate::IntTy;
|
||||
use crate::Interner;
|
||||
use crate::TyDecoder;
|
||||
use crate::TyEncoder;
|
||||
use crate::UintTy;
|
||||
use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, OptWithInfcx};
|
||||
|
||||
use self::RegionKind::*;
|
||||
use self::TyKind::*;
|
||||
|
||||
use rustc_data_structures::stable_hasher::HashStable;
|
||||
use rustc_serialize::{Decodable, Decoder, Encodable};
|
||||
|
||||
/// The movability of a generator / closure literal:
|
||||
/// whether a generator contains self-references, causing it to be `!Unpin`.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, Debug, Copy)]
|
||||
@ -914,620 +911,347 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a constant in Rust.
|
||||
// #[derive(derive_more::From)]
|
||||
pub enum ConstKind<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),
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Encodable, Decodable, HashStable_Generic)]
|
||||
pub enum IntTy {
|
||||
Isize,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
}
|
||||
|
||||
const fn const_kind_discriminant<I: Interner>(value: &ConstKind<I>) -> usize {
|
||||
match value {
|
||||
ConstKind::Param(_) => 0,
|
||||
ConstKind::Infer(_) => 1,
|
||||
ConstKind::Bound(_, _) => 2,
|
||||
ConstKind::Placeholder(_) => 3,
|
||||
ConstKind::Unevaluated(_) => 4,
|
||||
ConstKind::Value(_) => 5,
|
||||
ConstKind::Error(_) => 6,
|
||||
ConstKind::Expr(_) => 7,
|
||||
}
|
||||
}
|
||||
|
||||
impl<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 IntTy {
|
||||
pub fn name_str(&self) -> &'static str {
|
||||
match *self {
|
||||
IntTy::Isize => "isize",
|
||||
IntTy::I8 => "i8",
|
||||
IntTy::I16 => "i16",
|
||||
IntTy::I32 => "i32",
|
||||
IntTy::I64 => "i64",
|
||||
IntTy::I128 => "i128",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<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 {
|
||||
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(®ionkind_discriminant(other)).then_with(|| {
|
||||
match (self, other) {
|
||||
(ReEarlyBound(a_r), ReEarlyBound(b_r)) => a_r.cmp(b_r),
|
||||
(ReLateBound(a_d, a_r), ReLateBound(b_d, b_r)) => {
|
||||
a_d.cmp(b_d).then_with(|| a_r.cmp(b_r))
|
||||
}
|
||||
(ReFree(a_r), ReFree(b_r)) => a_r.cmp(b_r),
|
||||
(ReStatic, ReStatic) => Ordering::Equal,
|
||||
(ReVar(a_r), ReVar(b_r)) => a_r.cmp(b_r),
|
||||
(RePlaceholder(a_r), RePlaceholder(b_r)) => a_r.cmp(b_r),
|
||||
(ReErased, ReErased) => Ordering::Equal,
|
||||
_ => {
|
||||
debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}");
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
pub fn normalize(&self, target_width: u32) -> Self {
|
||||
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(_) => (),
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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>>(
|
||||
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:?}")
|
||||
}
|
||||
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!()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
@ -40,11 +40,14 @@
|
||||
//! - ty.super_visit_with(visitor)
|
||||
//! - u.visit_with(visitor)
|
||||
//! ```
|
||||
use crate::Interner;
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use std::fmt;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::Interner;
|
||||
|
||||
/// This trait is implemented for every type that can be visited,
|
||||
/// providing the skeleton of the traversal.
|
||||
///
|
||||
@ -116,3 +119,80 @@ pub trait TypeVisitor<I: Interner>: Sized {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user