1
Fork 0

Track bound vars

This commit is contained in:
Jack Huey 2020-10-05 20:41:46 -04:00
parent 62a49c3bb8
commit 30187c81f6
33 changed files with 478 additions and 362 deletions

View file

@ -124,6 +124,7 @@ where
{
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
self.as_ref().skip_binder().hash_stable(hcx, hasher);
self.bound_vars().hash_stable(hcx, hasher);
}
}

View file

@ -122,6 +122,7 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for Ty<'tcx> {
impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for ty::Binder<'tcx, ty::PredicateKind<'tcx>> {
fn encode(&self, e: &mut E) -> Result<(), E::Error> {
self.bound_vars().encode(e)?;
encode_with_shorthand(e, &self.skip_binder(), TyEncoder::predicate_shorthands)
}
}
@ -228,16 +229,20 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for Ty<'tcx> {
impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for ty::Binder<'tcx, ty::PredicateKind<'tcx>> {
fn decode(decoder: &mut D) -> Result<ty::Binder<'tcx, ty::PredicateKind<'tcx>>, D::Error> {
let bound_vars = Decodable::decode(decoder)?;
// Handle shorthands first, if we have an usize > 0x80.
Ok(ty::Binder::bind(if decoder.positioned_at_shorthand() {
let pos = decoder.read_usize()?;
assert!(pos >= SHORTHAND_OFFSET);
let shorthand = pos - SHORTHAND_OFFSET;
Ok(ty::Binder::bind_with_vars(
if decoder.positioned_at_shorthand() {
let pos = decoder.read_usize()?;
assert!(pos >= SHORTHAND_OFFSET);
let shorthand = pos - SHORTHAND_OFFSET;
decoder.with_position(shorthand, ty::PredicateKind::decode)?
} else {
ty::PredicateKind::decode(decoder)?
}))
decoder.with_position(shorthand, ty::PredicateKind::decode)?
} else {
ty::PredicateKind::decode(decoder)?
},
bound_vars,
))
}
}
@ -379,6 +384,13 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::N
}
}
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List<ty::BoundVariableKind> {
fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
let len = decoder.read_usize()?;
Ok(decoder.tcx().mk_bound_variable_kinds((0..len).map(|_| Decodable::decode(decoder)))?)
}
}
impl_decodable_via_ref! {
&'tcx ty::TypeckResults<'tcx>,
&'tcx ty::List<Ty<'tcx>>,
@ -387,7 +399,8 @@ impl_decodable_via_ref! {
&'tcx mir::Body<'tcx>,
&'tcx mir::UnsafetyCheckResult,
&'tcx mir::BorrowCheckResult<'tcx>,
&'tcx mir::coverage::CodeRegion
&'tcx mir::coverage::CodeRegion,
&'tcx ty::List<ty::BoundVariableKind>
}
#[macro_export]
@ -490,12 +503,14 @@ macro_rules! impl_binder_encode_decode {
$(
impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for ty::Binder<'tcx, $t> {
fn encode(&self, e: &mut E) -> Result<(), E::Error> {
self.bound_vars().encode(e)?;
self.as_ref().skip_binder().encode(e)
}
}
impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for ty::Binder<'tcx, $t> {
fn decode(decoder: &mut D) -> Result<Self, D::Error> {
Ok(ty::Binder::bind(Decodable::decode(decoder)?))
let bound_vars = Decodable::decode(decoder)?;
Ok(ty::Binder::bind_with_vars(Decodable::decode(decoder)?, bound_vars))
}
}
)*

View file

