Skip to content

Commit c63f1fe

Browse files
committedJul 9, 2021
Replace associated item bound vars with placeholders when projecting.
1 parent aa65b08 commit c63f1fe

18 files changed

+609
-31
lines changed
 

‎compiler/rustc_trait_selection/src/traits/project.rs

+302
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ use rustc_middle::ty::subst::Subst;
2929
use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness};
3030
use rustc_span::symbol::sym;
3131

32+
use std::collections::BTreeMap;
33+
3234
pub use rustc_middle::traits::Reveal;
3335

3436
pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>;
@@ -396,6 +398,53 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
396398
normalized_ty
397399
}
398400

401+
ty::Projection(data) if !data.trait_ref(self.tcx()).has_escaping_bound_vars() => {
402+
// Okay, so you thought the previous branch was hacky. Well, to
403+
// extend upon this, when the *trait ref* doesn't have escaping
404+
// bound vars, but the associated item *does* (can only occur
405+
// with GATs), then we might still be able to project the type.
406+
// For this, we temporarily replace the bound vars with
407+
// placeholders. Note though, that in the case that we still
408+
// can't project for whatever reason (e.g. self type isn't
409+
// known enough), we *can't* register an obligation and return
410+
// an inference variable (since then that obligation would have
411+
// bound vars and that's a can of worms). Instead, we just
412+
// give up and fall back to pretending like we never tried!
413+
414+
let infcx = self.selcx.infcx();
415+
let (data, mapped_regions, mapped_types, mapped_consts, universe_map) =
416+
BoundVarReplacer::replace_bound_vars(infcx, data);
417+
418+
let normalized_ty = opt_normalize_projection_type(
419+
self.selcx,
420+
self.param_env,
421+
data,
422+
self.cause.clone(),
423+
self.depth,
424+
&mut self.obligations,
425+
)
426+
.ok()
427+
.flatten()
428+
.unwrap_or_else(|| ty);
429+
430+
let normalized_ty = PlaceholderReplacer::replace_placeholders(
431+
infcx,
432+
mapped_regions,
433+
mapped_types,
434+
mapped_consts,
435+
universe_map,
436+
normalized_ty,
437+
);
438+
debug!(
439+
?self.depth,
440+
?ty,
441+
?normalized_ty,
442+
obligations.len = ?self.obligations.len(),
443+
"AssocTypeNormalizer: normalized type"
444+
);
445+
normalized_ty
446+
}
447+
399448
_ => ty,
400449
}
401450
}
@@ -410,6 +459,259 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
410459
}
411460
}
412461

