Add var to BoundRegion. Add query to get bound vars for applicable items.

This commit is contained in:
Jack Huey
2020-10-26 14:18:31 -04:00
parent 666859a6f8
commit 6d5efa9f04
53 changed files with 1274 additions and 385 deletions

View File

@@ -70,16 +70,16 @@ impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind {
ty::ReEmpty(universe) => {
universe.hash_stable(hcx, hasher);
}
ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrAnon(i) }) => {
ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrAnon(i), .. }) => {
db.hash_stable(hcx, hasher);
i.hash_stable(hcx, hasher);
}
ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrNamed(def_id, name) }) => {
ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrNamed(def_id, name), .. }) => {
db.hash_stable(hcx, hasher);
def_id.hash_stable(hcx, hasher);
name.hash_stable(hcx, hasher);
}
ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrEnv }) => {
ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrEnv, .. }) => {
db.hash_stable(hcx, hasher);
}
ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }) => {

View File

@@ -314,7 +314,8 @@ impl<'tcx> CanonicalVarValues<'tcx> {
tcx.mk_ty(ty::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i).into())).into()
}
GenericArgKind::Lifetime(..) => {
let br = ty::BoundRegion { kind: ty::BrAnon(i) };
let br =
ty::BoundRegion { var: ty::BoundVar::from_u32(i), kind: ty::BrAnon(i) };
tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)).into()
}
GenericArgKind::Const(ct) => tcx

View File

@@ -83,4 +83,6 @@ pub struct ResolveLifetimes {
/// be late-bound if (a) it does NOT appear in a where-clause and
/// (b) it DOES appear in the arguments.
pub late_bound: FxHashMap<LocalDefId, FxHashSet<ItemLocalId>>,
pub late_bound_vars: FxHashMap<LocalDefId, FxHashMap<ItemLocalId, Vec<ty::BoundVariableKind>>>,
}

View File

@@ -1290,6 +1290,10 @@ rustc_queries! {
-> Option<Vec<ObjectLifetimeDefault>> {
desc { "looking up lifetime defaults for a region on an item" }
}
query late_bound_vars_map(_: LocalDefId)
-> Option<&'tcx FxHashMap<ItemLocalId, Vec<ty::BoundVariableKind>>> {
desc { "looking up late bound vars" }
}
query visibility(def_id: DefId) -> ty::Visibility {
eval_always

View File

@@ -810,7 +810,7 @@ impl CanonicalUserType<'tcx> {
ty::ReLateBound(debruijn, br) => {
// We only allow a `ty::INNERMOST` index in substitutions.
assert_eq!(*debruijn, ty::INNERMOST);
cvar == br.assert_bound_var()
cvar == br.var
}
_ => false,
},
@@ -2672,6 +2672,17 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn object_lifetime_defaults(self, id: HirId) -> Option<Vec<ObjectLifetimeDefault>> {
self.object_lifetime_defaults_map(id.owner)
}
pub fn late_bound_vars(self, id: HirId) -> &'tcx List<ty::BoundVariableKind> {
self.mk_bound_variable_kinds(
self.late_bound_vars_map(id.owner)
.and_then(|map| map.get(&id.local_id).cloned())
.unwrap_or_else(|| {
bug!("No bound vars found for {:?} ({:?})", self.hir().node_to_string(id), id)
})
.iter(),
)
}
}
impl TyCtxtAt<'tcx> {

View File

