mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Uplift EarlyBinder
This commit is contained in:
parent
bbcdb4fd3e
commit
993553ceb8
@ -4686,6 +4686,7 @@ dependencies = [
|
||||
"rustc_span",
|
||||
"rustc_type_ir_macros",
|
||||
"smallvec",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -184,6 +184,15 @@ impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
|
||||
Const::new_var(tcx, vid, ty)
|
||||
}
|
||||
|
||||
fn new_bound(
|
||||
interner: TyCtxt<'tcx>,
|
||||
debruijn: ty::DebruijnIndex,
|
||||
var: ty::BoundVar,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Self {
|
||||
Const::new_bound(interner, debruijn, var, ty)
|
||||
}
|
||||
|
||||
fn new_anon_bound(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
debruijn: ty::DebruijnIndex,
|
||||
|
@ -3,7 +3,9 @@ use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
pub use rustc_type_ir::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable};
|
||||
pub use rustc_type_ir::fold::{
|
||||
shift_region, shift_vars, FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable,
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Some sample folders
|
||||
@ -412,103 +414,3 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
Binder::bind_with_vars(inner, bound_vars)
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Shifter
|
||||
//
|
||||
// Shifts the De Bruijn indices on all escaping bound vars by a
|
||||
// fixed amount. Useful in instantiation or when otherwise introducing
|
||||
// a binding level that is not intended to capture the existing bound
|
||||
// vars. See comment on `shift_vars_through_binders` method in
|
||||
// `rustc_middle/src/ty/generic_args.rs` for more details.
|
||||
|
||||
struct Shifter<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
current_index: ty::DebruijnIndex,
|
||||
amount: u32,
|
||||
}
|
||||
|
||||
impl<'tcx> Shifter<'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'tcx>, amount: u32) -> Self {
|
||||
Shifter { tcx, current_index: ty::INNERMOST, amount }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Shifter<'tcx> {
|
||||
fn interner(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
|
||||
&mut self,
|
||||
t: ty::Binder<'tcx, T>,
|
||||
) -> ty::Binder<'tcx, T> {
|
||||
self.current_index.shift_in(1);
|
||||
let t = t.super_fold_with(self);
|
||||
self.current_index.shift_out(1);
|
||||
t
|
||||
}
|
||||
|
||||
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||
match *r {
|
||||
ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
|
||||
let debruijn = debruijn.shifted_in(self.amount);
|
||||
ty::Region::new_bound(self.tcx, debruijn, br)
|
||||
}
|
||||
_ => r,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
match *ty.kind() {
|
||||
ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
|
||||
let debruijn = debruijn.shifted_in(self.amount);
|
||||
Ty::new_bound(self.tcx, debruijn, bound_ty)
|
||||
}
|
||||
|
||||
_ if ty.has_vars_bound_at_or_above(self.current_index) => ty.super_fold_with(self),
|
||||
_ => ty,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
if let ty::ConstKind::Bound(debruijn, bound_ct) = ct.kind()
|
||||
&& debruijn >= self.current_index
|
||||
{
|
||||
let debruijn = debruijn.shifted_in(self.amount);
|
||||
ty::Const::new_bound(self.tcx, debruijn, bound_ct, ct.ty())
|
||||
} else {
|
||||
ct.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
|
||||
if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shift_region<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
region: ty::Region<'tcx>,
|
||||
amount: u32,
|
||||
) -> ty::Region<'tcx> {
|
||||
match *region {
|
||||
ty::ReBound(debruijn, br) if amount > 0 => {
|
||||
ty::Region::new_bound(tcx, debruijn.shifted_in(amount), br)
|
||||
}
|
||||
_ => region,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shift_vars<'tcx, T>(tcx: TyCtxt<'tcx>, value: T, amount: u32) -> T
|
||||
where
|
||||
T: TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
debug!("shift_vars(value={:?}, amount={})", value, amount);
|
||||
|
||||
if amount == 0 || !value.has_escaping_bound_vars() {
|
||||
return value;
|
||||
}
|
||||
|
||||
value.fold_with(&mut Shifter::new(tcx, amount))
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
// Generic arguments.
|
||||
|
||||
use crate::ty::codec::{TyDecoder, TyEncoder};
|
||||
use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable};
|
||||
use crate::ty::fold::{FallibleTypeFolder, TypeFoldable};
|
||||
use crate::ty::sty::{ClosureArgs, CoroutineArgs, CoroutineClosureArgs, InlineConstArgs};
|
||||
use crate::ty::visit::{TypeVisitable, TypeVisitableExt, TypeVisitor};
|
||||
use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt};
|
||||
use crate::ty::visit::{TypeVisitable, TypeVisitor};
|
||||
use crate::ty::{self, Lift, List, Ty, TyCtxt};
|
||||
|
||||
use rustc_ast_ir::visit::VisitorResult;
|
||||
use rustc_ast_ir::walk_visitable_list;
|
||||
@ -12,19 +12,15 @@ use rustc_data_structures::intern::Interned;
|
||||
use rustc_errors::{DiagArgValue, IntoDiagArg};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_macros::extension;
|
||||
use rustc_macros::{
|
||||
Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable,
|
||||
};
|
||||
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
|
||||
use rustc_serialize::{Decodable, Encodable};
|
||||
use rustc_type_ir::WithCachedTypeInfo;
|
||||
use smallvec::SmallVec;
|
||||
use tracing::debug;
|
||||
|
||||
use core::intrinsics;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::num::NonZero;
|
||||
use std::ops::Deref;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
pub type GenericArgKind<'tcx> = rustc_type_ir::GenericArgKind<TyCtxt<'tcx>>;
|
||||
@ -576,500 +572,6 @@ impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>>> TypeVisitable<TyCtxt<'tcx>> for &'tcx
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to [`super::Binder`] except that it tracks early bound generics, i.e. `struct Foo<T>(T)`
|
||||
/// needs `T` instantiated immediately. This type primarily exists to avoid forgetting to call
|
||||
/// `instantiate`.
|
||||
///
|
||||
/// If you don't have anything to `instantiate`, you may be looking for
|
||||
/// [`instantiate_identity`](EarlyBinder::instantiate_identity) or [`skip_binder`](EarlyBinder::skip_binder).
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
#[derive(Encodable, Decodable, HashStable)]
|
||||
pub struct EarlyBinder<'tcx, T> {
|
||||
value: T,
|
||||
_tcx: PhantomData<&'tcx ()>,
|
||||
}
|
||||
|
||||
/// For early binders, you should first call `instantiate` before using any visitors.
|
||||
impl<'tcx, T> !TypeFoldable<TyCtxt<'tcx>> for ty::EarlyBinder<'tcx, T> {}
|
||||
impl<'tcx, T> !TypeVisitable<TyCtxt<'tcx>> for ty::EarlyBinder<'tcx, T> {}
|
||||
|
||||
impl<'tcx, T> EarlyBinder<'tcx, T> {
|
||||
pub fn bind(value: T) -> EarlyBinder<'tcx, T> {
|
||||
EarlyBinder { value, _tcx: PhantomData }
|
||||
}
|
||||
|
||||
pub fn as_ref(&self) -> EarlyBinder<'tcx, &T> {
|
||||
EarlyBinder { value: &self.value, _tcx: PhantomData }
|
||||
}
|
||||
|
||||
pub fn map_bound_ref<F, U>(&self, f: F) -> EarlyBinder<'tcx, U>
|
||||
where
|
||||
F: FnOnce(&T) -> U,
|
||||
{
|
||||
self.as_ref().map_bound(f)
|
||||
}
|
||||
|
||||
pub fn map_bound<F, U>(self, f: F) -> EarlyBinder<'tcx, U>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
let value = f(self.value);
|
||||
EarlyBinder { value, _tcx: PhantomData }
|
||||
}
|
||||
|
||||
pub fn try_map_bound<F, U, E>(self, f: F) -> Result<EarlyBinder<'tcx, U>, E>
|
||||
where
|
||||
F: FnOnce(T) -> Result<U, E>,
|
||||
{
|
||||
let value = f(self.value)?;
|
||||
Ok(EarlyBinder { value, _tcx: PhantomData })
|
||||
}
|
||||
|
||||
pub fn rebind<U>(&self, value: U) -> EarlyBinder<'tcx, U> {
|
||||
EarlyBinder { value, _tcx: PhantomData }
|
||||
}
|
||||
|
||||
/// Skips the binder and returns the "bound" value.
|
||||
/// This can be used to extract data that does not depend on generic parameters
|
||||
/// (e.g., getting the `DefId` of the inner value or getting the number of
|
||||
/// arguments of an `FnSig`). Otherwise, consider using
|
||||
/// [`instantiate_identity`](EarlyBinder::instantiate_identity).
|
||||
///
|
||||
/// To skip the binder on `x: &EarlyBinder<'tcx, T>` to obtain `&T`, leverage
|
||||
/// [`EarlyBinder::as_ref`](EarlyBinder::as_ref): `x.as_ref().skip_binder()`.
|
||||
///
|
||||
/// See also [`Binder::skip_binder`](super::Binder::skip_binder), which is
|
||||
/// the analogous operation on [`super::Binder`].
|
||||
pub fn skip_binder(self) -> T {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> EarlyBinder<'tcx, Option<T>> {
|
||||
pub fn transpose(self) -> Option<EarlyBinder<'tcx, T>> {
|
||||
self.value.map(|value| EarlyBinder { value, _tcx: PhantomData })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, 's, I: IntoIterator> EarlyBinder<'tcx, I>
|
||||
where
|
||||
I::Item: TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
pub fn iter_instantiated(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
args: &'s [GenericArg<'tcx>],
|
||||
) -> IterInstantiated<'s, 'tcx, I> {
|
||||
IterInstantiated { it: self.value.into_iter(), tcx, args }
|
||||
}
|
||||
|
||||
/// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity),
|
||||
/// but on an iterator of `TypeFoldable` values.
|
||||
pub fn instantiate_identity_iter(self) -> I::IntoIter {
|
||||
self.value.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IterInstantiated<'s, 'tcx, I: IntoIterator> {
|
||||
it: I::IntoIter,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
args: &'s [GenericArg<'tcx>],
|
||||
}
|
||||
|
||||
impl<'tcx, I: IntoIterator> Iterator for IterInstantiated<'_, 'tcx, I>
|
||||
where
|
||||
I::Item: TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(
|
||||
EarlyBinder { value: self.it.next()?, _tcx: PhantomData }
|
||||
.instantiate(self.tcx, self.args),
|
||||
)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.it.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, I: IntoIterator> DoubleEndedIterator for IterInstantiated<'_, 'tcx, I>
|
||||
where
|
||||
I::IntoIter: DoubleEndedIterator,
|
||||
I::Item: TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
Some(
|
||||
EarlyBinder { value: self.it.next_back()?, _tcx: PhantomData }
|
||||
.instantiate(self.tcx, self.args),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, I: IntoIterator> ExactSizeIterator for IterInstantiated<'_, 'tcx, I>
|
||||
where
|
||||
I::IntoIter: ExactSizeIterator,
|
||||
I::Item: TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<'tcx, 's, I: IntoIterator> EarlyBinder<'tcx, I>
|
||||
where
|
||||
I::Item: Deref,
|
||||
<I::Item as Deref>::Target: Copy + TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
pub fn iter_instantiated_copied(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
args: &'s [GenericArg<'tcx>],
|
||||
) -> IterInstantiatedCopied<'s, 'tcx, I> {
|
||||
IterInstantiatedCopied { it: self.value.into_iter(), tcx, args }
|
||||
}
|
||||
|
||||
/// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity),
|
||||
/// but on an iterator of values that deref to a `TypeFoldable`.
|
||||
pub fn instantiate_identity_iter_copied(
|
||||
self,
|
||||
) -> impl Iterator<Item = <I::Item as Deref>::Target> {
|
||||
self.value.into_iter().map(|v| *v)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IterInstantiatedCopied<'a, 'tcx, I: IntoIterator> {
|
||||
it: I::IntoIter,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
args: &'a [GenericArg<'tcx>],
|
||||
}
|
||||
|
||||
impl<'tcx, I: IntoIterator> Iterator for IterInstantiatedCopied<'_, 'tcx, I>
|
||||
where
|
||||
I::Item: Deref,
|
||||
<I::Item as Deref>::Target: Copy + TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
type Item = <I::Item as Deref>::Target;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.it.next().map(|value| {
|
||||
EarlyBinder { value: *value, _tcx: PhantomData }.instantiate(self.tcx, self.args)
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.it.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, I: IntoIterator> DoubleEndedIterator for IterInstantiatedCopied<'_, 'tcx, I>
|
||||
where
|
||||
I::IntoIter: DoubleEndedIterator,
|
||||
I::Item: Deref,
|
||||
<I::Item as Deref>::Target: Copy + TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.it.next_back().map(|value| {
|
||||
EarlyBinder { value: *value, _tcx: PhantomData }.instantiate(self.tcx, self.args)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, I: IntoIterator> ExactSizeIterator for IterInstantiatedCopied<'_, 'tcx, I>
|
||||
where
|
||||
I::IntoIter: ExactSizeIterator,
|
||||
I::Item: Deref,
|
||||
<I::Item as Deref>::Target: Copy + TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
}
|
||||
|
||||
pub struct EarlyBinderIter<'tcx, T> {
|
||||
t: T,
|
||||
_tcx: PhantomData<&'tcx ()>,
|
||||
}
|
||||
|
||||
impl<'tcx, T: IntoIterator> EarlyBinder<'tcx, T> {
|
||||
pub fn transpose_iter(self) -> EarlyBinderIter<'tcx, T::IntoIter> {
|
||||
EarlyBinderIter { t: self.value.into_iter(), _tcx: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T: Iterator> Iterator for EarlyBinderIter<'tcx, T> {
|
||||
type Item = EarlyBinder<'tcx, T::Item>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.t.next().map(|value| EarlyBinder { value, _tcx: PhantomData })
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.t.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T: TypeFoldable<TyCtxt<'tcx>>> ty::EarlyBinder<'tcx, T> {
|
||||
pub fn instantiate(self, tcx: TyCtxt<'tcx>, args: &[GenericArg<'tcx>]) -> T {
|
||||
let mut folder = ArgFolder { tcx, args, binders_passed: 0 };
|
||||
self.value.fold_with(&mut folder)
|
||||
}
|
||||
|
||||
/// Makes the identity replacement `T0 => T0, ..., TN => TN`.
|
||||
/// Conceptually, this converts universally bound variables into placeholders
|
||||
/// when inside of a given item.
|
||||
///
|
||||
/// For example, consider `for<T> fn foo<T>(){ .. }`:
|
||||
/// - Outside of `foo`, `T` is bound (represented by the presence of `EarlyBinder`).
|
||||
/// - Inside of the body of `foo`, we treat `T` as a placeholder by calling
|
||||
/// `instantiate_identity` to discharge the `EarlyBinder`.
|
||||
pub fn instantiate_identity(self) -> T {
|
||||
self.value
|
||||
}
|
||||
|
||||
/// Returns the inner value, but only if it contains no bound vars.
|
||||
pub fn no_bound_vars(self) -> Option<T> {
|
||||
if !self.value.has_param() { Some(self.value) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// The actual instantiation engine itself is a type folder.
|
||||
|
||||
struct ArgFolder<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
args: &'a [GenericArg<'tcx>],
|
||||
|
||||
/// Number of region binders we have passed through while doing the instantiation
|
||||
binders_passed: u32,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ArgFolder<'a, 'tcx> {
|
||||
#[inline]
|
||||
fn interner(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
|
||||
&mut self,
|
||||
t: ty::Binder<'tcx, T>,
|
||||
) -> ty::Binder<'tcx, T> {
|
||||
self.binders_passed += 1;
|
||||
let t = t.super_fold_with(self);
|
||||
self.binders_passed -= 1;
|
||||
t
|
||||
}
|
||||
|
||||
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn region_param_out_of_range(data: ty::EarlyParamRegion, args: &[GenericArg<'_>]) -> ! {
|
||||
bug!(
|
||||
"Region parameter out of range when instantiating in region {} (index={}, args = {:?})",
|
||||
data.name,
|
||||
data.index,
|
||||
args,
|
||||
)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn region_param_invalid(data: ty::EarlyParamRegion, other: GenericArgKind<'_>) -> ! {
|
||||
bug!(
|
||||
"Unexpected parameter {:?} when instantiating in region {} (index={})",
|
||||
other,
|
||||
data.name,
|
||||
data.index
|
||||
)
|
||||
}
|
||||
|
||||
// Note: This routine only handles regions that are bound on
|
||||
// type declarations and other outer declarations, not those
|
||||
// bound in *fn types*. Region instantiation of the bound
|
||||
// regions that appear in a function signature is done using
|
||||
// the specialized routine `ty::replace_late_regions()`.
|
||||
match *r {
|
||||
ty::ReEarlyParam(data) => {
|
||||
let rk = self.args.get(data.index as usize).map(|k| k.unpack());
|
||||
match rk {
|
||||
Some(GenericArgKind::Lifetime(lt)) => self.shift_region_through_binders(lt),
|
||||
Some(other) => region_param_invalid(data, other),
|
||||
None => region_param_out_of_range(data, self.args),
|
||||
}
|
||||
}
|
||||
ty::ReBound(..)
|
||||
| ty::ReLateParam(_)
|
||||
| ty::ReStatic
|
||||
| ty::RePlaceholder(_)
|
||||
| ty::ReErased
|
||||
| ty::ReError(_) => r,
|
||||
ty::ReVar(_) => bug!("unexpected region: {r:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if !t.has_param() {
|
||||
return t;
|
||||
}
|
||||
|
||||
match *t.kind() {
|
||||
ty::Param(p) => self.ty_for_param(p, t),
|
||||
_ => t.super_fold_with(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
if let ty::ConstKind::Param(p) = c.kind() {
|
||||
self.const_for_param(p, c)
|
||||
} else {
|
||||
c.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ArgFolder<'a, 'tcx> {
|
||||
fn ty_for_param(&self, p: ty::ParamTy, source_ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
// Look up the type in the args. It really should be in there.
|
||||
let opt_ty = self.args.get(p.index as usize).map(|k| k.unpack());
|
||||
let ty = match opt_ty {
|
||||
Some(GenericArgKind::Type(ty)) => ty,
|
||||
Some(kind) => self.type_param_expected(p, source_ty, kind),
|
||||
None => self.type_param_out_of_range(p, source_ty),
|
||||
};
|
||||
|
||||
self.shift_vars_through_binders(ty)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn type_param_expected(&self, p: ty::ParamTy, ty: Ty<'tcx>, kind: GenericArgKind<'tcx>) -> ! {
|
||||
bug!(
|
||||
"expected type for `{:?}` ({:?}/{}) but found {:?} when instantiating, args={:?}",
|
||||
p,
|
||||
ty,
|
||||
p.index,
|
||||
kind,
|
||||
self.args,
|
||||
)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn type_param_out_of_range(&self, p: ty::ParamTy, ty: Ty<'tcx>) -> ! {
|
||||
bug!(
|
||||
"type parameter `{:?}` ({:?}/{}) out of range when instantiating, args={:?}",
|
||||
p,
|
||||
ty,
|
||||
p.index,
|
||||
self.args,
|
||||
)
|
||||
}
|
||||
|
||||
fn const_for_param(&self, p: ParamConst, source_ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
// Look up the const in the args. It really should be in there.
|
||||
let opt_ct = self.args.get(p.index as usize).map(|k| k.unpack());
|
||||
let ct = match opt_ct {
|
||||
Some(GenericArgKind::Const(ct)) => ct,
|
||||
Some(kind) => self.const_param_expected(p, source_ct, kind),
|
||||
None => self.const_param_out_of_range(p, source_ct),
|
||||
};
|
||||
|
||||
self.shift_vars_through_binders(ct)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn const_param_expected(
|
||||
&self,
|
||||
p: ty::ParamConst,
|
||||
ct: ty::Const<'tcx>,
|
||||
kind: GenericArgKind<'tcx>,
|
||||
) -> ! {
|
||||
bug!(
|
||||
"expected const for `{:?}` ({:?}/{}) but found {:?} when instantiating args={:?}",
|
||||
p,
|
||||
ct,
|
||||
p.index,
|
||||
kind,
|
||||
self.args,
|
||||
)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn const_param_out_of_range(&self, p: ty::ParamConst, ct: ty::Const<'tcx>) -> ! {
|
||||
bug!(
|
||||
"const parameter `{:?}` ({:?}/{}) out of range when instantiating args={:?}",
|
||||
p,
|
||||
ct,
|
||||
p.index,
|
||||
self.args,
|
||||
)
|
||||
}
|
||||
|
||||
/// It is sometimes necessary to adjust the De Bruijn indices during instantiation. This occurs
|
||||
/// when we are instantating a type with escaping bound vars into a context where we have
|
||||
/// passed through binders. That's quite a mouthful. Let's see an example:
|
||||
///
|
||||
/// ```
|
||||
/// type Func<A> = fn(A);
|
||||
/// type MetaFunc = for<'a> fn(Func<&'a i32>);
|
||||
/// ```
|
||||
///
|
||||
/// The type `MetaFunc`, when fully expanded, will be
|
||||
/// ```ignore (illustrative)
|
||||
/// for<'a> fn(fn(&'a i32))
|
||||
/// // ^~ ^~ ^~~
|
||||
/// // | | |
|
||||
/// // | | DebruijnIndex of 2
|
||||
/// // Binders
|
||||
/// ```
|
||||
/// Here the `'a` lifetime is bound in the outer function, but appears as an argument of the
|
||||
/// inner one. Therefore, that appearance will have a DebruijnIndex of 2, because we must skip
|
||||
/// over the inner binder (remember that we count De Bruijn indices from 1). However, in the
|
||||
/// definition of `MetaFunc`, the binder is not visible, so the type `&'a i32` will have a
|
||||
/// De Bruijn index of 1. It's only during the instantiation that we can see we must increase the
|
||||
/// depth by 1 to account for the binder that we passed through.
|
||||
///
|
||||
/// As a second example, consider this twist:
|
||||
///
|
||||
/// ```
|
||||
/// type FuncTuple<A> = (A,fn(A));
|
||||
/// type MetaFuncTuple = for<'a> fn(FuncTuple<&'a i32>);
|
||||
/// ```
|
||||
///
|
||||
/// Here the final type will be:
|
||||
/// ```ignore (illustrative)
|
||||
/// for<'a> fn((&'a i32, fn(&'a i32)))
|
||||
/// // ^~~ ^~~
|
||||
/// // | |
|
||||
/// // DebruijnIndex of 1 |
|
||||
/// // DebruijnIndex of 2
|
||||
/// ```
|
||||
/// As indicated in the diagram, here the same type `&'a i32` is instantiated once, but in the
|
||||
/// first case we do not increase the De Bruijn index and in the second case we do. The reason
|
||||
/// is that only in the second case have we passed through a fn binder.
|
||||
fn shift_vars_through_binders<T: TypeFoldable<TyCtxt<'tcx>>>(&self, val: T) -> T {
|
||||
debug!(
|
||||
"shift_vars(val={:?}, binders_passed={:?}, has_escaping_bound_vars={:?})",
|
||||
val,
|
||||
self.binders_passed,
|
||||
val.has_escaping_bound_vars()
|
||||
);
|
||||
|
||||
if self.binders_passed == 0 || !val.has_escaping_bound_vars() {
|
||||
return val;
|
||||
}
|
||||
|
||||
let result = ty::fold::shift_vars(TypeFolder::interner(self), val, self.binders_passed);
|
||||
debug!("shift_vars: shifted result = {:?}", result);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn shift_region_through_binders(&self, region: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||
if self.binders_passed == 0 || !region.has_escaping_bound_vars() {
|
||||
return region;
|
||||
}
|
||||
ty::fold::shift_region(self.tcx, region, self.binders_passed)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores the user-given args to reach some fully qualified path
|
||||
/// (e.g., `<T>::Item` or `<T as Trait>::Item`).
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
|
||||
|
@ -114,9 +114,9 @@ pub use self::rvalue_scopes::RvalueScopes;
|
||||
pub use self::sty::{
|
||||
AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig,
|
||||
ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, CoroutineClosureArgs,
|
||||
CoroutineClosureArgsParts, CoroutineClosureSignature, FnSig, GenSig, InlineConstArgs,
|
||||
InlineConstArgsParts, ParamConst, ParamTy, PolyFnSig, TyKind, TypeAndMut, UpvarArgs,
|
||||
VarianceDiagInfo,
|
||||
CoroutineClosureArgsParts, CoroutineClosureSignature, EarlyBinder, FnSig, GenSig,
|
||||
InlineConstArgs, InlineConstArgsParts, ParamConst, ParamTy, PolyFnSig, TyKind, TypeAndMut,
|
||||
UpvarArgs, VarianceDiagInfo,
|
||||
};
|
||||
pub use self::trait_def::TraitDef;
|
||||
pub use self::typeck_results::{
|
||||
|
@ -138,6 +138,14 @@ impl<'tcx> Region<'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_type_ir::inherent::Region<TyCtxt<'tcx>> for Region<'tcx> {
|
||||
fn new_bound(
|
||||
interner: TyCtxt<'tcx>,
|
||||
debruijn: ty::DebruijnIndex,
|
||||
var: ty::BoundRegion,
|
||||
) -> Self {
|
||||
Region::new_bound(interner, debruijn, var)
|
||||
}
|
||||
|
||||
fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self {
|
||||
Region::new_bound(tcx, debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::BrAnon })
|
||||
}
|
||||
@ -340,6 +348,12 @@ pub struct EarlyParamRegion {
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
impl rustc_type_ir::inherent::ParamLike for EarlyParamRegion {
|
||||
fn index(self) -> u32 {
|
||||
self.index
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for EarlyParamRegion {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// FIXME(BoxyUwU): self.def_id goes first because of `erased-regions-in-hidden-ty.rs` being impossible to write
|
||||
|
@ -40,6 +40,7 @@ pub type TypeAndMut<'tcx> = ir::TypeAndMut<TyCtxt<'tcx>>;
|
||||
pub type AliasTy<'tcx> = ir::AliasTy<TyCtxt<'tcx>>;
|
||||
pub type FnSig<'tcx> = ir::FnSig<TyCtxt<'tcx>>;
|
||||
pub type Binder<'tcx, T> = ir::Binder<TyCtxt<'tcx>, T>;
|
||||
pub type EarlyBinder<'tcx, T> = ir::EarlyBinder<TyCtxt<'tcx>, T>;
|
||||
|
||||
pub trait Article {
|
||||
fn article(&self) -> &'static str;
|
||||
@ -954,6 +955,12 @@ pub struct ParamTy {
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
impl rustc_type_ir::inherent::ParamLike for ParamTy {
|
||||
fn index(self) -> u32 {
|
||||
self.index
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ParamTy {
|
||||
pub fn new(index: u32, name: Symbol) -> ParamTy {
|
||||
ParamTy { index, name }
|
||||
@ -982,6 +989,12 @@ pub struct ParamConst {
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
impl rustc_type_ir::inherent::ParamLike for ParamConst {
|
||||
fn index(self) -> u32 {
|
||||
self.index
|
||||
}
|
||||
}
|
||||
|
||||
impl ParamConst {
|
||||
pub fn new(index: u32, name: Symbol) -> ParamConst {
|
||||
ParamConst { index, name }
|
||||
@ -1423,6 +1436,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
|
||||
Ty::new_var(tcx, vid)
|
||||
}
|
||||
|
||||
fn new_bound(interner: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundTy) -> Self {
|
||||
Ty::new_bound(interner, debruijn, var)
|
||||
}
|
||||
|
||||
fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self {
|
||||
Ty::new_bound(tcx, debruijn, ty::BoundTy { var, kind: ty::BoundTyKind::Anon })
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ rustc_serialize = { path = "../rustc_serialize", optional = true }
|
||||
rustc_span = { path = "../rustc_span", optional = true }
|
||||
rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
|
||||
smallvec = { version = "1.8.1", default-features = false }
|
||||
tracing = "0.1"
|
||||
# tidy-alphabetical-end
|
||||
|
||||
[features]
|
||||
|
@ -1,12 +1,14 @@
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{ControlFlow, Deref};
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use rustc_macros::HashStable_NoContext;
|
||||
use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
|
||||
use rustc_serialize::Decodable;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
|
||||
use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable};
|
||||
use crate::inherent::*;
|
||||
use crate::lift::Lift;
|
||||
use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
|
||||
@ -338,3 +340,516 @@ impl<I: Interner> TypeVisitor<I> for ValidateBoundVars<I> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to [`super::Binder`] except that it tracks early bound generics, i.e. `struct Foo<T>(T)`
|
||||
/// needs `T` instantiated immediately. This type primarily exists to avoid forgetting to call
|
||||
/// `instantiate`.
|
||||
///
|
||||
/// If you don't have anything to `instantiate`, you may be looking for
|
||||
/// [`instantiate_identity`](EarlyBinder::instantiate_identity) or [`skip_binder`](EarlyBinder::skip_binder).
|
||||
#[derive(derivative::Derivative)]
|
||||
#[derivative(
|
||||
Clone(bound = "T: Clone"),
|
||||
Copy(bound = "T: Copy"),
|
||||
PartialEq(bound = "T: PartialEq"),
|
||||
Eq(bound = "T: Eq"),
|
||||
Ord(bound = "T: Ord"),
|
||||
PartialOrd(bound = "T: Ord"),
|
||||
Hash(bound = "T: Hash"),
|
||||
Debug(bound = "T: Debug")
|
||||
)]
|
||||
#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
|
||||
pub struct EarlyBinder<I: Interner, T> {
|
||||
value: T,
|
||||
_tcx: PhantomData<I>,
|
||||
}
|
||||
|
||||
/// For early binders, you should first call `instantiate` before using any visitors.
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<I: Interner, T> !TypeFoldable<I> for ty::EarlyBinder<I, T> {}
|
||||
|
||||
/// For early binders, you should first call `instantiate` before using any visitors.
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<I: Interner, T> !TypeVisitable<I> for ty::EarlyBinder<I, T> {}
|
||||
|
||||
impl<I: Interner, T> EarlyBinder<I, T> {
|
||||
pub fn bind(value: T) -> EarlyBinder<I, T> {
|
||||
EarlyBinder { value, _tcx: PhantomData }
|
||||
}
|
||||
|
||||
pub fn as_ref(&self) -> EarlyBinder<I, &T> {
|
||||
EarlyBinder { value: &self.value, _tcx: PhantomData }
|
||||
}
|
||||
|
||||
pub fn map_bound_ref<F, U>(&self, f: F) -> EarlyBinder<I, U>
|
||||
where
|
||||
F: FnOnce(&T) -> U,
|
||||
{
|
||||
self.as_ref().map_bound(f)
|
||||
}
|
||||
|
||||
pub fn map_bound<F, U>(self, f: F) -> EarlyBinder<I, U>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
let value = f(self.value);
|
||||
EarlyBinder { value, _tcx: PhantomData }
|
||||
}
|
||||
|
||||
pub fn try_map_bound<F, U, E>(self, f: F) -> Result<EarlyBinder<I, U>, E>
|
||||
where
|
||||
F: FnOnce(T) -> Result<U, E>,
|
||||
{
|
||||
let value = f(self.value)?;
|
||||
Ok(EarlyBinder { value, _tcx: PhantomData })
|
||||
}
|
||||
|
||||
pub fn rebind<U>(&self, value: U) -> EarlyBinder<I, U> {
|
||||
EarlyBinder { value, _tcx: PhantomData }
|
||||
}
|
||||
|
||||
/// Skips the binder and returns the "bound" value.
|
||||
/// This can be used to extract data that does not depend on generic parameters
|
||||
/// (e.g., getting the `DefId` of the inner value or getting the number of
|
||||
/// arguments of an `FnSig`). Otherwise, consider using
|
||||
/// [`instantiate_identity`](EarlyBinder::instantiate_identity).
|
||||
///
|
||||
/// To skip the binder on `x: &EarlyBinder<I, T>` to obtain `&T`, leverage
|
||||
/// [`EarlyBinder::as_ref`](EarlyBinder::as_ref): `x.as_ref().skip_binder()`.
|
||||
///
|
||||
/// See also [`Binder::skip_binder`](super::Binder::skip_binder), which is
|
||||
/// the analogous operation on [`super::Binder`].
|
||||
pub fn skip_binder(self) -> T {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner, T> EarlyBinder<I, Option<T>> {
|
||||
pub fn transpose(self) -> Option<EarlyBinder<I, T>> {
|
||||
self.value.map(|value| EarlyBinder { value, _tcx: PhantomData })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, I: Interner, Iter: IntoIterator> EarlyBinder<I, Iter>
|
||||
where
|
||||
Iter::Item: TypeFoldable<I>,
|
||||
{
|
||||
pub fn iter_instantiated(
|
||||
self,
|
||||
tcx: I,
|
||||
args: &'s [I::GenericArg],
|
||||
) -> IterInstantiated<'s, I, Iter> {
|
||||
IterInstantiated { it: self.value.into_iter(), tcx, args }
|
||||
}
|
||||
|
||||
/// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity),
|
||||
/// but on an iterator of `TypeFoldable` values.
|
||||
pub fn instantiate_identity_iter(self) -> Iter::IntoIter {
|
||||
self.value.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IterInstantiated<'s, I: Interner, Iter: IntoIterator> {
|
||||
it: Iter::IntoIter,
|
||||
tcx: I,
|
||||
args: &'s [I::GenericArg],
|
||||
}
|
||||
|
||||
impl<I: Interner, Iter: IntoIterator> Iterator for IterInstantiated<'_, I, Iter>
|
||||
where
|
||||
Iter::Item: TypeFoldable<I>,
|
||||
{
|
||||
type Item = Iter::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(
|
||||
EarlyBinder { value: self.it.next()?, _tcx: PhantomData }
|
||||
.instantiate(self.tcx, self.args),
|
||||
)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.it.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner, Iter: IntoIterator> DoubleEndedIterator for IterInstantiated<'_, I, Iter>
|
||||
where
|
||||
Iter::IntoIter: DoubleEndedIterator,
|
||||
Iter::Item: TypeFoldable<I>,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
Some(
|
||||
EarlyBinder { value: self.it.next_back()?, _tcx: PhantomData }
|
||||
.instantiate(self.tcx, self.args),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner, Iter: IntoIterator> ExactSizeIterator for IterInstantiated<'_, I, Iter>
|
||||
where
|
||||
Iter::IntoIter: ExactSizeIterator,
|
||||
Iter::Item: TypeFoldable<I>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<'s, I: Interner, Iter: IntoIterator> EarlyBinder<I, Iter>
|
||||
where
|
||||
Iter::Item: Deref,
|
||||
<Iter::Item as Deref>::Target: Copy + TypeFoldable<I>,
|
||||
{
|
||||
pub fn iter_instantiated_copied(
|
||||
self,
|
||||
tcx: I,
|
||||
args: &'s [I::GenericArg],
|
||||
) -> IterInstantiatedCopied<'s, I, Iter> {
|
||||
IterInstantiatedCopied { it: self.value.into_iter(), tcx, args }
|
||||
}
|
||||
|
||||
/// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity),
|
||||
/// but on an iterator of values that deref to a `TypeFoldable`.
|
||||
pub fn instantiate_identity_iter_copied(
|
||||
self,
|
||||
) -> impl Iterator<Item = <Iter::Item as Deref>::Target> {
|
||||
self.value.into_iter().map(|v| *v)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IterInstantiatedCopied<'a, I: Interner, Iter: IntoIterator> {
|
||||
it: Iter::IntoIter,
|
||||
tcx: I,
|
||||
args: &'a [I::GenericArg],
|
||||
}
|
||||
|
||||
impl<I: Interner, Iter: IntoIterator> Iterator for IterInstantiatedCopied<'_, I, Iter>
|
||||
where
|
||||
Iter::Item: Deref,
|
||||
<Iter::Item as Deref>::Target: Copy + TypeFoldable<I>,
|
||||
{
|
||||
type Item = <Iter::Item as Deref>::Target;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.it.next().map(|value| {
|
||||
EarlyBinder { value: *value, _tcx: PhantomData }.instantiate(self.tcx, self.args)
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.it.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner, Iter: IntoIterator> DoubleEndedIterator for IterInstantiatedCopied<'_, I, Iter>
|
||||
where
|
||||
Iter::IntoIter: DoubleEndedIterator,
|
||||
Iter::Item: Deref,
|
||||
<Iter::Item as Deref>::Target: Copy + TypeFoldable<I>,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.it.next_back().map(|value| {
|
||||
EarlyBinder { value: *value, _tcx: PhantomData }.instantiate(self.tcx, self.args)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner, Iter: IntoIterator> ExactSizeIterator for IterInstantiatedCopied<'_, I, Iter>
|
||||
where
|
||||
Iter::IntoIter: ExactSizeIterator,
|
||||
Iter::Item: Deref,
|
||||
<Iter::Item as Deref>::Target: Copy + TypeFoldable<I>,
|
||||
{
|
||||
}
|
||||
|
||||
pub struct EarlyBinderIter<I, T> {
|
||||
t: T,
|
||||
_tcx: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<I: Interner, T: IntoIterator> EarlyBinder<I, T> {
|
||||
pub fn transpose_iter(self) -> EarlyBinderIter<I, T::IntoIter> {
|
||||
EarlyBinderIter { t: self.value.into_iter(), _tcx: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner, T: Iterator> Iterator for EarlyBinderIter<I, T> {
|
||||
type Item = EarlyBinder<I, T::Item>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.t.next().map(|value| EarlyBinder { value, _tcx: PhantomData })
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.t.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner, T: TypeFoldable<I>> ty::EarlyBinder<I, T> {
|
||||
pub fn instantiate(self, tcx: I, args: &[I::GenericArg]) -> T {
|
||||
let mut folder = ArgFolder { tcx, args, binders_passed: 0 };
|
||||
self.value.fold_with(&mut folder)
|
||||
}
|
||||
|
||||
/// Makes the identity replacement `T0 => T0, ..., TN => TN`.
|
||||
/// Conceptually, this converts universally bound variables into placeholders
|
||||
/// when inside of a given item.
|
||||
///
|
||||
/// For example, consider `for<T> fn foo<T>(){ .. }`:
|
||||
/// - Outside of `foo`, `T` is bound (represented by the presence of `EarlyBinder`).
|
||||
/// - Inside of the body of `foo`, we treat `T` as a placeholder by calling
|
||||
/// `instantiate_identity` to discharge the `EarlyBinder`.
|
||||
pub fn instantiate_identity(self) -> T {
|
||||
self.value
|
||||
}
|
||||
|
||||
/// Returns the inner value, but only if it contains no bound vars.
|
||||
pub fn no_bound_vars(self) -> Option<T> {
|
||||
if !self.value.has_param() { Some(self.value) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// The actual instantiation engine itself is a type folder.
|
||||
|
||||
struct ArgFolder<'a, I: Interner> {
|
||||
tcx: I,
|
||||
args: &'a [I::GenericArg],
|
||||
|
||||
/// Number of region binders we have passed through while doing the instantiation
|
||||
binders_passed: u32,
|
||||
}
|
||||
|
||||
impl<'a, I: Interner> TypeFolder<I> for ArgFolder<'a, I> {
|
||||
#[inline]
|
||||
fn interner(&self) -> I {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_binder<T: TypeFoldable<I>>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T> {
|
||||
self.binders_passed += 1;
|
||||
let t = t.super_fold_with(self);
|
||||
self.binders_passed -= 1;
|
||||
t
|
||||
}
|
||||
|
||||
fn fold_region(&mut self, r: I::Region) -> I::Region {
|
||||
// Note: This routine only handles regions that are bound on
|
||||
// type declarations and other outer declarations, not those
|
||||
// bound in *fn types*. Region instantiation of the bound
|
||||
// regions that appear in a function signature is done using
|
||||
// the specialized routine `ty::replace_late_regions()`.
|
||||
match r.kind() {
|
||||
ty::ReEarlyParam(data) => {
|
||||
let rk = self.args.get(data.index() as usize).map(|k| k.kind());
|
||||
match rk {
|
||||
Some(ty::GenericArgKind::Lifetime(lt)) => self.shift_region_through_binders(lt),
|
||||
Some(other) => self.region_param_expected(data, r, other),
|
||||
None => self.region_param_out_of_range(data, r),
|
||||
}
|
||||
}
|
||||
ty::ReBound(..)
|
||||
| ty::ReLateParam(_)
|
||||
| ty::ReStatic
|
||||
| ty::RePlaceholder(_)
|
||||
| ty::ReErased
|
||||
| ty::ReError(_) => r,
|
||||
ty::ReVar(_) => panic!("unexpected region: {r:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
|
||||
if !t.has_param() {
|
||||
return t;
|
||||
}
|
||||
|
||||
match t.kind() {
|
||||
ty::Param(p) => self.ty_for_param(p, t),
|
||||
_ => t.super_fold_with(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, c: I::Const) -> I::Const {
|
||||
if let ty::ConstKind::Param(p) = c.kind() {
|
||||
self.const_for_param(p, c)
|
||||
} else {
|
||||
c.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I: Interner> ArgFolder<'a, I> {
|
||||
fn ty_for_param(&self, p: I::ParamTy, source_ty: I::Ty) -> I::Ty {
|
||||
// Look up the type in the args. It really should be in there.
|
||||
let opt_ty = self.args.get(p.index() as usize).map(|k| k.kind());
|
||||
let ty = match opt_ty {
|
||||
Some(ty::GenericArgKind::Type(ty)) => ty,
|
||||
Some(kind) => self.type_param_expected(p, source_ty, kind),
|
||||
None => self.type_param_out_of_range(p, source_ty),
|
||||
};
|
||||
|
||||
self.shift_vars_through_binders(ty)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn type_param_expected(&self, p: I::ParamTy, ty: I::Ty, kind: ty::GenericArgKind<I>) -> ! {
|
||||
panic!(
|
||||
"expected type for `{:?}` ({:?}/{}) but found {:?} when instantiating, args={:?}",
|
||||
p,
|
||||
ty,
|
||||
p.index(),
|
||||
kind,
|
||||
self.args,
|
||||
)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn type_param_out_of_range(&self, p: I::ParamTy, ty: I::Ty) -> ! {
|
||||
panic!(
|
||||
"type parameter `{:?}` ({:?}/{}) out of range when instantiating, args={:?}",
|
||||
p,
|
||||
ty,
|
||||
p.index(),
|
||||
self.args,
|
||||
)
|
||||
}
|
||||
|
||||
fn const_for_param(&self, p: I::ParamConst, source_ct: I::Const) -> I::Const {
|
||||
// Look up the const in the args. It really should be in there.
|
||||
let opt_ct = self.args.get(p.index() as usize).map(|k| k.kind());
|
||||
let ct = match opt_ct {
|
||||
Some(ty::GenericArgKind::Const(ct)) => ct,
|
||||
Some(kind) => self.const_param_expected(p, source_ct, kind),
|
||||
None => self.const_param_out_of_range(p, source_ct),
|
||||
};
|
||||
|
||||
self.shift_vars_through_binders(ct)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn const_param_expected(
|
||||
&self,
|
||||
p: I::ParamConst,
|
||||
ct: I::Const,
|
||||
kind: ty::GenericArgKind<I>,
|
||||
) -> ! {
|
||||
panic!(
|
||||
"expected const for `{:?}` ({:?}/{}) but found {:?} when instantiating args={:?}",
|
||||
p,
|
||||
ct,
|
||||
p.index(),
|
||||
kind,
|
||||
self.args,
|
||||
)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn const_param_out_of_range(&self, p: I::ParamConst, ct: I::Const) -> ! {
|
||||
panic!(
|
||||
"const parameter `{:?}` ({:?}/{}) out of range when instantiating args={:?}",
|
||||
p,
|
||||
ct,
|
||||
p.index(),
|
||||
self.args,
|
||||
)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn region_param_expected(
|
||||
&self,
|
||||
ebr: I::EarlyParamRegion,
|
||||
r: I::Region,
|
||||
kind: ty::GenericArgKind<I>,
|
||||
) -> ! {
|
||||
panic!(
|
||||
"expected region for `{:?}` ({:?}/{}) but found {:?} when instantiating args={:?}",
|
||||
ebr,
|
||||
r,
|
||||
ebr.index(),
|
||||
kind,
|
||||
self.args,
|
||||
)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn region_param_out_of_range(&self, ebr: I::EarlyParamRegion, r: I::Region) -> ! {
|
||||
panic!(
|
||||
"const parameter `{:?}` ({:?}/{}) out of range when instantiating args={:?}",
|
||||
ebr,
|
||||
r,
|
||||
ebr.index(),
|
||||
self.args,
|
||||
)
|
||||
}
|
||||
|
||||
/// It is sometimes necessary to adjust the De Bruijn indices during instantiation. This occurs
|
||||
/// when we are instantating a type with escaping bound vars into a context where we have
|
||||
/// passed through binders. That's quite a mouthful. Let's see an example:
|
||||
///
|
||||
/// ```
|
||||
/// type Func<A> = fn(A);
|
||||
/// type MetaFunc = for<'a> fn(Func<&'a i32>);
|
||||
/// ```
|
||||
///
|
||||
/// The type `MetaFunc`, when fully expanded, will be
|
||||
/// ```ignore (illustrative)
|
||||
/// for<'a> fn(fn(&'a i32))
|
||||
/// // ^~ ^~ ^~~
|
||||
/// // | | |
|
||||
/// // | | DebruijnIndex of 2
|
||||
/// // Binders
|
||||
/// ```
|
||||
/// Here the `'a` lifetime is bound in the outer function, but appears as an argument of the
|
||||
/// inner one. Therefore, that appearance will have a DebruijnIndex of 2, because we must skip
|
||||
/// over the inner binder (remember that we count De Bruijn indices from 1). However, in the
|
||||
/// definition of `MetaFunc`, the binder is not visible, so the type `&'a i32` will have a
|
||||
/// De Bruijn index of 1. It's only during the instantiation that we can see we must increase the
|
||||
/// depth by 1 to account for the binder that we passed through.
|
||||
///
|
||||
/// As a second example, consider this twist:
|
||||
///
|
||||
/// ```
|
||||
/// type FuncTuple<A> = (A,fn(A));
|
||||
/// type MetaFuncTuple = for<'a> fn(FuncTuple<&'a i32>);
|
||||
/// ```
|
||||
///
|
||||
/// Here the final type will be:
|
||||
/// ```ignore (illustrative)
|
||||
/// for<'a> fn((&'a i32, fn(&'a i32)))
|
||||
/// // ^~~ ^~~
|
||||
/// // | |
|
||||
/// // DebruijnIndex of 1 |
|
||||
/// // DebruijnIndex of 2
|
||||
/// ```
|
||||
/// As indicated in the diagram, here the same type `&'a i32` is instantiated once, but in the
|
||||
/// first case we do not increase the De Bruijn index and in the second case we do. The reason
|
||||
/// is that only in the second case have we passed through a fn binder.
|
||||
fn shift_vars_through_binders<T: TypeFoldable<I>>(&self, val: T) -> T {
|
||||
debug!(
|
||||
"shift_vars(val={:?}, binders_passed={:?}, has_escaping_bound_vars={:?})",
|
||||
val,
|
||||
self.binders_passed,
|
||||
val.has_escaping_bound_vars()
|
||||
);
|
||||
|
||||
if self.binders_passed == 0 || !val.has_escaping_bound_vars() {
|
||||
return val;
|
||||
}
|
||||
|
||||
let result = ty::fold::shift_vars(TypeFolder::interner(self), val, self.binders_passed);
|
||||
debug!("shift_vars: shifted result = {:?}", result);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn shift_region_through_binders(&self, region: I::Region) -> I::Region {
|
||||
if self.binders_passed == 0 || !region.has_escaping_bound_vars() {
|
||||
return region;
|
||||
}
|
||||
ty::fold::shift_region(self.tcx, region, self.binders_passed)
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +47,10 @@
|
||||
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use std::mem;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::visit::TypeVisitable;
|
||||
use crate::inherent::*;
|
||||
use crate::visit::{TypeVisitable, TypeVisitableExt as _};
|
||||
use crate::{self as ty, Interner, Lrc};
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
@ -325,3 +327,95 @@ impl<I: Interner, T: TypeFoldable<I>, Ix: Idx> TypeFoldable<I> for IndexVec<Ix,
|
||||
self.raw.try_fold_with(folder).map(IndexVec::from_raw)
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Shifter
|
||||
//
|
||||
// Shifts the De Bruijn indices on all escaping bound vars by a
|
||||
// fixed amount. Useful in instantiation or when otherwise introducing
|
||||
// a binding level that is not intended to capture the existing bound
|
||||
// vars. See comment on `shift_vars_through_binders` method in
|
||||
// `rustc_middle/src/ty/generic_args.rs` for more details.
|
||||
|
||||
struct Shifter<I: Interner> {
|
||||
tcx: I,
|
||||
current_index: ty::DebruijnIndex,
|
||||
amount: u32,
|
||||
}
|
||||
|
||||
impl<I: Interner> Shifter<I> {
|
||||
pub fn new(tcx: I, amount: u32) -> Self {
|
||||
Shifter { tcx, current_index: ty::INNERMOST, amount }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner> TypeFolder<I> for Shifter<I> {
|
||||
fn interner(&self) -> I {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_binder<T: TypeFoldable<I>>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T> {
|
||||
self.current_index.shift_in(1);
|
||||
let t = t.super_fold_with(self);
|
||||
self.current_index.shift_out(1);
|
||||
t
|
||||
}
|
||||
|
||||
fn fold_region(&mut self, r: I::Region) -> I::Region {
|
||||
match r.kind() {
|
||||
ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
|
||||
let debruijn = debruijn.shifted_in(self.amount);
|
||||
Region::new_bound(self.tcx, debruijn, br)
|
||||
}
|
||||
_ => r,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
|
||||
match ty.kind() {
|
||||
ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
|
||||
let debruijn = debruijn.shifted_in(self.amount);
|
||||
Ty::new_bound(self.tcx, debruijn, bound_ty)
|
||||
}
|
||||
|
||||
_ if ty.has_vars_bound_at_or_above(self.current_index) => ty.super_fold_with(self),
|
||||
_ => ty,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, ct: I::Const) -> I::Const {
|
||||
match ct.kind() {
|
||||
ty::ConstKind::Bound(debruijn, bound_ct) if debruijn >= self.current_index => {
|
||||
let debruijn = debruijn.shifted_in(self.amount);
|
||||
Const::new_bound(self.tcx, debruijn, bound_ct, ct.ty())
|
||||
}
|
||||
_ => ct.super_fold_with(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
|
||||
if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shift_region<I: Interner>(tcx: I, region: I::Region, amount: u32) -> I::Region {
|
||||
match region.kind() {
|
||||
ty::ReBound(debruijn, br) if amount > 0 => {
|
||||
Region::new_bound(tcx, debruijn.shifted_in(amount), br)
|
||||
}
|
||||
_ => region,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shift_vars<I: Interner, T>(tcx: I, value: T, amount: u32) -> T
|
||||
where
|
||||
T: TypeFoldable<I>,
|
||||
{
|
||||
debug!("shift_vars(value={:?}, amount={})", value, amount);
|
||||
|
||||
if amount == 0 || !value.has_escaping_bound_vars() {
|
||||
return value;
|
||||
}
|
||||
|
||||
value.fold_with(&mut Shifter::new(tcx, amount))
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ pub trait Ty<I: Interner<Ty = Self>>:
|
||||
|
||||
fn new_var(interner: I, var: ty::TyVid) -> Self;
|
||||
|
||||
fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundTy) -> Self;
|
||||
|
||||
fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
|
||||
|
||||
fn new_alias(interner: I, kind: ty::AliasTyKind, alias_ty: ty::AliasTy<I>) -> Self;
|
||||
@ -65,7 +67,10 @@ pub trait Region<I: Interner<Region = Self>>:
|
||||
+ Into<I::GenericArg>
|
||||
+ IntoKind<Kind = ty::RegionKind<I>>
|
||||
+ Flags
|
||||
+ TypeVisitable<I>
|
||||
{
|
||||
fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundRegion) -> Self;
|
||||
|
||||
fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
|
||||
|
||||
fn new_static(interner: I) -> Self;
|
||||
@ -87,6 +92,8 @@ pub trait Const<I: Interner<Const = Self>>:
|
||||
|
||||
fn new_var(interner: I, var: ty::ConstVid, ty: I::Ty) -> Self;
|
||||
|
||||
fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundConst, ty: I::Ty) -> Self;
|
||||
|
||||
fn new_anon_bound(
|
||||
interner: I,
|
||||
debruijn: ty::DebruijnIndex,
|
||||
@ -162,3 +169,7 @@ pub trait BoundVarLike<I: Interner> {
|
||||
|
||||
fn assert_eq(self, var: I::BoundVarKind);
|
||||
}
|
||||
|
||||
pub trait ParamLike {
|
||||
fn index(self) -> u32;
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ pub trait Interner:
|
||||
type Ty: Ty<Self>;
|
||||
type Tys: Tys<Self>;
|
||||
type FnInputTys: Copy + Debug + Hash + Eq + Deref<Target = [Self::Ty]> + TypeVisitable<Self>;
|
||||
type ParamTy: Copy + Debug + Hash + Eq;
|
||||
type ParamTy: Copy + Debug + Hash + Eq + ParamLike;
|
||||
type BoundTy: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
|
||||
type PlaceholderTy: PlaceholderLike;
|
||||
|
||||
@ -81,14 +81,14 @@ pub trait Interner:
|
||||
// Kinds of consts
|
||||
type Const: Const<Self>;
|
||||
type PlaceholderConst: PlaceholderLike;
|
||||
type ParamConst: Copy + Debug + Hash + Eq;
|
||||
type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
|
||||
type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
|
||||
type ValueConst: Copy + Debug + Hash + Eq;
|
||||
type ExprConst: Copy + DebugWithInfcx<Self> + Hash + Eq;
|
||||
|
||||
// Kinds of regions
|
||||
type Region: Region<Self>;
|
||||
type EarlyParamRegion: Copy + Debug + Hash + Eq;
|
||||
type EarlyParamRegion: Copy + Debug + Hash + Eq + ParamLike;
|
||||
type LateParamRegion: Copy + Debug + Hash + Eq;
|
||||
type BoundRegion: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
|
||||
type PlaceholderRegion: PlaceholderLike;
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![cfg_attr(
|
||||
feature = "nightly",
|
||||
feature(associated_type_defaults, min_specialization, never_type, rustc_attrs)
|
||||
feature(associated_type_defaults, min_specialization, never_type, rustc_attrs, negative_impls)
|
||||
)]
|
||||
#![allow(rustc::usage_of_ty_tykind)]
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
|
Loading…
Reference in New Issue
Block a user