mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Uplift TypeVisitableExt into rustc_type_ir
This commit is contained in:
parent
bc1b9e0e9a
commit
f4e886323c
@ -35,6 +35,16 @@ impl<'tcx> IntoKind for Const<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_type_ir::visit::Flags for Const<'tcx> {
|
||||
fn flags(&self) -> TypeFlags {
|
||||
self.0.flags
|
||||
}
|
||||
|
||||
fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
|
||||
self.0.outer_exclusive_binder
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ConstTy<TyCtxt<'tcx>> for Const<'tcx> {
|
||||
fn ty(self) -> Ty<'tcx> {
|
||||
self.ty()
|
||||
@ -63,11 +73,13 @@ impl<'tcx> Const<'tcx> {
|
||||
self.0.kind
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Think about removing this.
|
||||
#[inline]
|
||||
pub fn flags(self) -> TypeFlags {
|
||||
self.0.flags
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Think about removing this.
|
||||
#[inline]
|
||||
pub fn outer_exclusive_binder(self) -> ty::DebruijnIndex {
|
||||
self.0.outer_exclusive_binder
|
||||
|
@ -88,6 +88,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
||||
type Term = ty::Term<'tcx>;
|
||||
|
||||
type Binder<T> = Binder<'tcx, T>;
|
||||
type BoundVars = &'tcx List<ty::BoundVariableKind>;
|
||||
type BoundVar = ty::BoundVariableKind;
|
||||
type CanonicalVars = CanonicalVarInfos<'tcx>;
|
||||
|
||||
type Ty = Ty<'tcx>;
|
||||
@ -151,6 +153,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
||||
) -> Self::Const {
|
||||
Const::new_bound(self, debruijn, var, ty)
|
||||
}
|
||||
|
||||
fn expect_error_or_delayed_bug() {
|
||||
let has_errors = ty::tls::with(|tcx| tcx.dcx().has_errors_or_lint_errors_or_delayed_bugs());
|
||||
assert!(has_errors.is_some());
|
||||
}
|
||||
}
|
||||
|
||||
type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
|
||||
|
@ -503,6 +503,16 @@ impl<'tcx> IntoKind for Ty<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_type_ir::visit::Flags for Ty<'tcx> {
|
||||
fn flags(&self) -> TypeFlags {
|
||||
self.0.flags
|
||||
}
|
||||
|
||||
fn outer_exclusive_binder(&self) -> DebruijnIndex {
|
||||
self.0.outer_exclusive_binder
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyParamRegion {
|
||||
/// Does this early bound region have a name? Early bound regions normally
|
||||
/// always have names except when using anonymous lifetimes (`'_`).
|
||||
|
@ -29,6 +29,16 @@ pub struct Predicate<'tcx>(
|
||||
pub(super) Interned<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>,
|
||||
);
|
||||
|
||||
impl<'tcx> rustc_type_ir::visit::Flags for Predicate<'tcx> {
|
||||
fn flags(&self) -> TypeFlags {
|
||||
self.0.flags
|
||||
}
|
||||
|
||||
fn outer_exclusive_binder(&self) -> ty::DebruijnIndex {
|
||||
self.0.outer_exclusive_binder
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Predicate<'tcx> {
|
||||
/// Gets the inner `ty::Binder<'tcx, PredicateKind<'tcx>>`.
|
||||
#[inline]
|
||||
@ -36,11 +46,13 @@ impl<'tcx> Predicate<'tcx> {
|
||||
self.0.internee
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Think about removing this.
|
||||
#[inline(always)]
|
||||
pub fn flags(self) -> TypeFlags {
|
||||
self.0.flags
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Think about removing this.
|
||||
#[inline(always)]
|
||||
pub fn outer_exclusive_binder(self) -> DebruijnIndex {
|
||||
self.0.outer_exclusive_binder
|
||||
|
@ -26,6 +26,19 @@ impl<'tcx> rustc_type_ir::IntoKind for Region<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_type_ir::visit::Flags for Region<'tcx> {
|
||||
fn flags(&self) -> TypeFlags {
|
||||
self.type_flags()
|
||||
}
|
||||
|
||||
fn outer_exclusive_binder(&self) -> ty::DebruijnIndex {
|
||||
match **self {
|
||||
ty::ReBound(debruijn, _) => debruijn.shifted_in(1),
|
||||
_ => ty::INNERMOST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Region<'tcx> {
|
||||
#[inline]
|
||||
pub fn new_early_param(
|
||||
|
@ -942,6 +942,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> rustc_type_ir::BoundVars<TyCtxt<'tcx>> for ty::Binder<'tcx, T> {
|
||||
fn bound_vars(&self) -> &'tcx List<ty::BoundVariableKind> {
|
||||
self.bound_vars
|
||||
}
|
||||
|
||||
fn has_no_bound_vars(&self) -> bool {
|
||||
self.bound_vars.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> Binder<'tcx, T> {
|
||||
/// Skips the binder and returns the "bound" value. This is a
|
||||
/// risky thing to do because it's easy to get confused about
|
||||
@ -1808,6 +1818,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
self.0.0
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Think about removing this.
|
||||
#[inline(always)]
|
||||
pub fn flags(self) -> TypeFlags {
|
||||
self.0.0.flags
|
||||
|
@ -1320,6 +1320,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
ty
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Think about removing this.
|
||||
#[inline]
|
||||
pub fn outer_exclusive_binder(self) -> ty::DebruijnIndex {
|
||||
self.0.outer_exclusive_binder
|
||||
|
@ -1,140 +1,10 @@
|
||||
use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::sso::SsoHashSet;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
|
||||
|
||||
pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> {
|
||||
/// Returns `true` if `self` has any late-bound regions that are either
|
||||
/// bound by `binder` or bound by some binder outside of `binder`.
|
||||
/// If `binder` is `ty::INNERMOST`, this indicates whether
|
||||
/// there are any late-bound regions that appear free.
|
||||
fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool {
|
||||
self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break()
|
||||
}
|
||||
|
||||
/// Returns `true` if this type has any regions that escape `binder` (and
|
||||
/// hence are not bound by it).
|
||||
fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool {
|
||||
self.has_vars_bound_at_or_above(binder.shifted_in(1))
|
||||
}
|
||||
|
||||
/// Return `true` if this type has regions that are not a part of the type.
|
||||
/// For example, `for<'a> fn(&'a i32)` return `false`, while `fn(&'a i32)`
|
||||
/// would return `true`. The latter can occur when traversing through the
|
||||
/// former.
|
||||
///
|
||||
/// See [`HasEscapingVarsVisitor`] for more information.
|
||||
fn has_escaping_bound_vars(&self) -> bool {
|
||||
self.has_vars_bound_at_or_above(ty::INNERMOST)
|
||||
}
|
||||
|
||||
fn has_type_flags(&self, flags: TypeFlags) -> bool {
|
||||
let res =
|
||||
self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags);
|
||||
trace!(?self, ?flags, ?res, "has_type_flags");
|
||||
res
|
||||
}
|
||||
fn has_projections(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PROJECTION)
|
||||
}
|
||||
fn has_inherent_projections(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_INHERENT)
|
||||
}
|
||||
fn has_opaque_types(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
|
||||
}
|
||||
fn has_coroutines(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_COROUTINE)
|
||||
}
|
||||
fn references_error(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_ERROR)
|
||||
}
|
||||
fn error_reported(&self) -> Result<(), ErrorGuaranteed> {
|
||||
if self.references_error() {
|
||||
// We must include lint errors and delayed bugs here.
|
||||
if let Some(reported) =
|
||||
ty::tls::with(|tcx| tcx.dcx().has_errors_or_lint_errors_or_delayed_bugs())
|
||||
{
|
||||
Err(reported)
|
||||
} else {
|
||||
bug!("expected some kind of error in `error_reported`");
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
fn has_non_region_param(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM)
|
||||
}
|
||||
fn has_infer_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_RE_INFER)
|
||||
}
|
||||
fn has_infer_types(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_INFER)
|
||||
}
|
||||
fn has_non_region_infer(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_INFER - TypeFlags::HAS_RE_INFER)
|
||||
}
|
||||
fn has_infer(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_INFER)
|
||||
}
|
||||
fn has_placeholders(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PLACEHOLDER)
|
||||
}
|
||||
fn has_non_region_placeholders(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PLACEHOLDER - TypeFlags::HAS_RE_PLACEHOLDER)
|
||||
}
|
||||
fn has_param(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PARAM)
|
||||
}
|
||||
/// "Free" regions in this context means that it has any region
|
||||
/// that is not (a) erased or (b) late-bound.
|
||||
fn has_free_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
|
||||
}
|
||||
|
||||
fn has_erased_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_RE_ERASED)
|
||||
}
|
||||
|
||||
/// True if there are any un-erased free regions.
|
||||
fn has_erasable_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
|
||||
}
|
||||
|
||||
/// Indicates whether this value references only 'global'
|
||||
/// generic parameters that are the same regardless of what fn we are
|
||||
/// in. This is used for caching.
|
||||
fn is_global(&self) -> bool {
|
||||
!self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES)
|
||||
}
|
||||
|
||||
/// True if there are any late-bound regions
|
||||
fn has_bound_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_RE_BOUND)
|
||||
}
|
||||
/// True if there are any late-bound non-region variables
|
||||
fn has_non_region_bound_vars(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_BOUND_VARS - TypeFlags::HAS_RE_BOUND)
|
||||
}
|
||||
/// True if there are any bound variables
|
||||
fn has_bound_vars(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_BOUND_VARS)
|
||||
}
|
||||
|
||||
/// Indicates whether this value still has parameters/placeholders/inference variables
|
||||
/// which could be replaced later, in a way that would change the results of `impl`
|
||||
/// specialization.
|
||||
fn still_further_specializable(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>>> TypeVisitableExt<'tcx> for T {}
|
||||
pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Region folder
|
||||
@ -370,185 +240,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ValidateBoundVars<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
struct FoundEscapingVars;
|
||||
|
||||
/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a
|
||||
/// bound region or a bound type.
|
||||
///
|
||||
/// So, for example, consider a type like the following, which has two binders:
|
||||
///
|
||||
/// for<'a> fn(x: for<'b> fn(&'a isize, &'b isize))
|
||||
/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope
|
||||
/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope
|
||||
///
|
||||
/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the
|
||||
/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner
|
||||
/// fn type*, that type has an escaping region: `'a`.
|
||||
///
|
||||
/// Note that what I'm calling an "escaping var" is often just called a "free var". However,
|
||||
/// we already use the term "free var". It refers to the regions or types that we use to represent
|
||||
/// bound regions or type params on a fn definition while we are type checking its body.
|
||||
///
|
||||
/// To clarify, conceptually there is no particular difference between
|
||||
/// an "escaping" var and a "free" var. However, there is a big
|
||||
/// difference in practice. Basically, when "entering" a binding
|
||||
/// level, one is generally required to do some sort of processing to
|
||||
/// a bound var, such as replacing it with a fresh/placeholder
|
||||
/// var, or making an entry in the environment to represent the
|
||||
/// scope to which it is attached, etc. An escaping var represents
|
||||
/// a bound var for which this processing has not yet been done.
|
||||
struct HasEscapingVarsVisitor {
|
||||
/// Anything bound by `outer_index` or "above" is escaping.
|
||||
outer_index: ty::DebruijnIndex,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasEscapingVarsVisitor {
|
||||
type BreakTy = FoundEscapingVars;
|
||||
|
||||
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
|
||||
&mut self,
|
||||
t: &Binder<'tcx, T>,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
self.outer_index.shift_in(1);
|
||||
let result = t.super_visit_with(self);
|
||||
self.outer_index.shift_out(1);
|
||||
result
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// If the outer-exclusive-binder is *strictly greater* than
|
||||
// `outer_index`, that means that `t` contains some content
|
||||
// bound at `outer_index` or above (because
|
||||
// `outer_exclusive_binder` is always 1 higher than the
|
||||
// content in `t`). Therefore, `t` has some escaping vars.
|
||||
if t.outer_exclusive_binder() > self.outer_index {
|
||||
ControlFlow::Break(FoundEscapingVars)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// If the region is bound by `outer_index` or anything outside
|
||||
// of outer index, then it escapes the binders we have
|
||||
// visited.
|
||||
if r.bound_at_or_above_binder(self.outer_index) {
|
||||
ControlFlow::Break(FoundEscapingVars)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// If the outer-exclusive-binder is *strictly greater* than
|
||||
// `outer_index`, that means that `ct` contains some content
|
||||
// bound at `outer_index` or above (because
|
||||
// `outer_exclusive_binder` is always 1 higher than the
|
||||
// content in `t`). Therefore, `t` has some escaping vars.
|
||||
if ct.outer_exclusive_binder() > self.outer_index {
|
||||
ControlFlow::Break(FoundEscapingVars)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
if predicate.outer_exclusive_binder() > self.outer_index {
|
||||
ControlFlow::Break(FoundEscapingVars)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
struct FoundFlags;
|
||||
|
||||
// FIXME: Optimize for checking for infer flags
|
||||
struct HasTypeFlagsVisitor {
|
||||
flags: ty::TypeFlags,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for HasTypeFlagsVisitor {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.flags.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this visitor traverses values down to the level of
|
||||
// `Ty`/`Const`/`Predicate`, but not within those types. This is because the
|
||||
// type flags at the outer layer are enough. So it's faster than it first
|
||||
// looks, particular for `Ty`/`Predicate` where it's just a field access.
|
||||
//
|
||||
// N.B. The only case where this isn't totally true is binders, which also
|
||||
// add `HAS_{RE,TY,CT}_LATE_BOUND` flag depending on the *bound variables* that
|
||||
// are present, regardless of whether those bound variables are used. This
|
||||
// is important for anonymization of binders in `TyCtxt::erase_regions`. We
|
||||
// specifically detect this case in `visit_binder`.
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasTypeFlagsVisitor {
|
||||
type BreakTy = FoundFlags;
|
||||
|
||||
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
|
||||
&mut self,
|
||||
t: &Binder<'tcx, T>,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
// If we're looking for the HAS_BINDER_VARS flag, check if the
|
||||
// binder has vars. This won't be present in the binder's bound
|
||||
// value, so we need to check here too.
|
||||
if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.bound_vars().is_empty() {
|
||||
return ControlFlow::Break(FoundFlags);
|
||||
}
|
||||
|
||||
t.super_visit_with(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// Note: no `super_visit_with` call.
|
||||
let flags = t.flags();
|
||||
if flags.intersects(self.flags) {
|
||||
ControlFlow::Break(FoundFlags)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// Note: no `super_visit_with` call, as usual for `Region`.
|
||||
let flags = r.type_flags();
|
||||
if flags.intersects(self.flags) {
|
||||
ControlFlow::Break(FoundFlags)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// Note: no `super_visit_with` call.
|
||||
if c.flags().intersects(self.flags) {
|
||||
ControlFlow::Break(FoundFlags)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// Note: no `super_visit_with` call.
|
||||
if predicate.flags().intersects(self.flags) {
|
||||
ControlFlow::Break(FoundFlags)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects all the late-bound regions at the innermost binding level
|
||||
/// into a hash set.
|
||||
struct LateBoundRegionsCollector {
|
||||
|
7
compiler/rustc_type_ir/src/binder.rs
Normal file
7
compiler/rustc_type_ir/src/binder.rs
Normal file
@ -0,0 +1,7 @@
|
||||
use crate::Interner;
|
||||
|
||||
pub trait BoundVars<I: Interner> {
|
||||
fn bound_vars(&self) -> I::BoundVars;
|
||||
|
||||
fn has_no_bound_vars(&self) -> bool;
|
||||
}
|
@ -3,8 +3,8 @@ use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
use crate::{
|
||||
BoundVar, CanonicalVarInfo, ConstKind, DebruijnIndex, DebugWithInfcx, RegionKind, TyKind,
|
||||
UniverseIndex,
|
||||
BoundVar, BoundVars, CanonicalVarInfo, ConstKind, DebruijnIndex, DebugWithInfcx, RegionKind,
|
||||
TyKind, UniverseIndex,
|
||||
};
|
||||
|
||||
pub trait Interner: Sized {
|
||||
@ -19,7 +19,10 @@ pub trait Interner: Sized {
|
||||
type GenericArg: Copy + DebugWithInfcx<Self> + Hash + Ord;
|
||||
type Term: Copy + Debug + Hash + Ord;
|
||||
|
||||
type Binder<T>;
|
||||
type Binder<T>: BoundVars<Self>;
|
||||
type BoundVars: IntoIterator<Item = Self::BoundVar>;
|
||||
type BoundVar;
|
||||
|
||||
type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator<Item = CanonicalVarInfo<Self>>;
|
||||
|
||||
// Kinds of tys
|
||||
@ -86,6 +89,9 @@ pub trait Interner: Sized {
|
||||
fn mk_bound_ty(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Ty;
|
||||
fn mk_bound_region(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Region;
|
||||
fn mk_bound_const(self, debruijn: DebruijnIndex, var: BoundVar, ty: Self::Ty) -> Self::Const;
|
||||
|
||||
/// Assert that an error has been delayed or emitted.
|
||||
fn expect_error_or_delayed_bug();
|
||||
}
|
||||
|
||||
/// Common capabilities of placeholder kinds
|
||||
|
@ -30,6 +30,7 @@ pub mod visit;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod binder;
|
||||
mod canonical;
|
||||
mod const_kind;
|
||||
mod debug;
|
||||
@ -39,6 +40,7 @@ mod interner;
|
||||
mod predicate_kind;
|
||||
mod region_kind;
|
||||
|
||||
pub use binder::*;
|
||||
pub use canonical::*;
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use codec::*;
|
||||
|
@ -45,8 +45,7 @@ use rustc_index::{Idx, IndexVec};
|
||||
use std::fmt;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::Interner;
|
||||
use crate::Lrc;
|
||||
use crate::{self as ty, BoundVars, Interner, IntoKind, Lrc, TypeFlags};
|
||||
|
||||
/// This trait is implemented for every type that can be visited,
|
||||
/// providing the skeleton of the traversal.
|
||||
@ -200,3 +199,393 @@ impl<I: Interner, T: TypeVisitable<I>, Ix: Idx> TypeVisitable<I> for IndexVec<Ix
|
||||
self.iter().try_for_each(|t| t.visit_with(visitor))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Flags {
|
||||
fn flags(&self) -> TypeFlags;
|
||||
fn outer_exclusive_binder(&self) -> ty::DebruijnIndex;
|
||||
}
|
||||
|
||||
pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> {
|
||||
fn has_type_flags(&self, flags: TypeFlags) -> bool;
|
||||
|
||||
/// Returns `true` if `self` has any late-bound regions that are either
|
||||
/// bound by `binder` or bound by some binder outside of `binder`.
|
||||
/// If `binder` is `ty::INNERMOST`, this indicates whether
|
||||
/// there are any late-bound regions that appear free.
|
||||
fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool;
|
||||
|
||||
/// Returns `true` if this type has any regions that escape `binder` (and
|
||||
/// hence are not bound by it).
|
||||
fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool {
|
||||
self.has_vars_bound_at_or_above(binder.shifted_in(1))
|
||||
}
|
||||
|
||||
/// Return `true` if this type has regions that are not a part of the type.
|
||||
/// For example, `for<'a> fn(&'a i32)` return `false`, while `fn(&'a i32)`
|
||||
/// would return `true`. The latter can occur when traversing through the
|
||||
/// former.
|
||||
///
|
||||
/// See [`HasEscapingVarsVisitor`] for more information.
|
||||
fn has_escaping_bound_vars(&self) -> bool {
|
||||
self.has_vars_bound_at_or_above(ty::INNERMOST)
|
||||
}
|
||||
|
||||
fn has_projections(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PROJECTION)
|
||||
}
|
||||
|
||||
fn has_inherent_projections(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_INHERENT)
|
||||
}
|
||||
|
||||
fn has_opaque_types(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
|
||||
}
|
||||
|
||||
fn has_coroutines(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_COROUTINE)
|
||||
}
|
||||
|
||||
fn references_error(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_ERROR)
|
||||
}
|
||||
|
||||
fn error_reported(&self) -> Result<(), I::ErrorGuaranteed>;
|
||||
|
||||
fn has_non_region_param(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM)
|
||||
}
|
||||
|
||||
fn has_infer_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_RE_INFER)
|
||||
}
|
||||
|
||||
fn has_infer_types(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_TY_INFER)
|
||||
}
|
||||
|
||||
fn has_non_region_infer(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_INFER - TypeFlags::HAS_RE_INFER)
|
||||
}
|
||||
|
||||
fn has_infer(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_INFER)
|
||||
}
|
||||
|
||||
fn has_placeholders(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PLACEHOLDER)
|
||||
}
|
||||
|
||||
fn has_non_region_placeholders(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PLACEHOLDER - TypeFlags::HAS_RE_PLACEHOLDER)
|
||||
}
|
||||
|
||||
fn has_param(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_PARAM)
|
||||
}
|
||||
|
||||
/// "Free" regions in this context means that it has any region
|
||||
/// that is not (a) erased or (b) late-bound.
|
||||
fn has_free_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
|
||||
}
|
||||
|
||||
fn has_erased_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_RE_ERASED)
|
||||
}
|
||||
|
||||
/// True if there are any un-erased free regions.
|
||||
fn has_erasable_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
|
||||
}
|
||||
|
||||
/// Indicates whether this value references only 'global'
|
||||
/// generic parameters that are the same regardless of what fn we are
|
||||
/// in. This is used for caching.
|
||||
fn is_global(&self) -> bool {
|
||||
!self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES)
|
||||
}
|
||||
|
||||
/// True if there are any late-bound regions
|
||||
fn has_bound_regions(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_RE_BOUND)
|
||||
}
|
||||
/// True if there are any late-bound non-region variables
|
||||
fn has_non_region_bound_vars(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_BOUND_VARS - TypeFlags::HAS_RE_BOUND)
|
||||
}
|
||||
/// True if there are any bound variables
|
||||
fn has_bound_vars(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::HAS_BOUND_VARS)
|
||||
}
|
||||
|
||||
/// Indicates whether this value still has parameters/placeholders/inference variables
|
||||
/// which could be replaced later, in a way that would change the results of `impl`
|
||||
/// specialization.
|
||||
fn still_further_specializable(&self) -> bool {
|
||||
self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner, T: TypeVisitable<I>> TypeVisitableExt<I> for T
|
||||
where
|
||||
I::Ty: Flags,
|
||||
I::Region: Flags,
|
||||
I::Const: Flags,
|
||||
I::Predicate: Flags,
|
||||
{
|
||||
fn has_type_flags(&self, flags: TypeFlags) -> bool {
|
||||
let res =
|
||||
self.visit_with(&mut HasTypeFlagsVisitor { flags }) == ControlFlow::Break(FoundFlags);
|
||||
res
|
||||
}
|
||||
|
||||
fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool {
|
||||
self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break()
|
||||
}
|
||||
|
||||
fn error_reported(&self) -> Result<(), I::ErrorGuaranteed> {
|
||||
if self.references_error() {
|
||||
if let ControlFlow::Break(guar) = self.visit_with(&mut HasErrorVisitor) {
|
||||
Err(guar)
|
||||
} else {
|
||||
panic!("type flags said there was an error, but now there is not")
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
struct FoundFlags;
|
||||
|
||||
// FIXME: Optimize for checking for infer flags
|
||||
struct HasTypeFlagsVisitor {
|
||||
flags: ty::TypeFlags,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for HasTypeFlagsVisitor {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.flags.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this visitor traverses values down to the level of
|
||||
// `Ty`/`Const`/`Predicate`, but not within those types. This is because the
|
||||
// type flags at the outer layer are enough. So it's faster than it first
|
||||
// looks, particular for `Ty`/`Predicate` where it's just a field access.
|
||||
//
|
||||
// N.B. The only case where this isn't totally true is binders, which also
|
||||
// add `HAS_{RE,TY,CT}_LATE_BOUND` flag depending on the *bound variables* that
|
||||
// are present, regardless of whether those bound variables are used. This
|
||||
// is important for anonymization of binders in `TyCtxt::erase_regions`. We
|
||||
// specifically detect this case in `visit_binder`.
|
||||
impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor
|
||||
where
|
||||
I::Ty: Flags,
|
||||
I::Region: Flags,
|
||||
I::Const: Flags,
|
||||
I::Predicate: Flags,
|
||||
{
|
||||
type BreakTy = FoundFlags;
|
||||
|
||||
fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &I::Binder<T>) -> ControlFlow<Self::BreakTy>
|
||||
where
|
||||
I::Binder<T>: TypeSuperVisitable<I>,
|
||||
{
|
||||
// If we're looking for the HAS_BINDER_VARS flag, check if the
|
||||
// binder has vars. This won't be present in the binder's bound
|
||||
// value, so we need to check here too.
|
||||
if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.has_no_bound_vars() {
|
||||
return ControlFlow::Break(FoundFlags);
|
||||
}
|
||||
|
||||
t.super_visit_with(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> {
|
||||
// Note: no `super_visit_with` call.
|
||||
let flags = t.flags();
|
||||
if flags.intersects(self.flags) {
|
||||
ControlFlow::Break(FoundFlags)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_region(&mut self, r: I::Region) -> ControlFlow<Self::BreakTy> {
|
||||
// Note: no `super_visit_with` call, as usual for `Region`.
|
||||
let flags = r.flags();
|
||||
if flags.intersects(self.flags) {
|
||||
ControlFlow::Break(FoundFlags)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_const(&mut self, c: I::Const) -> ControlFlow<Self::BreakTy> {
|
||||
// Note: no `super_visit_with` call.
|
||||
if c.flags().intersects(self.flags) {
|
||||
ControlFlow::Break(FoundFlags)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_predicate(&mut self, predicate: I::Predicate) -> ControlFlow<Self::BreakTy> {
|
||||
// Note: no `super_visit_with` call.
|
||||
if predicate.flags().intersects(self.flags) {
|
||||
ControlFlow::Break(FoundFlags)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
struct FoundEscapingVars;
|
||||
|
||||
/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a
|
||||
/// bound region or a bound type.
|
||||
///
|
||||
/// So, for example, consider a type like the following, which has two binders:
|
||||
///
|
||||
/// for<'a> fn(x: for<'b> fn(&'a isize, &'b isize))
|
||||
/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope
|
||||
/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope
|
||||
///
|
||||
/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the
|
||||
/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner
|
||||
/// fn type*, that type has an escaping region: `'a`.
|
||||
///
|
||||
/// Note that what I'm calling an "escaping var" is often just called a "free var". However,
|
||||
/// we already use the term "free var". It refers to the regions or types that we use to represent
|
||||
/// bound regions or type params on a fn definition while we are type checking its body.
|
||||
///
|
||||
/// To clarify, conceptually there is no particular difference between
|
||||
/// an "escaping" var and a "free" var. However, there is a big
|
||||
/// difference in practice. Basically, when "entering" a binding
|
||||
/// level, one is generally required to do some sort of processing to
|
||||
/// a bound var, such as replacing it with a fresh/placeholder
|
||||
/// var, or making an entry in the environment to represent the
|
||||
/// scope to which it is attached, etc. An escaping var represents
|
||||
/// a bound var for which this processing has not yet been done.
|
||||
struct HasEscapingVarsVisitor {
|
||||
/// Anything bound by `outer_index` or "above" is escaping.
|
||||
outer_index: ty::DebruijnIndex,
|
||||
}
|
||||
|
||||
impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor
|
||||
where
|
||||
I::Ty: Flags,
|
||||
I::Region: Flags,
|
||||
I::Const: Flags,
|
||||
I::Predicate: Flags,
|
||||
{
|
||||
type BreakTy = FoundEscapingVars;
|
||||
|
||||
fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &I::Binder<T>) -> ControlFlow<Self::BreakTy>
|
||||
where
|
||||
I::Binder<T>: TypeSuperVisitable<I>,
|
||||
{
|
||||
self.outer_index.shift_in(1);
|
||||
let result = t.super_visit_with(self);
|
||||
self.outer_index.shift_out(1);
|
||||
result
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> {
|
||||
// If the outer-exclusive-binder is *strictly greater* than
|
||||
// `outer_index`, that means that `t` contains some content
|
||||
// bound at `outer_index` or above (because
|
||||
// `outer_exclusive_binder` is always 1 higher than the
|
||||
// content in `t`). Therefore, `t` has some escaping vars.
|
||||
if t.outer_exclusive_binder() > self.outer_index {
|
||||
ControlFlow::Break(FoundEscapingVars)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_region(&mut self, r: I::Region) -> ControlFlow<Self::BreakTy> {
|
||||
// If the region is bound by `outer_index` or anything outside
|
||||
// of outer index, then it escapes the binders we have
|
||||
// visited.
|
||||
if r.outer_exclusive_binder() > self.outer_index {
|
||||
ControlFlow::Break(FoundEscapingVars)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, ct: I::Const) -> ControlFlow<Self::BreakTy> {
|
||||
// If the outer-exclusive-binder is *strictly greater* than
|
||||
// `outer_index`, that means that `ct` contains some content
|
||||
// bound at `outer_index` or above (because
|
||||
// `outer_exclusive_binder` is always 1 higher than the
|
||||
// content in `t`). Therefore, `t` has some escaping vars.
|
||||
if ct.outer_exclusive_binder() > self.outer_index {
|
||||
ControlFlow::Break(FoundEscapingVars)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_predicate(&mut self, predicate: I::Predicate) -> ControlFlow<Self::BreakTy> {
|
||||
if predicate.outer_exclusive_binder() > self.outer_index {
|
||||
ControlFlow::Break(FoundEscapingVars)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HasErrorVisitor;
|
||||
|
||||
impl<I: Interner> TypeVisitor<I> for HasErrorVisitor
|
||||
where
|
||||
I::Ty: Flags,
|
||||
I::Region: Flags,
|
||||
I::Const: Flags,
|
||||
I::Predicate: Flags,
|
||||
{
|
||||
type BreakTy = I::ErrorGuaranteed;
|
||||
|
||||
fn visit_ty(&mut self, t: <I as Interner>::Ty) -> ControlFlow<Self::BreakTy>
|
||||
where
|
||||
<I as Interner>::Ty: TypeSuperVisitable<I>,
|
||||
{
|
||||
if let ty::Error(guar) = t.kind() {
|
||||
ControlFlow::Break(guar)
|
||||
} else {
|
||||
t.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, c: <I as Interner>::Const) -> ControlFlow<Self::BreakTy>
|
||||
where
|
||||
<I as Interner>::Const: TypeSuperVisitable<I>,
|
||||
{
|
||||
if let ty::ConstKind::Error(guar) = c.kind() {
|
||||
ControlFlow::Break(guar)
|
||||
} else {
|
||||
c.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_region(&mut self, r: <I as Interner>::Region) -> ControlFlow<Self::BreakTy> {
|
||||
if let ty::ReError(guar) = r.kind() {
|
||||
ControlFlow::Break(guar)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user