@@ -634,6 +634,42 @@ impl<'tcx> TyCtxt<'tcx> {
.0
}
pub fn shift_bound_var_indices<T>(self, bound_vars: usize, value: T) -> T
where
T: TypeFoldable<'tcx>,
{
self.replace_escaping_bound_vars(
value,
|r| {
self.mk_region(ty::ReLateBound(
ty::INNERMOST,
ty::BoundRegion {
var: ty::BoundVar::from_usize(r.var.as_usize() + bound_vars),
kind: r.kind,
},
))
},
|t| {
self.mk_ty(ty::Bound(
ty::INNERMOST,
ty::BoundTy {
var: ty::BoundVar::from_usize(t.var.as_usize() + bound_vars),
kind: t.kind,
},
))
},
|c, ty| {
self.mk_const(ty::Const {
val: ty::ConstKind::Bound(
ty::INNERMOST,
ty::BoundVar::from_usize(c.as_usize() + bound_vars),
),
ty,
})
},
)
}
/// Returns a set of all late-bound regions that are constrained
/// by `value`, meaning that if we instantiate those LBR with
/// variables and equate `value` with something else, those
@@ -695,16 +731,21 @@ impl<'tcx> TyCtxt<'tcx> {
T: TypeFoldable<'tcx>,
{
let mut counter = 0;
Binder::bind(
self.replace_late_bound_regions(sig, |_| {
let br = ty::BoundRegion { kind: ty::BrAnon(counter) };
let inner = self
.replace_late_bound_regions(sig, |_| {
let br = ty::BoundRegion {
var: ty::BoundVar::from_u32(counter),
kind: ty::BrAnon(counter),
};
let r = self.mk_region(ty::ReLateBound(ty::INNERMOST, br));
counter += 1;
r
})
.0,
self,
)
.0;
let bound_vars = self.mk_bound_variable_kinds(
(0..counter).map(|i| ty::BoundVariableKind::Region(ty::BrAnon(i))),
);
Binder::bind_with_vars(inner, bound_vars)
}
}
@@ -777,27 +818,105 @@ impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> {
}
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::ReLateBound(index, _br) if *index == self.binder_index => {
bug!("{:?} {:?}", index, _br)
}
ty::BrAnon(var) => match self.vars.entry(var) {
Entry::Vacant(entry) => {
entry.insert(ty::BoundVariableKind::Region(br.kind));
_ => (),
};
r.super_visit_with(self)
}
}
pub struct ValidateBoundVars<'tcx> {
bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
binder_index: ty::DebruijnIndex,
// 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> ValidateBoundVars<'tcx> {
pub fn new(bound_vars: &'tcx ty::List<ty::BoundVariableKind>) -> Self {
ValidateBoundVars {
bound_vars,
binder_index: ty::INNERMOST,
visited: SsoHashSet::default(),
}
}
}
impl<'tcx> TypeVisitor<'tcx> for ValidateBoundVars<'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::BREAK;
}
match *t.kind() {
ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => {
if self.bound_vars.len() <= bound_ty.var.as_usize() {
panic!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars);
}
let list_var = self.bound_vars[bound_ty.var.as_usize()];
match list_var {
ty::BoundVariableKind::Ty(kind) => {
if kind != bound_ty.kind {
panic!(
"Mismatched type kinds: {:?} doesn't var in list {:?}",
bound_ty.kind, list_var
);
}
}
Entry::Occupied(entry) => match entry.get() {
ty::BoundVariableKind::Region(_) => {}
_ => bug!("Conflicting bound vars"),
},
},
ty::BrEnv => {
// FIXME
_ => panic!(
"Mismatched bound variable kinds! Expected type, found {:?}",
list_var
),
}
},
}
_ => (),
};
t.super_visit_with(self)
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
match r {
ty::ReLateBound(index, br) if *index == self.binder_index => {
if self.bound_vars.len() <= br.var.as_usize() {
panic!("Not enough bound vars: {:?} not found in {:?}", *br, self.bound_vars);
}
let list_var = self.bound_vars[br.var.as_usize()];
match list_var {
ty::BoundVariableKind::Region(kind) => {
if kind != br.kind {
panic!(
"Mismatched region kinds: {:?} doesn't match var ({:?}) in list ({:?})",
br.kind, list_var, self.bound_vars
);
}
}
_ => panic!(
"Mismatched bound variable kinds! Expected region, found {:?}",
list_var
),
}
}
_ => (),
};