462+
pub struct BoundVarReplacer<'me, 'tcx> {
463+
pub infcx: &'me InferCtxt<'me, 'tcx>,
464+
pub mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
465+
pub mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
466+
pub mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
467+
pub universes: BTreeMap<ty::DebruijnIndex, ty::UniverseIndex>,
468+
pub universes_inverse: BTreeMap<ty::UniverseIndex, ty::DebruijnIndex>,
469+
pub current_index: ty::DebruijnIndex,
470+
}
471+
472+
impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> {
473+
pub fn replace_bound_vars<T: TypeFoldable<'tcx>>(
474+
infcx: &'me InferCtxt<'me, 'tcx>,
475+
value: T,
476+
) -> (
477+
T,
478+
BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
479+
BTreeMap<ty::PlaceholderType, ty::BoundTy>,
480+
BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
481+
BTreeMap<ty::UniverseIndex, ty::DebruijnIndex>,
482+
) {
483+
let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new();
484+
let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new();
485+
let mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar> = BTreeMap::new();
486+
487+
let mut replacer = BoundVarReplacer {
488+
infcx,
489+
mapped_regions,
490+
mapped_types,
491+
mapped_consts,
492+
universes: BTreeMap::new(),
493+
universes_inverse: BTreeMap::new(),
494+
current_index: ty::INNERMOST,
495+
};
496+
497+
let value = value.super_fold_with(&mut replacer);
498+
499+
(
500+
value,
501+
replacer.mapped_regions,
502+
replacer.mapped_types,
503+
replacer.mapped_consts,
504+
replacer.universes_inverse,
505+
)
506+
}
507+
}
508+
509+
impl TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> {
510+
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
511+
self.infcx.tcx
512+
}
513+
514+
fn fold_binder<T: TypeFoldable<'tcx>>(
515+
&mut self,
516+
t: ty::Binder<'tcx, T>,
517+
) -> ty::Binder<'tcx, T> {
518+
self.current_index.shift_in(1);
519+
let t = t.super_fold_with(self);
520+
self.current_index.shift_out(1);
521+
t
522+
}
523+
524+
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
525+
match *r {
526+
ty::ReLateBound(debruijn, br) => {
527+
let infcx = self.infcx;
528+
let placeholder_db_index =
529+
ty::DebruijnIndex::from_u32(self.current_index.as_u32() - debruijn.as_u32());
530+
let universe = *self
531+
.universes
532+
.entry(placeholder_db_index)
533+
.or_insert_with(|| infcx.create_next_universe());
534+
self.universes_inverse.insert(universe, placeholder_db_index);
535+
let p = ty::PlaceholderRegion { universe, name: br.kind };
536+
self.mapped_regions.insert(p.clone(), br);
537+
self.infcx.tcx.mk_region(ty::RePlaceholder(p))
538+
}
539+
_ => r,
540+
}
541+
}
542+
543+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
544+
match *t.kind() {
545+
ty::Bound(debruijn, bound_ty) => {
546+
let infcx = self.infcx;
547+
let placeholder_db_index =
548+
ty::DebruijnIndex::from_u32(self.current_index.as_u32() - debruijn.as_u32());
549+
let universe = *self
550+
.universes
551+
.entry(placeholder_db_index)
552+
.or_insert_with(|| infcx.create_next_universe());
553+
self.universes_inverse.insert(universe, placeholder_db_index);
554+
let p = ty::PlaceholderType { universe, name: bound_ty.var };
555+
self.mapped_types.insert(p.clone(), bound_ty);
556+
self.infcx.tcx.mk_ty(ty::Placeholder(p))
557+
}
558+
_ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
559+
_ => t,
560+
}
561+
}
562+
563+
fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
564+
match *ct {
565+
ty::Const { val: ty::ConstKind::Bound(debruijn, bound_const), ty } => {
566+
let infcx = self.infcx;
567+
let placeholder_db_index =
568+
ty::DebruijnIndex::from_u32(self.current_index.as_u32() - debruijn.as_u32());
569+
let universe = *self
570+
.universes
571+
.entry(placeholder_db_index)
572+
.or_insert_with(|| infcx.create_next_universe());
573+
self.universes_inverse.insert(universe, placeholder_db_index);
574+
let p = ty::PlaceholderConst {
575+
universe,
576+
name: ty::BoundConst { var: bound_const, ty },
577+
};
578+
self.mapped_consts.insert(p.clone(), bound_const);
579+
self.infcx.tcx.mk_const(ty::Const { val: ty::ConstKind::Placeholder(p), ty })
580+
}
581+
_ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self),
582+
_ => ct,
583+
}
584+
}
585+
}
586+
587+
pub struct PlaceholderReplacer<'me, 'tcx> {
588+
pub infcx: &'me InferCtxt<'me, 'tcx>,
589+
pub mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
590+
pub mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
591+
pub mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
592+
pub universes_inverse: BTreeMap<ty::UniverseIndex, ty::DebruijnIndex>,
593+
pub current_index: ty::DebruijnIndex,
594+
}
595+
596+
impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> {
597+
pub fn replace_placeholders<T: TypeFoldable<'tcx>>(
598+
infcx: &'me InferCtxt<'me, 'tcx>,
599+
mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
600+
mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
601+
mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
602+
universes_inverse: BTreeMap<ty::UniverseIndex, ty::DebruijnIndex>,
603+
value: T,
604+
) -> T {
605+
let mut replacer = PlaceholderReplacer {
606+
infcx,
607+
mapped_regions,
608+
mapped_types,
609+
mapped_consts,
610+
universes_inverse,
611+
current_index: ty::INNERMOST,
612+
};
613+
value.super_fold_with(&mut replacer)
614+
}
615+
}
616+
617+
impl TypeFolder<'tcx> for PlaceholderReplacer<'_, 'tcx> {
618+
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
619+
self.infcx.tcx
620+
}
621+
622+
fn fold_binder<T: TypeFoldable<'tcx>>(
623+
&mut self,
624+
t: ty::Binder<'tcx, T>,
625+
) -> ty::Binder<'tcx, T> {
626+
if !t.has_placeholders() && !t.has_infer_regions() {
627+
return t;
628+
}
629+
self.current_index.shift_in(1);
630+
let t = t.super_fold_with(self);
631+
self.current_index.shift_out(1);
632+
t
633+
}
634+
635+
fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> {
636+
let r1 = match r0 {
637+
ty::ReVar(_) => self
638+
.infcx
639+
.inner
640+
.borrow_mut()
641+
.unwrap_region_constraints()
642+
.opportunistic_resolve_region(self.infcx.tcx, r0),
643+
_ => r0,
644+
};
645+
646+
let r2 = match *r1 {
647+
ty::RePlaceholder(p) => {
648+
let replace_var = self.mapped_regions.get(&p);
649+
match replace_var {
650+
Some(replace_var) => {
651+
let db = self
652+
.universes_inverse
653+
.get(&p.universe)
654+
.unwrap_or_else(|| bug!("Unexpected placeholder universe."));
655+
let index =
656+
ty::DebruijnIndex::from_u32(db.as_u32() + self.current_index.as_u32());
657+
self.tcx().mk_region(ty::ReLateBound(index, *replace_var))
658+
}
659+
None => r1,
660+
}
661+
}
662+
_ => r1,
663+
};
664+
665+
debug!(?r0, ?r1, ?r2, "fold_region");
666+
667+
r2
668+
}
669+
670+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
671+
match *ty.kind() {
672+
ty::Placeholder(p) => {
673+
let replace_var = self.mapped_types.get(&p);
674+
match replace_var {
675+
Some(replace_var) => {
676+
let db = self
677+
.universes_inverse
678+
.get(&p.universe)
679+
.unwrap_or_else(|| bug!("Unexpected placeholder universe."));
680+
let index =
681+
ty::DebruijnIndex::from_u32(db.as_u32() + self.current_index.as_u32());
682+
self.tcx().mk_ty(ty::Bound(index, *replace_var))
683+
}
684+
None => ty,
685+
}
686+
}
687+
688+
_ if ty.has_placeholders() || ty.has_infer_regions() => ty.super_fold_with(self),
689+
_ => ty,
690+
}
691+
}
692+
693+
fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
694+
if let ty::Const { val: ty::ConstKind::Placeholder(p), ty } = *ct {
695+
let replace_var = self.mapped_consts.get(&p);
696+
match replace_var {
697+
Some(replace_var) => {
698+
let db = self
699+
.universes_inverse
700+
.get(&p.universe)
701+
.unwrap_or_else(|| bug!("Unexpected placeholder universe."));
702+
let index =
703+
ty::DebruijnIndex::from_u32(db.as_u32() + self.current_index.as_u32());
704+
self.tcx()
705+
.mk_const(ty::Const { val: ty::ConstKind::Bound(index, *replace_var), ty })
706+
}
707+
None => ct,
708+
}
709+
} else {
710+
ct.super_fold_with(self)
711+
}
712+
}
713+
}
714+
413715
/// The guts of `normalize`: normalize a specific projection like `<T
414716
/// as Trait>::Item`. The result is always a type (and possibly
415717
/// additional obligations). If ambiguity arises, which implies that