@ -96,6 +96,7 @@ pub struct CtxtInterners<'tcx> {
const_: InternedSet<'tcx, Const<'tcx>>,
/// Const allocations.
allocation: InternedSet<'tcx, Allocation>,
bound_variable_kinds: InternedSet<'tcx, List<ty::BoundVariableKind>>,
}
impl<'tcx> CtxtInterners<'tcx> {
@ -114,6 +115,7 @@ impl<'tcx> CtxtInterners<'tcx> {
place_elems: Default::default(),
const_: Default::default(),
allocation: Default::default(),
bound_variable_kinds: Default::default(),
}
}
@ -1624,6 +1626,7 @@ nop_list_lift! {poly_existential_predicates; ty::Binder<'a, ExistentialPredicate
nop_list_lift! {predicates; Predicate<'a> => Predicate<'tcx>}
nop_list_lift! {canonical_var_infos; CanonicalVarInfo<'a> => CanonicalVarInfo<'tcx>}
nop_list_lift! {projs; ProjectionKind => ProjectionKind}
nop_list_lift! {bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariableKind}
// This is the impl for `&'a InternalSubsts<'a>`.
nop_list_lift! {substs; GenericArg<'a> => GenericArg<'tcx>}
@ -2080,6 +2083,7 @@ slice_interners!(
predicates: _intern_predicates(Predicate<'tcx>),
projs: _intern_projs(ProjectionKind),
place_elems: _intern_place_elems(PlaceElem<'tcx>),
bound_variable_kinds: _intern_bound_variable_kinds(ty::BoundVariableKind),
);
impl<'tcx> TyCtxt<'tcx> {
@ -2516,6 +2520,13 @@ impl<'tcx> TyCtxt<'tcx> {
if ts.is_empty() { List::empty() } else { self._intern_canonical_var_infos(ts) }
}
pub fn intern_bound_variable_kinds(
self,
ts: &[ty::BoundVariableKind],
) -> &'tcx List<ty::BoundVariableKind> {
if ts.is_empty() { List::empty() } else { self._intern_bound_variable_kinds(ts) }
}
pub fn mk_fn_sig<I>(
self,
inputs: I,
@ -2576,6 +2587,15 @@ impl<'tcx> TyCtxt<'tcx> {
self.mk_substs(iter::once(self_ty.into()).chain(rest.iter().cloned()))
}
pub fn mk_bound_variable_kinds<
I: InternAs<[ty::BoundVariableKind], &'tcx List<ty::BoundVariableKind>>,
>(
self,
iter: I,
) -> I::Output {
iter.intern_with(|xs| self.intern_bound_variable_kinds(xs))
}
/// Walks upwards from `id` to find a node which might change lint levels with attributes.
/// It stops at `bound` and just returns it if reached.
pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId {

View file

@ -35,6 +35,7 @@ use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sso::SsoHashSet;
use std::collections::BTreeMap;
use std::fmt;
use std::ops::ControlFlow;
@ -702,10 +703,109 @@ impl<'tcx> TyCtxt<'tcx> {
r
})
.0,
self,
)
}
}
pub struct BoundVarsCollector<'tcx> {
binder_index: ty::DebruijnIndex,
vars: BTreeMap<u32, ty::BoundVariableKind>,
// We may encounter the same variable at different levels of binding, so
// this can't just be `Ty`
visited: SsoHashSet<(ty::DebruijnIndex, Ty<'tcx>)>,
}
impl<'tcx> BoundVarsCollector<'tcx> {
pub fn new() -> Self {
BoundVarsCollector {
binder_index: ty::INNERMOST,
vars: BTreeMap::new(),
visited: SsoHashSet::default(),
}
}
pub fn into_vars(self, tcx: TyCtxt<'tcx>) -> &'tcx ty::List<ty::BoundVariableKind> {
let max = self.vars.iter().map(|(k, _)| *k).max().unwrap_or_else(|| 0);
for i in 0..max {
if let None = self.vars.get(&i) {
panic!("Unknown variable: {:?}", i);
}
}
tcx.mk_bound_variable_kinds(self.vars.into_iter().map(|(_, v)| v))
}
}
impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> {
type BreakTy = ();
fn visit_binder<T: TypeFoldable<'tcx>>(
&mut self,
t: &Binder<'tcx, T>,
) -> ControlFlow<Self::BreakTy> {
self.binder_index.shift_in(1);
let result = t.super_visit_with(self);
self.binder_index.shift_out(1);
result
}
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if t.outer_exclusive_binder < self.binder_index
|| !self.visited.insert((self.binder_index, t))
{
return ControlFlow::CONTINUE;
}
use std::collections::btree_map::Entry;
match *t.kind() {
ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => {
match self.vars.entry(bound_ty.var.as_u32()) {
Entry::Vacant(entry) => {
entry.insert(ty::BoundVariableKind::Ty(bound_ty.kind));
}
Entry::Occupied(entry) => match entry.get() {
ty::BoundVariableKind::Ty(_) => {}
_ => bug!("Conflicting bound vars"),
},
}
}
_ => (),
};
t.super_visit_with(self)
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
use std::collections::btree_map::Entry;
match r {
ty::ReLateBound(index, br) if *index == self.binder_index => match br.kind {
ty::BrNamed(_def_id, _name) => {
// FIXME
}
ty::BrAnon(var) => match self.vars.entry(var) {
Entry::Vacant(entry) => {
entry.insert(ty::BoundVariableKind::Region(br.kind));
}
Entry::Occupied(entry) => match entry.get() {
ty::BoundVariableKind::Region(_) => {}
_ => bug!("Conflicting bound vars"),
},
},
ty::BrEnv => {
// FIXME
}
},
_ => (),
};
r.super_visit_with(self)
}
}
///////////////////////////////////////////////////////////////////////////
// Shifter
//
@ -907,57 +1007,6 @@ impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor {
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
struct FoundFlags;
crate struct CountBoundVars {
crate outer_index: ty::DebruijnIndex,
crate bound_tys: FxHashSet<ty::BoundTy>,
crate bound_regions: FxHashSet<ty::BoundRegion>,
crate bound_consts: FxHashSet<ty::BoundVar>,
}
impl<'tcx> TypeVisitor<'tcx> for CountBoundVars {
type BreakTy = ();
fn visit_binder<T: TypeFoldable<'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
}
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
match t.kind {
ty::Bound(debruijn, ty) if debruijn == self.outer_index => {
self.bound_tys.insert(ty);
ControlFlow::CONTINUE
}
_ => t.super_visit_with(self),
}
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
match r {
ty::ReLateBound(debruijn, re) if *debruijn == self.outer_index => {
self.bound_regions.insert(*re);
ControlFlow::CONTINUE
}
_ => r.super_visit_with(self),
}
}
fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
match ct.val {
ty::ConstKind::Bound(debruijn, c) if debruijn == self.outer_index => {
self.bound_consts.insert(c);
ControlFlow::CONTINUE
}
_ => ct.super_visit_with(self),
}
}
}
// FIXME: Optimize for checking for infer flags
struct HasTypeFlagsVisitor {
flags: ty::TypeFlags,

View file

@ -67,12 +67,12 @@ pub use self::sty::BoundRegionKind::*;
pub use self::sty::RegionKind::*;
pub use self::sty::TyKind::*;
pub use self::sty::{
Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, CanonicalPolyFnSig,
ClosureSubsts, ClosureSubstsParts, ConstVid, EarlyBoundRegion, ExistentialPredicate,
ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig, GeneratorSubsts,
GeneratorSubstsParts, ParamConst, ParamTy, PolyExistentialProjection, PolyExistentialTraitRef,
PolyFnSig, PolyGenSig, PolyTraitRef, ProjectionTy, Region, RegionKind, RegionVid, TraitRef,
TyKind, TypeAndMut, UpvarSubsts,
Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, BoundVariableKind,
CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid, EarlyBoundRegion,
ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig,
GeneratorSubsts, GeneratorSubstsParts, ParamConst, ParamTy, PolyExistentialProjection,
PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, ProjectionTy, Region, RegionKind,
RegionVid, TraitRef, TyKind, TypeAndMut, UpvarSubsts,
};
pub use self::trait_def::TraitDef;
@ -546,7 +546,7 @@ impl<'tcx> Predicate<'tcx> {
let substs = trait_ref.skip_binder().substs;
let pred = self.kind().skip_binder();
let new = pred.subst(tcx, substs);
tcx.reuse_or_mk_predicate(self, ty::Binder::bind(new))
tcx.reuse_or_mk_predicate(self, ty::Binder::bind(new, tcx))
}
}