View File

@@ -1,3 +1,4 @@
// ignore-tidy-filelength
use crate::ich::StableHashingContext;
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::mir::{GeneratorLayout, GeneratorSavedLocal};
@@ -2481,21 +2482,42 @@ impl<'tcx> ty::Instance<'tcx> {
ty::Closure(def_id, substs) => {
let sig = substs.as_closure().sig();
let env_ty = tcx.closure_env_ty(def_id, substs).unwrap();
sig.map_bound(|sig| {
let bound_vars = tcx.mk_bound_variable_kinds(
sig.bound_vars()
.iter()
.chain(iter::once(ty::BoundVariableKind::Region(ty::BrEnv))),
);
let br = ty::BoundRegion {
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
kind: ty::BoundRegionKind::BrEnv,
};
let env_region = ty::ReLateBound(ty::INNERMOST, br);
let env_ty = tcx.closure_env_ty(def_id, substs, env_region).unwrap();
let sig = sig.skip_binder();
ty::Binder::bind_with_vars(
tcx.mk_fn_sig(
iter::once(env_ty.skip_binder()).chain(sig.inputs().iter().cloned()),
iter::once(env_ty).chain(sig.inputs().iter().cloned()),
sig.output(),
sig.c_variadic,
sig.unsafety,
sig.abi,
)
})
),
bound_vars,
)
}
ty::Generator(_, substs, _) => {
let sig = substs.as_generator().poly_sig();
let br = ty::BoundRegion { kind: ty::BrEnv };
let bound_vars = tcx.mk_bound_variable_kinds(
sig.bound_vars()
.iter()
.chain(iter::once(ty::BoundVariableKind::Region(ty::BrEnv))),
);
let br = ty::BoundRegion {
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
kind: ty::BoundRegionKind::BrEnv,
};
let env_region = ty::ReLateBound(ty::INNERMOST, br);
let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
@@ -2504,21 +2526,21 @@ impl<'tcx> ty::Instance<'tcx> {
let pin_substs = tcx.intern_substs(&[env_ty.into()]);
let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs);
sig.map_bound(|sig| {
let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
let state_adt_ref = tcx.adt_def(state_did);
let state_substs =
tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
let sig = sig.skip_binder();
let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
let state_adt_ref = tcx.adt_def(state_did);
let state_substs = tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
ty::Binder::bind_with_vars(
tcx.mk_fn_sig(
[env_ty, sig.resume_ty].iter(),
&ret_ty,
false,
hir::Unsafety::Normal,
rustc_target::spec::abi::Abi::Rust,
)
})
),
bound_vars,
)
}
_ => bug!("unexpected type {:?} in Instance::fn_sig", ty),
}

View File

@@ -543,10 +543,33 @@ impl<'tcx> Predicate<'tcx> {
// substitution code expects equal binding levels in the values
// from the substitution and the value being substituted into, and
// this trick achieves that).
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))
// Working through the second example:
// trait_ref: for<'x> T: Foo1<'^0.0>; substs: [T, '^0.0]
// predicate: for<'b> Self: Bar1<'a, '^0.0>; substs: [Self, 'a, '^0.0]
// We want to end up with:
// for<'x, 'b> T: Bar1<'^0.0, '^0.1>
// To do this:
// 1) We must shift all bound vars in predicate by the length
// of trait ref's bound vars. So, we would end up with predicate like
// Self: Bar1<'a, '^0.1>
// 2) We can then apply the trait substs to this, ending up with
// T: Bar1<'^0.0, '^0.1>
// 3) Finally, to create the final bound vars, we concatenate the bound
// vars of the trait ref with those of the predicate:
// ['x, 'b]
let bound_pred = self.kind();
let pred_bound_vars = bound_pred.bound_vars();
let trait_bound_vars = trait_ref.bound_vars();
// 1) Self: Bar1<'a, '^0.0> -> Self: Bar1<'a, '^0.1>
let shifted_pred =
tcx.shift_bound_var_indices(trait_bound_vars.len(), bound_pred.skip_binder());
// 2) Self: Bar1<'a, '^0.1> -> T: Bar1<'^0.0, '^0.1>
let new = shifted_pred.subst(tcx, trait_ref.skip_binder().substs);
// 3) ['x] + ['b] -> ['x, 'b]
let bound_vars =
tcx.mk_bound_variable_kinds(trait_bound_vars.iter().chain(pred_bound_vars));
tcx.reuse_or_mk_predicate(self, ty::Binder::bind_with_vars(new, bound_vars))
}
}