‎compiler/rustc_trait_selection/src/traits/query/normalize.rs

+63
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,69 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
204204
}
205205
}
206206
}
207+
ty::Projection(data) if !data.trait_ref(self.infcx.tcx).has_escaping_bound_vars() => {
208+
// See note in `rustc_trait_selection::traits::project`
209+
210+
// One other point mentioning: In `traits::project`, if a
211+
// projection can't be normalized, we return an inference variable
212+
// and register an obligation to later resolve that. Here, the query
213+
// will just return ambiguity. In both cases, the effect is the same: we only want
214+
// to return `ty` because there are bound vars that we aren't yet handling in a more
215+
// complete way.
216+
217+
let tcx = self.infcx.tcx;
218+
let infcx = self.infcx;
219+
let (data, mapped_regions, mapped_types, mapped_consts, universe_map) =
220+
crate::traits::project::BoundVarReplacer::replace_bound_vars(infcx, data);
221+
let data = data.super_fold_with(self);
222+
223+
let mut orig_values = OriginalQueryValues::default();
224+
// HACK(matthewjasper) `'static` is special-cased in selection,
225+
// so we cannot canonicalize it.
226+
let c_data = self
227+
.infcx
228+
.canonicalize_hr_query_hack(self.param_env.and(data), &mut orig_values);
229+
debug!("QueryNormalizer: c_data = {:#?}", c_data);
230+
debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
231+
let normalized_ty = match tcx.normalize_projection_ty(c_data) {
232+
Ok(result) => {
233+
// We don't expect ambiguity.
234+
if result.is_ambiguous() {
235+
self.error = true;
236+
return ty;
237+
}
238+
match self.infcx.instantiate_query_response_and_region_obligations(
239+
self.cause,
240+
self.param_env,
241+
&orig_values,
242+
result,
243+
) {
244+
Ok(InferOk { value: result, obligations }) => {
245+
debug!("QueryNormalizer: result = {:#?}", result);
246+
debug!("QueryNormalizer: obligations = {:#?}", obligations);
247+
self.obligations.extend(obligations);
248+
result.normalized_ty
249+
}
250+
Err(_) => {
251+
self.error = true;
252+
ty
253+
}
254+
}
255+
}
256+
Err(NoSolution) => {
257+
self.error = true;
258+
ty
259+
}
260+
};
261+
crate::traits::project::PlaceholderReplacer::replace_placeholders(
262+
infcx,
263+
mapped_regions,
264+
mapped_types,
265+
mapped_consts,
266+
universe_map,
267+
normalized_ty,
268+
)
269+
}
207270

208271
_ => ty,
209272
})();

0 commit comments

Comments
 (0)
Please sign in to comment.