View file

@ -460,8 +460,10 @@ where
{
type Lifted = ty::Binder<'tcx, T::Lifted>;
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
// FIXME: need to lift inner values
tcx.lift(self.skip_binder()).map(|v| ty::Binder::bind(v))
let bound_vars = tcx.lift(self.bound_vars());
tcx.lift(self.skip_binder())
.zip(bound_vars)
.map(|(value, vars)| ty::Binder::bind_with_vars(value, vars))
}
}

View file

@ -5,6 +5,7 @@
use self::TyKind::*;
use crate::infer::canonical::Canonical;
use crate::ty::fold::BoundVarsCollector;
use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef};
use crate::ty::InferTy::{self, *};
use crate::ty::{
@ -947,6 +948,14 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> {
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
#[derive(HashStable)]
pub enum BoundVariableKind {
Ty(BoundTyKind),
Region(BoundRegionKind),
Const,
}
/// Binder is a binder for higher-ranked lifetimes or types. It is part of the
/// compiler's representation for things like `for<'a> Fn(&'a isize)`
/// (which would be represented by the type `PolyTraitRef ==
@ -957,7 +966,7 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> {
///
/// `Decodable` and `Encodable` are implemented for `Binder<T>` using the `impl_binder_encode_decode!` macro.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Binder<'tcx, T>(T, u32, std::marker::PhantomData<&'tcx ()>);
pub struct Binder<'tcx, T>(T, &'tcx List<BoundVariableKind>);
impl<'tcx, T> Binder<'tcx, T>
where
@ -969,48 +978,22 @@ where
/// different binding level.
pub fn dummy(value: T) -> Binder<'tcx, T> {
debug_assert!(!value.has_escaping_bound_vars());
Binder(value, 0, std::marker::PhantomData)
Binder(value, ty::List::empty())
}
/// Wraps `value` in a binder, binding higher-ranked vars (if any).
pub fn bind(value: T) -> Binder<'tcx, T> {
use crate::ty::fold::CountBoundVars;
use rustc_data_structures::fx::FxHashSet;
let mut counter = CountBoundVars {
outer_index: ty::INNERMOST,
bound_tys: FxHashSet::default(),
bound_regions: FxHashSet::default(),
bound_consts: FxHashSet::default(),
};
value.visit_with(&mut counter);
let bound_tys = counter.bound_tys.len();
let bound_regions = if !counter.bound_regions.is_empty() {
let mut env = false;
let mut anons = FxHashSet::default();
let mut named = FxHashSet::default();
for br in counter.bound_regions {
match br.kind {
ty::BrAnon(idx) => {
anons.insert(idx);
}
ty::BrNamed(def_id, _) => {
named.insert(def_id);
}
ty::BrEnv => env = true,
}
}
(if env { 1 } else { 0 }) + anons.len() + named.len()
} else {
0
};
let bound_consts = counter.bound_consts.len();
let bound_vars = bound_tys + bound_regions + bound_consts;
Binder(value, bound_vars as u32, std::marker::PhantomData)
pub fn bind(value: T, tcx: TyCtxt<'tcx>) -> Binder<'tcx, T> {
let mut collector = BoundVarsCollector::new();
value.visit_with(&mut collector);
Binder(value, collector.into_vars(tcx))
}
}
impl<'tcx, T> Binder<'tcx, T> {
pub fn bind_with_vars(value: T, vars: &'tcx List<BoundVariableKind>) -> Binder<'tcx, T> {
Binder(value, vars)
}
/// Skips the binder and returns the "bound" value. This is a
/// risky thing to do because it's easy to get confused about
/// De Bruijn indices and the like. It is usually better to
@ -1031,12 +1014,12 @@ impl<'tcx, T> Binder<'tcx, T> {
self.0
}
pub fn bound_vars(&self) -> u32 {
pub fn bound_vars(&self) -> &'tcx List<BoundVariableKind> {
self.1
}
pub fn as_ref(&self) -> Binder<'tcx, &T> {
Binder(&self.0, self.1, std::marker::PhantomData)
Binder(&self.0, self.1)
}
pub fn map_bound_ref<F, U>(&self, f: F) -> Binder<'tcx, U>
@ -1050,7 +1033,7 @@ impl<'tcx, T> Binder<'tcx, T> {
where
F: FnOnce(T) -> U,
{
Binder(f(self.0), self.1, std::marker::PhantomData)
Binder(f(self.0), self.1)
}
/// Wraps a `value` in a binder, using the same bound variables as the
@ -1063,7 +1046,7 @@ impl<'tcx, T> Binder<'tcx, T> {
/// because bound vars aren't allowed to change here, whereas they are
/// in `bind`. This may be (debug) asserted in the future.
pub fn rebind<U>(&self, value: U) -> Binder<'tcx, U> {
Binder(value, self.1, std::marker::PhantomData)
Binder(value, self.1)
}
/// Unwraps and returns the value within, but only if it contains
@ -1094,7 +1077,7 @@ impl<'tcx, T> Binder<'tcx, T> {
where
F: FnOnce(T, U) -> R,
{
Binder(f(self.0, u.0), self.1, std::marker::PhantomData)
Binder(f(self.0, u.0), self.1)
}
/// Splits the contents into two things that share the same binder
@ -1108,14 +1091,14 @@ impl<'tcx, T> Binder<'tcx, T> {
F: FnOnce(T) -> (U, V),
{
let (u, v) = f(self.0);
(Binder(u, self.1, std::marker::PhantomData), Binder(v, self.1, std::marker::PhantomData))
(Binder(u, self.1), Binder(v, self.1))
}
}
impl<'tcx, T> Binder<'tcx, Option<T>> {
pub fn transpose(self) -> Option<Binder<'tcx, T>> {
let bound_vars = self.1;
self.0.map(|v| Binder(v, bound_vars, std::marker::PhantomData))
self.0.map(|v| Binder(v, bound_vars))
}
}

View file

@ -510,7 +510,7 @@ impl<'tcx> TyCtxt<'tcx> {
ty::ClosureKind::FnMut => self.mk_mut_ref(self.mk_region(env_region), closure_ty),
ty::ClosureKind::FnOnce => closure_ty,
};
Some(ty::Binder::bind(env_ty))
Some(ty::Binder::bind(env_ty, self))
}
/// Returns `true` if the node pointed to by `def_id` is a `static` item.