View File

@@ -1636,7 +1636,7 @@ impl<F: fmt::Write> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> {
data.name != kw::Empty && data.name != kw::UnderscoreLifetime
}
ty::ReLateBound(_, ty::BoundRegion { kind: br })
ty::ReLateBound(_, ty::BoundRegion { kind: br, .. })
| ty::ReFree(ty::FreeRegion { bound_region: br, .. })
| ty::RePlaceholder(ty::Placeholder { name: br, .. }) => {
if let ty::BrNamed(_, name) = br {
@@ -1715,7 +1715,7 @@ impl<F: fmt::Write> FmtPrinter<'_, '_, F> {
return Ok(self);
}
}
ty::ReLateBound(_, ty::BoundRegion { kind: br })
ty::ReLateBound(_, ty::BoundRegion { kind: br, .. })
| ty::ReFree(ty::FreeRegion { bound_region: br, .. })
| ty::RePlaceholder(ty::Placeholder { name: br, .. }) => {
if let ty::BrNamed(_, name) = br {
@@ -1821,7 +1821,8 @@ impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
ty::BrNamed(DefId::local(CRATE_DEF_INDEX), name)
}
};
self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind }))
self.tcx
.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { var: br.var, kind }))
});
start_or_continue(&mut self, "", "> ")?;
@@ -1865,7 +1866,7 @@ impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
struct LateBoundRegionNameCollector<'a>(&'a mut FxHashSet<Symbol>);
impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_> {
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
if let ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name) }) = *r {
if let ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) = *r {
self.0.insert(name);
}
r.super_visit_with(self)

View File

