diff options
| author | jackh726 <jack.huey@umassmed.edu> | 2021-07-09 00:04:47 -0400 |
|---|---|---|
| committer | jackh726 <jack.huey@umassmed.edu> | 2021-07-09 00:04:47 -0400 |
| commit | c63f1fe92b99a9828e9f999355213b0b73175aa0 (patch) | |
| tree | 6d25fb3f0128266ebb1f32d6328a7318897d2092 /compiler/rustc_trait_selection/src | |
| parent | aa65b08b1dbaf4b637847646801ebc8c01d7ecbd (diff) | |
Replace associated item bound vars with placeholders when projecting.
Diffstat (limited to 'compiler/rustc_trait_selection/src')
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/project.rs | 302 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/query/normalize.rs | 63 |
2 files changed, 365 insertions, 0 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 0e685205069..8433b4ecb0a 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -29,6 +29,8 @@ use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; use rustc_span::symbol::sym; +use std::collections::BTreeMap; + pub use rustc_middle::traits::Reveal; pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; @@ -396,6 +398,53 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { normalized_ty } + ty::Projection(data) if !data.trait_ref(self.tcx()).has_escaping_bound_vars() => { + // Okay, so you thought the previous branch was hacky. Well, to + // extend upon this, when the *trait ref* doesn't have escaping + // bound vars, but the associated item *does* (can only occur + // with GATs), then we might still be able to project the type. + // For this, we temporarily replace the bound vars with + // placeholders. Note though, that in the case that we still + // can't project for whatever reason (e.g. self type isn't + // known enough), we *can't* register an obligation and return + // an inference variable (since then that obligation would have + // bound vars and that's a can of worms). Instead, we just + // give up and fall back to pretending like we never tried! + + let infcx = self.selcx.infcx(); + let (data, mapped_regions, mapped_types, mapped_consts, universe_map) = + BoundVarReplacer::replace_bound_vars(infcx, data); + + let normalized_ty = opt_normalize_projection_type( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + &mut self.obligations, + ) + .ok() + .flatten() + .unwrap_or_else(|| ty); + + let normalized_ty = PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_map, + normalized_ty, + ); + debug!( + ?self.depth, + ?ty, + ?normalized_ty, + obligations.len = ?self.obligations.len(), + "AssocTypeNormalizer: normalized type" + ); + normalized_ty + } + _ => ty, } } @@ -410,6 +459,259 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { } } +pub struct BoundVarReplacer<'me, 'tcx> { + pub infcx: &'me InferCtxt<'me, 'tcx>, + pub mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + pub mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, + pub mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>, + pub universes: BTreeMap<ty::DebruijnIndex, ty::UniverseIndex>, + pub universes_inverse: BTreeMap<ty::UniverseIndex, ty::DebruijnIndex>, + pub current_index: ty::DebruijnIndex, +} + +impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { + pub fn replace_bound_vars<T: TypeFoldable<'tcx>>( + infcx: &'me InferCtxt<'me, 'tcx>, + value: T, + ) -> ( + T, + BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + BTreeMap<ty::PlaceholderType, ty::BoundTy>, + BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>, + BTreeMap<ty::UniverseIndex, ty::DebruijnIndex>, + ) { + let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new(); + let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new(); + let mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar> = BTreeMap::new(); + + let mut replacer = BoundVarReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universes: BTreeMap::new(), + universes_inverse: BTreeMap::new(), + current_index: ty::INNERMOST, + }; + + let value = value.super_fold_with(&mut replacer); + + ( + value, + replacer.mapped_regions, + replacer.mapped_types, + replacer.mapped_consts, + replacer.universes_inverse, + ) + } +} + +impl TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_binder<T: TypeFoldable<'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::ReLateBound(debruijn, br) => { + let infcx = self.infcx; + let placeholder_db_index = + ty::DebruijnIndex::from_u32(self.current_index.as_u32() - debruijn.as_u32()); + let universe = *self + .universes + .entry(placeholder_db_index) + .or_insert_with(|| infcx.create_next_universe()); + self.universes_inverse.insert(universe, placeholder_db_index); + let p = ty::PlaceholderRegion { universe, name: br.kind }; + self.mapped_regions.insert(p.clone(), br); + self.infcx.tcx.mk_region(ty::RePlaceholder(p)) + } + _ => r, + } + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match *t.kind() { + ty::Bound(debruijn, bound_ty) => { + let infcx = self.infcx; + let placeholder_db_index = + ty::DebruijnIndex::from_u32(self.current_index.as_u32() - debruijn.as_u32()); + let universe = *self + .universes + .entry(placeholder_db_index) + .or_insert_with(|| infcx.create_next_universe()); + self.universes_inverse.insert(universe, placeholder_db_index); + let p = ty::PlaceholderType { universe, name: bound_ty.var }; + self.mapped_types.insert(p.clone(), bound_ty); + self.infcx.tcx.mk_ty(ty::Placeholder(p)) + } + _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), + _ => t, + } + } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + match *ct { + ty::Const { val: ty::ConstKind::Bound(debruijn, bound_const), ty } => { + let infcx = self.infcx; + let placeholder_db_index = + ty::DebruijnIndex::from_u32(self.current_index.as_u32() - debruijn.as_u32()); + let universe = *self + .universes + .entry(placeholder_db_index) + .or_insert_with(|| infcx.create_next_universe()); + self.universes_inverse.insert(universe, placeholder_db_index); + let p = ty::PlaceholderConst { + universe, + name: ty::BoundConst { var: bound_const, ty }, + }; + self.mapped_consts.insert(p.clone(), bound_const); + self.infcx.tcx.mk_const(ty::Const { val: ty::ConstKind::Placeholder(p), ty }) + } + _ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self), + _ => ct, + } + } +} + +pub struct PlaceholderReplacer<'me, 'tcx> { + pub infcx: &'me InferCtxt<'me, 'tcx>, + pub mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + pub mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, + pub mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>, + pub universes_inverse: BTreeMap<ty::UniverseIndex, ty::DebruijnIndex>, + pub current_index: ty::DebruijnIndex, +} + +impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { + pub fn replace_placeholders<T: TypeFoldable<'tcx>>( + infcx: &'me InferCtxt<'me, 'tcx>, + mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, + mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>, + universes_inverse: BTreeMap<ty::UniverseIndex, ty::DebruijnIndex>, + value: T, + ) -> T { + let mut replacer = PlaceholderReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universes_inverse, + current_index: ty::INNERMOST, + }; + value.super_fold_with(&mut replacer) + } +} + +impl TypeFolder<'tcx> for PlaceholderReplacer<'_, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_binder<T: TypeFoldable<'tcx>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + if !t.has_placeholders() && !t.has_infer_regions() { + return 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, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { + let r1 = match r0 { + ty::ReVar(_) => self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_region(self.infcx.tcx, r0), + _ => r0, + }; + + let r2 = match *r1 { + ty::RePlaceholder(p) => { + let replace_var = self.mapped_regions.get(&p); + match replace_var { + Some(replace_var) => { + let db = self + .universes_inverse + .get(&p.universe) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let index = + ty::DebruijnIndex::from_u32(db.as_u32() + self.current_index.as_u32()); + self.tcx().mk_region(ty::ReLateBound(index, *replace_var)) + } + None => r1, + } + } + _ => r1, + }; + + debug!(?r0, ?r1, ?r2, "fold_region"); + + r2 + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + match *ty.kind() { + ty::Placeholder(p) => { + let replace_var = self.mapped_types.get(&p); + match replace_var { + Some(replace_var) => { + let db = self + .universes_inverse + .get(&p.universe) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let index = + ty::DebruijnIndex::from_u32(db.as_u32() + self.current_index.as_u32()); + self.tcx().mk_ty(ty::Bound(index, *replace_var)) + } + None => ty, + } + } + + _ if ty.has_placeholders() || ty.has_infer_regions() => ty.super_fold_with(self), + _ => ty, + } + } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + if let ty::Const { val: ty::ConstKind::Placeholder(p), ty } = *ct { + let replace_var = self.mapped_consts.get(&p); + match replace_var { + Some(replace_var) => { + let db = self + .universes_inverse + .get(&p.universe) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let index = + ty::DebruijnIndex::from_u32(db.as_u32() + self.current_index.as_u32()); + self.tcx() + .mk_const(ty::Const { val: ty::ConstKind::Bound(index, *replace_var), ty }) + } + None => ct, + } + } else { + ct.super_fold_with(self) + } + } +} + /// The guts of `normalize`: normalize a specific projection like `<T /// as Trait>::Item`. The result is always a type (and possibly /// additional obligations). If ambiguity arises, which implies that diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 6673e021bf3..004372dd8b7 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -204,6 +204,69 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { } } } + ty::Projection(data) if !data.trait_ref(self.infcx.tcx).has_escaping_bound_vars() => { + // See note in `rustc_trait_selection::traits::project` + + // One other point mentioning: In `traits::project`, if a + // projection can't be normalized, we return an inference variable + // and register an obligation to later resolve that. Here, the query + // will just return ambiguity. In both cases, the effect is the same: we only want + // to return `ty` because there are bound vars that we aren't yet handling in a more + // complete way. + + let tcx = self.infcx.tcx; + let infcx = self.infcx; + let (data, mapped_regions, mapped_types, mapped_consts, universe_map) = + crate::traits::project::BoundVarReplacer::replace_bound_vars(infcx, data); + let data = data.super_fold_with(self); + + let mut orig_values = OriginalQueryValues::default(); + // HACK(matthewjasper) `'static` is special-cased in selection, + // so we cannot canonicalize it. + let c_data = self + .infcx + .canonicalize_hr_query_hack(self.param_env.and(data), &mut orig_values); + debug!("QueryNormalizer: c_data = {:#?}", c_data); + debug!("QueryNormalizer: orig_values = {:#?}", orig_values); + let normalized_ty = match tcx.normalize_projection_ty(c_data) { + Ok(result) => { + // We don't expect ambiguity. + if result.is_ambiguous() { + self.error = true; + return ty; + } + match self.infcx.instantiate_query_response_and_region_obligations( + self.cause, + self.param_env, + &orig_values, + result, + ) { + Ok(InferOk { value: result, obligations }) => { + debug!("QueryNormalizer: result = {:#?}", result); + debug!("QueryNormalizer: obligations = {:#?}", obligations); + self.obligations.extend(obligations); + result.normalized_ty + } + Err(_) => { + self.error = true; + ty + } + } + } + Err(NoSolution) => { + self.error = true; + ty + } + }; + crate::traits::project::PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_map, + normalized_ty, + ) + } _ => ty, })(); |
