Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace associated item bound vars with placeholders when projecting #86993

Merged
merged 4 commits into from
Jul 16, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
302 changes: 302 additions & 0 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>>;
Expand Down Expand Up @@ -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(
jackh726 marked this conversation as resolved.
Show resolved Hide resolved
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,
}
}
Expand All @@ -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);
jackh726 marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
63 changes: 63 additions & 0 deletions compiler/rustc_trait_selection/src/traits/query/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})();
Expand Down
Loading