@@ -6,6 +6,7 @@ use self::TyKind::*;
use crate::infer::canonical::Canonical;
use crate::ty::fold::BoundVarsCollector;
use crate::ty::fold::ValidateBoundVars;
use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef};
use crate::ty::InferTy::{self, *};
use crate::ty::{
@@ -63,22 +64,10 @@ pub enum BoundRegionKind {
#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, PartialOrd, Ord)]
#[derive(HashStable)]
pub struct BoundRegion {
pub var: BoundVar,
pub kind: BoundRegionKind,
}
impl BoundRegion {
/// When canonicalizing, we replace unbound inference variables and free
/// regions with anonymous late bound regions. This method asserts that
/// we have an anonymous late bound region, which hence may refer to
/// a canonical variable.
pub fn assert_bound_var(&self) -> BoundVar {
match self.kind {
BoundRegionKind::BrAnon(var) => BoundVar::from_u32(var),
_ => bug!("bound region is not anonymous"),
}
}
}
impl BoundRegionKind {
pub fn is_named(&self) -> bool {
match *self {
@@ -987,13 +976,17 @@ where
value.visit_with(&mut collector);
Binder(value, collector.into_vars(tcx))
}
pub fn bind_with_vars(value: T, vars: &'tcx List<BoundVariableKind>) -> Binder<'tcx, T> {
if cfg!(debug_assertions) {
let mut validator = ValidateBoundVars::new(vars);
value.visit_with(&mut validator);
}
Binder(value, vars)
}
}
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
@@ -1022,18 +1015,31 @@ impl<'tcx, T> Binder<'tcx, T> {
Binder(&self.0, self.1)
}
pub fn map_bound_ref<F, U>(&self, f: F) -> Binder<'tcx, U>
pub fn map_bound_ref_unchecked<F, U>(&self, f: F) -> Binder<'tcx, U>
where
F: FnOnce(&T) -> U,
{
let value = f(&self.0);
Binder(value, self.1)
}
pub fn map_bound_ref<F, U: TypeFoldable<'tcx>>(&self, f: F) -> Binder<'tcx, U>
where
F: FnOnce(&T) -> U,
{
self.as_ref().map_bound(f)
}
pub fn map_bound<F, U>(self, f: F) -> Binder<'tcx, U>
pub fn map_bound<F, U: TypeFoldable<'tcx>>(self, f: F) -> Binder<'tcx, U>
where
F: FnOnce(T) -> U,
{
Binder(f(self.0), self.1)
let value = f(self.0);
if cfg!(debug_assertions) {
let mut validator = ValidateBoundVars::new(self.1);
value.visit_with(&mut validator);
}
Binder(value, self.1)
}
/// Wraps a `value` in a binder, using the same bound variables as the
@@ -1045,7 +1051,14 @@ impl<'tcx, T> Binder<'tcx, T> {
/// don't actually track bound vars. However, semantically, it is different
/// 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> {
pub fn rebind<U>(&self, value: U) -> Binder<'tcx, U>
where
U: TypeFoldable<'tcx>,
{
if cfg!(debug_assertions) {
let mut validator = ValidateBoundVars::new(self.bound_vars());
value.visit_with(&mut validator);
}
Binder(value, self.1)
}
@@ -1066,20 +1079,6 @@ impl<'tcx, T> Binder<'tcx, T> {
if self.0.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) }
}
/// Given two things that have the same binder level,
/// and an operation that wraps on their contents, executes the operation
/// and then wraps its result.
///
/// `f` should consider bound regions at depth 1 to be free, and
/// anything it produces with bound regions at depth 1 will be
/// bound in the resulting return value.
pub fn fuse<U, F, R>(self, u: Binder<'tcx, U>, f: F) -> Binder<'tcx, R>
where
F: FnOnce(T, U) -> R,
{
Binder(f(self.0, u.0), self.1)
}
/// Splits the contents into two things that share the same binder
/// level as the original, returning two distinct binders.
///
@@ -1204,7 +1203,7 @@ pub type PolyFnSig<'tcx> = Binder<'tcx, FnSig<'tcx>>;
impl<'tcx> PolyFnSig<'tcx> {
#[inline]
pub fn inputs(&self) -> Binder<'tcx, &'tcx [Ty<'tcx>]> {
self.map_bound_ref(|fn_sig| fn_sig.inputs())
self.map_bound_ref_unchecked(|fn_sig| fn_sig.inputs())
}
#[inline]
pub fn input(&self, index: usize) -> ty::Binder<'tcx, Ty<'tcx>> {

View File

@@ -499,18 +499,18 @@ impl<'tcx> TyCtxt<'tcx> {
self,
closure_def_id: DefId,
closure_substs: SubstsRef<'tcx>,
) -> Option<ty::Binder<'tcx, Ty<'tcx>>> {
env_region: ty::RegionKind,
) -> Option<Ty<'tcx>> {
let closure_ty = self.mk_closure(closure_def_id, closure_substs);
let br = ty::BoundRegion { kind: ty::BrEnv };
let env_region = ty::ReLateBound(ty::INNERMOST, br);
let closure_kind_ty = closure_substs.as_closure().kind_ty();
let closure_kind = closure_kind_ty.to_opt_closure_kind()?;
debug_assert!(!closure_ty.has_escaping_bound_vars());
let env_ty = match closure_kind {
ty::ClosureKind::Fn => self.mk_imm_ref(self.mk_region(env_region), closure_ty),
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, self))
Some(env_ty)
}
/// Returns `true` if the node pointed to by `def_id` is a `static` item.