From c63f1fe92b99a9828e9f999355213b0b73175aa0 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 9 Jul 2021 00:04:47 -0400 Subject: [PATCH 1/4] Replace associated item bound vars with placeholders when projecting. --- .../src/traits/project.rs | 302 ++++++++++++++++++ .../src/traits/query/normalize.rs | 63 ++++ compiler/rustc_typeck/src/check/wfcheck.rs | 6 +- .../associated-types-for-unimpl-trait.stderr | 4 +- .../associated-types-no-suitable-bound.stderr | 4 +- ...ated-types-no-suitable-supertrait-2.stderr | 4 +- ...ciated-types-no-suitable-supertrait.stderr | 8 +- ...ted-trait-in-method-without-default.stderr | 4 +- .../normalization-debruijn-1.rs | 36 +++ .../normalization-debruijn-2.rs | 31 ++ .../normalization-debruijn-3.rs | 41 +++ .../normalization-generality.rs | 36 +++ .../generic-associated-types/issue-76407.rs | 28 ++ .../generic-associated-types/issue-76826.rs | 45 +++ src/test/ui/issues/issue-18611.stderr | 8 +- .../ui/issues/issue-20831-debruijn.stderr | 8 +- .../ui/nll/normalization-bounds-error.stderr | 8 +- src/test/ui/wf/wf-foreign-fn-decl-ret.stderr | 4 +- 18 files changed, 609 insertions(+), 31 deletions(-) create mode 100644 src/test/ui/associated-types/normalization-debruijn-1.rs create mode 100644 src/test/ui/associated-types/normalization-debruijn-2.rs create mode 100644 src/test/ui/associated-types/normalization-debruijn-3.rs create mode 100644 src/test/ui/associated-types/normalization-generality.rs create mode 100644 src/test/ui/generic-associated-types/issue-76407.rs create mode 100644 src/test/ui/generic-associated-types/issue-76826.rs diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 0e685205069cb..8433b4ecb0ab1 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, + pub mapped_types: BTreeMap, + pub mapped_consts: BTreeMap, ty::BoundVar>, + pub universes: BTreeMap, + pub universes_inverse: BTreeMap, + pub current_index: ty::DebruijnIndex, +} + +impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { + pub fn replace_bound_vars>( + infcx: &'me InferCtxt<'me, 'tcx>, + value: T, + ) -> ( + T, + BTreeMap, + BTreeMap, + BTreeMap, ty::BoundVar>, + BTreeMap, + ) { + let mapped_regions: BTreeMap = BTreeMap::new(); + let mapped_types: BTreeMap = BTreeMap::new(); + let mapped_consts: BTreeMap, 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>( + &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, + pub mapped_types: BTreeMap, + pub mapped_consts: BTreeMap, ty::BoundVar>, + pub universes_inverse: BTreeMap, + pub current_index: ty::DebruijnIndex, +} + +impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { + pub fn replace_placeholders>( + infcx: &'me InferCtxt<'me, 'tcx>, + mapped_regions: BTreeMap, + mapped_types: BTreeMap, + mapped_consts: BTreeMap, ty::BoundVar>, + universes_inverse: BTreeMap, + 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>( + &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 `::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 6673e021bf308..004372dd8b74d 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, })(); diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 0e063c86f2f4e..c7a28cd9f3ef4 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -406,7 +406,6 @@ fn check_associated_item( } ty::AssocKind::Fn => { let sig = fcx.tcx.fn_sig(item.def_id); - let sig = fcx.normalize_associated_types_in(span, sig); let hir_sig = sig_if_method.expect("bad signature for method"); check_fn_or_method( fcx, @@ -611,7 +610,6 @@ fn check_item_fn( for_id(tcx, item_id, span).with_fcx(|fcx| { let def_id = tcx.hir().local_def_id(item_id); let sig = tcx.fn_sig(def_id); - let sig = fcx.normalize_associated_types_in(span, sig); let mut implied_bounds = vec![]; check_fn_or_method(fcx, ident.span, sig, decl, def_id.to_def_id(), &mut implied_bounds); implied_bounds @@ -902,8 +900,8 @@ fn check_fn_or_method<'fcx, 'tcx>( def_id: DefId, implied_bounds: &mut Vec>, ) { - let sig = fcx.normalize_associated_types_in(span, sig); let sig = fcx.tcx.liberate_late_bound_regions(def_id, sig); + let sig = fcx.normalize_associated_types_in(span, sig); for (&input_ty, ty) in iter::zip(sig.inputs(), hir_decl.inputs) { fcx.register_wf_obligation(input_ty.into(), ty.span, ObligationCauseCode::MiscObligation); @@ -1081,8 +1079,8 @@ fn check_method_receiver<'fcx, 'tcx>( let span = fn_sig.decl.inputs[0].span; let sig = fcx.tcx.fn_sig(method.def_id); - let sig = fcx.normalize_associated_types_in(span, sig); let sig = fcx.tcx.liberate_late_bound_regions(method.def_id, sig); + let sig = fcx.normalize_associated_types_in(span, sig); debug!("check_method_receiver: sig={:?}", sig); diff --git a/src/test/ui/associated-types/associated-types-for-unimpl-trait.stderr b/src/test/ui/associated-types/associated-types-for-unimpl-trait.stderr index 6d7289bd0712b..e8c11a32bf7fd 100644 --- a/src/test/ui/associated-types/associated-types-for-unimpl-trait.stderr +++ b/src/test/ui/associated-types/associated-types-for-unimpl-trait.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `Self: Get` is not satisfied - --> $DIR/associated-types-for-unimpl-trait.rs:10:5 + --> $DIR/associated-types-for-unimpl-trait.rs:10:8 | LL | fn uhoh(&self, foo: U, bar: ::Value) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` + | ^^^^ the trait `Get` is not implemented for `Self` | help: consider further restricting `Self` | diff --git a/src/test/ui/associated-types/associated-types-no-suitable-bound.stderr b/src/test/ui/associated-types/associated-types-no-suitable-bound.stderr index 0b5dee611e489..e3be434698ab9 100644 --- a/src/test/ui/associated-types/associated-types-no-suitable-bound.stderr +++ b/src/test/ui/associated-types/associated-types-no-suitable-bound.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `T: Get` is not satisfied - --> $DIR/associated-types-no-suitable-bound.rs:11:5 + --> $DIR/associated-types-no-suitable-bound.rs:11:8 | LL | fn uhoh(foo: ::Value) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `T` + | ^^^^ the trait `Get` is not implemented for `T` | help: consider restricting type parameter `T` | diff --git a/src/test/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr b/src/test/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr index dfe62aa5d6b00..9dc3414e9edf0 100644 --- a/src/test/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr +++ b/src/test/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `Self: Get` is not satisfied - --> $DIR/associated-types-no-suitable-supertrait-2.rs:17:5 + --> $DIR/associated-types-no-suitable-supertrait-2.rs:17:8 | LL | fn uhoh(&self, foo: U, bar: ::Value) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` + | ^^^^ the trait `Get` is not implemented for `Self` | help: consider further restricting `Self` | diff --git a/src/test/ui/associated-types/associated-types-no-suitable-supertrait.stderr b/src/test/ui/associated-types/associated-types-no-suitable-supertrait.stderr index f0f2451a1ecea..c2aed3f9de548 100644 --- a/src/test/ui/associated-types/associated-types-no-suitable-supertrait.stderr +++ b/src/test/ui/associated-types/associated-types-no-suitable-supertrait.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `Self: Get` is not satisfied - --> $DIR/associated-types-no-suitable-supertrait.rs:17:5 + --> $DIR/associated-types-no-suitable-supertrait.rs:17:8 | LL | fn uhoh(&self, foo: U, bar: ::Value) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` + | ^^^^ the trait `Get` is not implemented for `Self` | help: consider further restricting `Self` | @@ -10,10 +10,10 @@ LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Ge | ^^^^^^^^^^^^^^^ error[E0277]: the trait bound `(T, U): Get` is not satisfied - --> $DIR/associated-types-no-suitable-supertrait.rs:22:5 + --> $DIR/associated-types-no-suitable-supertrait.rs:22:8 | LL | fn uhoh(&self, foo: U, bar: <(T, U) as Get>::Value) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `(T, U)` + | ^^^^ the trait `Get` is not implemented for `(T, U)` error: aborting due to 2 previous errors diff --git a/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.stderr b/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.stderr index 4528f03c54a63..fb842d968676d 100644 --- a/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.stderr +++ b/src/test/ui/associated-types/associated-types-projection-to-unrelated-trait-in-method-without-default.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `Self: Get` is not satisfied - --> $DIR/associated-types-projection-to-unrelated-trait-in-method-without-default.rs:10:5 + --> $DIR/associated-types-projection-to-unrelated-trait-in-method-without-default.rs:10:8 | LL | fn okay(&self, foo: U, bar: ::Value); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` + | ^^^^ the trait `Get` is not implemented for `Self` | help: consider further restricting `Self` | diff --git a/src/test/ui/associated-types/normalization-debruijn-1.rs b/src/test/ui/associated-types/normalization-debruijn-1.rs new file mode 100644 index 0000000000000..a5abf1ba99d6a --- /dev/null +++ b/src/test/ui/associated-types/normalization-debruijn-1.rs @@ -0,0 +1,36 @@ +// build-pass +// edition:2018 + +// Regression test to ensure we handle debruijn indices correctly in projection +// normalization under binders. Found in crater run for #85499 + +use std::future::Future; +use std::pin::Pin; +pub enum Outcome { + Success((S, E)), +} +pub struct Request<'r> { + _marker: std::marker::PhantomData<&'r ()>, +} +pub trait FromRequest<'r>: Sized { + type Error; + fn from_request<'life0>( + request: &'r Request<'life0>, + ) -> Pin>>>; +} +impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option { + type Error = (); + fn from_request<'life0>( + request: &'r Request<'life0>, + ) -> Pin>>> { + Box::pin(async move { + let request = request; + match T::from_request(request).await { + _ => todo!(), + } + }); + todo!() + } +} + +fn main() {} diff --git a/src/test/ui/associated-types/normalization-debruijn-2.rs b/src/test/ui/associated-types/normalization-debruijn-2.rs new file mode 100644 index 0000000000000..abe248e16a198 --- /dev/null +++ b/src/test/ui/associated-types/normalization-debruijn-2.rs @@ -0,0 +1,31 @@ +// build-pass +// edition:2018 + +// Regression test to ensure we handle debruijn indices correctly in projection +// normalization under binders. Found in crater run for #85499 + +use std::future::Future; +use std::pin::Pin; +pub enum Outcome { + Success(S), + Failure(E), +} +pub struct Request<'r> { + _marker: std::marker::PhantomData<&'r ()>, +} +pub trait FromRequest<'r>: Sized { + type Error; + fn from_request<'life0>( + request: &'r Request<'life0>, + ) -> Pin>>>; +} +pub struct S { + _marker: std::marker::PhantomData, +} +impl<'r, T: FromRequest<'r>> S { + pub async fn from_request(request: &'r Request<'_>) { + let _ = T::from_request(request).await; + } +} + +fn main() {} diff --git a/src/test/ui/associated-types/normalization-debruijn-3.rs b/src/test/ui/associated-types/normalization-debruijn-3.rs new file mode 100644 index 0000000000000..2bea78cf7bd69 --- /dev/null +++ b/src/test/ui/associated-types/normalization-debruijn-3.rs @@ -0,0 +1,41 @@ +// build-pass +// edition:2018 + +// Regression test to ensure we handle debruijn indices correctly in projection +// normalization under binders. Found in crater run for #85499 + +use std::future::{Future, Ready}; +async fn read() { + let _ = connect(&()).await; +} +async fn connect(addr: A) { + let _ = addr.to_socket_addr().await; +} +pub trait ToSocketAddr { + type Future: Future; + fn to_socket_addr(&self) -> Self::Future; +} +impl ToSocketAddr for &() { + type Future = Ready<()>; + fn to_socket_addr(&self) -> Self::Future { + unimplemented!() + } +} +struct Server; +impl Server { + fn and_then(self, _fun: F) -> AndThen { + unimplemented!() + } +} +struct AndThen { + _marker: std::marker::PhantomData, +} +pub async fn run(_: F) { +} +fn main() { + let _ = async { + let server = Server; + let verification_route = server.and_then(read); + run(verification_route).await; + }; +} diff --git a/src/test/ui/associated-types/normalization-generality.rs b/src/test/ui/associated-types/normalization-generality.rs new file mode 100644 index 0000000000000..f8e3f5b58d1b3 --- /dev/null +++ b/src/test/ui/associated-types/normalization-generality.rs @@ -0,0 +1,36 @@ +// build-pass + +// Ensures that we don't regress on "implementation is not general enough" when +// normalizating under binders. + +#![feature(no_core)] + +pub trait Yokeable<'a> { + type Output: 'a; +} + +pub struct Yoke Yokeable<'a>> { + _yokeable: Y, +} + +impl Yokeable<'a>> Yoke { + pub fn project<'this, P>( + &'this self, + _f: for<'a> fn(>::Output, &'a ()) ->

>::Output, + ) -> Yoke

+ where + P: for<'a> Yokeable<'a>, + { + unimplemented!() + } +} + +pub fn slice(y: Yoke<&'static ()>) -> Yoke<&'static ()> { + y.project(move |yk, _| yk) +} + +impl<'a, T> Yokeable<'a> for &'static T { + type Output = &'a T; +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-76407.rs b/src/test/ui/generic-associated-types/issue-76407.rs new file mode 100644 index 0000000000000..42f19feb5f132 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-76407.rs @@ -0,0 +1,28 @@ +// check-pass + +#![feature(generic_associated_types)] +#![allow(incomplete_features)] + +trait Marker {} + +impl Marker for u32 {} + +trait MyTrait { + type Item<'a>; +} + +struct MyStruct; + +impl MyTrait for MyStruct { + type Item<'a> = u32; +} + +fn ty_check() +where + T: MyTrait, + for<'a> T::Item<'a>: Marker +{} + +fn main() { + ty_check::(); +} diff --git a/src/test/ui/generic-associated-types/issue-76826.rs b/src/test/ui/generic-associated-types/issue-76826.rs new file mode 100644 index 0000000000000..a905ef4eb13fd --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-76826.rs @@ -0,0 +1,45 @@ +// run-pass + +#![feature(generic_associated_types)] +#![allow(incomplete_features)] + +pub trait Iter { + type Item<'a> where Self: 'a; + + fn next<'a>(&'a mut self) -> Option>; + + fn for_each(mut self, mut f: F) + where Self: Sized, F: for<'a> FnMut(Self::Item<'a>) + { + while let Some(item) = self.next() { + f(item); + } + } +} + +pub struct Windows { + items: Vec, + start: usize, + len: usize, +} + +impl Windows { + pub fn new(items: Vec, len: usize) -> Self { + Self { items, start: 0, len } + } +} + +impl Iter for Windows { + type Item<'a> where T: 'a = &'a mut [T]; + + fn next<'a>(&'a mut self) -> Option> { + let slice = self.items.get_mut(self.start..self.start + self.len)?; + self.start += 1; + Some(slice) + } +} + +fn main() { + Windows::new(vec![1, 2, 3, 4, 5], 3) + .for_each(|slice| println!("{:?}", slice)); +} diff --git a/src/test/ui/issues/issue-18611.stderr b/src/test/ui/issues/issue-18611.stderr index 22c3470b61ede..8872f51753c94 100644 --- a/src/test/ui/issues/issue-18611.stderr +++ b/src/test/ui/issues/issue-18611.stderr @@ -1,10 +1,8 @@ error[E0277]: the trait bound `isize: HasState` is not satisfied - --> $DIR/issue-18611.rs:1:1 + --> $DIR/issue-18611.rs:1:4 | -LL | / fn add_state(op: ::State) { -LL | | -LL | | } - | |_^ the trait `HasState` is not implemented for `isize` +LL | fn add_state(op: ::State) { + | ^^^^^^^^^ the trait `HasState` is not implemented for `isize` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-20831-debruijn.stderr b/src/test/ui/issues/issue-20831-debruijn.stderr index e68482d1caf69..03e3311e0f39b 100644 --- a/src/test/ui/issues/issue-20831-debruijn.stderr +++ b/src/test/ui/issues/issue-20831-debruijn.stderr @@ -1,8 +1,8 @@ error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements - --> $DIR/issue-20831-debruijn.rs:28:33 + --> $DIR/issue-20831-debruijn.rs:28:8 | LL | fn subscribe(&mut self, t : Box::Output> + 'a>) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ | note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 28:58... --> $DIR/issue-20831-debruijn.rs:28:58 @@ -15,10 +15,10 @@ note: ...but the lifetime must also be valid for the lifetime `'a` as defined on LL | impl<'a> Publisher<'a> for MyStruct<'a> { | ^^ note: ...so that the types are compatible - --> $DIR/issue-20831-debruijn.rs:28:33 + --> $DIR/issue-20831-debruijn.rs:28:8 | LL | fn subscribe(&mut self, t : Box::Output> + 'a>) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ = note: expected `Publisher<'_>` found `Publisher<'_>` diff --git a/src/test/ui/nll/normalization-bounds-error.stderr b/src/test/ui/nll/normalization-bounds-error.stderr index d003acd879a77..8c7c8918f3fc6 100644 --- a/src/test/ui/nll/normalization-bounds-error.stderr +++ b/src/test/ui/nll/normalization-bounds-error.stderr @@ -1,8 +1,8 @@ error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'d` due to conflicting requirements - --> $DIR/normalization-bounds-error.rs:12:1 + --> $DIR/normalization-bounds-error.rs:12:4 | LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ | note: first, the lifetime cannot outlive the lifetime `'d` as defined on the function body at 12:14... --> $DIR/normalization-bounds-error.rs:12:14 @@ -15,10 +15,10 @@ note: ...but the lifetime must also be valid for the lifetime `'a` as defined on LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {} | ^^ note: ...so that the types are compatible - --> $DIR/normalization-bounds-error.rs:12:1 + --> $DIR/normalization-bounds-error.rs:12:4 | LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ = note: expected `Visitor<'d>` found `Visitor<'_>` diff --git a/src/test/ui/wf/wf-foreign-fn-decl-ret.stderr b/src/test/ui/wf/wf-foreign-fn-decl-ret.stderr index 9081b7929d935..a0eb7d10bd94a 100644 --- a/src/test/ui/wf/wf-foreign-fn-decl-ret.stderr +++ b/src/test/ui/wf/wf-foreign-fn-decl-ret.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `(): Foo` is not satisfied - --> $DIR/wf-foreign-fn-decl-ret.rs:11:5 + --> $DIR/wf-foreign-fn-decl-ret.rs:11:12 | LL | pub fn lint_me() -> <() as Foo>::Assoc; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()` + | ^^^^^^^ the trait `Foo` is not implemented for `()` error[E0277]: the trait bound `u32: Unsatisfied` is not satisfied --> $DIR/wf-foreign-fn-decl-ret.rs:14:32 From a9f1e1c440ae20f0ea99f5e265f24094f1a613a5 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 13 Jul 2021 10:50:40 -0400 Subject: [PATCH 2/4] WIP partial apply fix --- .../src/traits/project.rs | 268 +++++++++++------- .../src/traits/query/normalize.rs | 29 +- 2 files changed, 193 insertions(+), 104 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 8433b4ecb0ab1..4f74f86564d15 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -298,6 +298,7 @@ struct AssocTypeNormalizer<'a, 'b, 'tcx> { cause: ObligationCause<'tcx>, obligations: &'a mut Vec>, depth: usize, + universes: Vec>, } impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { @@ -308,12 +309,16 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { depth: usize, obligations: &'a mut Vec>, ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { - AssocTypeNormalizer { selcx, param_env, cause, obligations, depth } + AssocTypeNormalizer { selcx, param_env, cause, obligations, depth, universes: vec![] } } fn fold>(&mut self, value: T) -> T { let value = self.selcx.infcx().resolve_vars_if_possible(value); + if value.has_escaping_bound_vars() { + bug!("Normalizing without wrapping in a `Binder`"); + } + if !value.has_projections() { value } else { value.fold_with(self) } } } @@ -323,6 +328,16 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { self.selcx.tcx() } + fn fold_binder>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.universes.push(None); + let t = t.super_fold_with(self); + self.universes.pop(); + t + } + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { if !ty.has_projections() { return ty; @@ -412,37 +427,40 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { // 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 + let replaced = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + if let Some((data, mapped_regions, mapped_types, mapped_consts)) = replaced { + 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, + &self.universes, + normalized_ty, + ); + debug!( + ?self.depth, + ?ty, + ?normalized_ty, + obligations.len = ?self.obligations.len(), + "AssocTypeNormalizer: normalized type" + ); + normalized_ty + } else { + ty + } } _ => ty, @@ -460,26 +478,42 @@ 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, - pub mapped_types: BTreeMap, - pub mapped_consts: BTreeMap, ty::BoundVar>, - pub universes: BTreeMap, - pub universes_inverse: BTreeMap, - pub current_index: ty::DebruijnIndex, + infcx: &'me InferCtxt<'me, 'tcx>, + // These three maps track the bound variable that were replaced by placeholders. It might be + // nice to remove these since we already have the `kind` in the placeholder; we really just need + // the `var` (but we *could* bring that into scope if we were to track them as we pass them). + mapped_regions: BTreeMap, + mapped_types: BTreeMap, + mapped_consts: BTreeMap, ty::BoundVar>, + // The current depth relative to *this* folding, *not* the entire normalization. In other words, + // the depth of binders we've passed here. + current_index: ty::DebruijnIndex, + // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy: + // we don't actually create a universe until we see a bound var we have to replace. + universe_indices: &'me mut Vec>, + // FIXME: So, this is a less-than-ideal solution to a problem we want to solve eventually. Ideally, we + // shouldn't need to worry about bound vars for which we haven't passed (`self.current_index`) + // or that we don't explicitly know about (`self.universe_indices`). This is true for + // `AssocTypeNormalizer` but not `QueryNormalizer` currently. When we can always know about + // any binding levels above us, we can remove this. (The alternative would be + // `outer_exclusive_binder`, but that only exists on `Ty`. Otherwise, we would have to visit + // through the `T`, which we specifically want to avoid not being lazy.) + failed: bool, } impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { + /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that + /// use a binding level above `universe_indices.len()`, we fail. pub fn replace_bound_vars>( infcx: &'me InferCtxt<'me, 'tcx>, + universe_indices: &'me mut Vec>, value: T, - ) -> ( + ) -> Option<( T, BTreeMap, BTreeMap, BTreeMap, ty::BoundVar>, - BTreeMap, - ) { + )> { let mapped_regions: BTreeMap = BTreeMap::new(); let mapped_types: BTreeMap = BTreeMap::new(); let mapped_consts: BTreeMap, ty::BoundVar> = BTreeMap::new(); @@ -489,20 +523,16 @@ impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { mapped_regions, mapped_types, mapped_consts, - universes: BTreeMap::new(), - universes_inverse: BTreeMap::new(), current_index: ty::INNERMOST, + universe_indices, + failed: false, }; let value = value.super_fold_with(&mut replacer); - ( - value, - replacer.mapped_regions, - replacer.mapped_types, - replacer.mapped_consts, - replacer.universes_inverse, - ) + (!replacer.failed).then(|| { + (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) + }) } } @@ -523,15 +553,24 @@ impl TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { - ty::ReLateBound(debruijn, br) => { + ty::ReLateBound(debruijn, _) + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + self.failed = true; + r + } + ty::ReLateBound(debruijn, br) if debruijn >= self.current_index => { 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 index = self.universe_indices.len() - debruijn.as_usize() + + self.current_index.as_usize() + - 1; + let universe = self.universe_indices[index].unwrap_or_else(|| { + for i in self.universe_indices.iter_mut().take(index + 1) { + *i = i.or_else(|| Some(infcx.create_next_universe())) + } + self.universe_indices[index].unwrap() + }); let p = ty::PlaceholderRegion { universe, name: br.kind }; self.mapped_regions.insert(p.clone(), br); self.infcx.tcx.mk_region(ty::RePlaceholder(p)) @@ -542,15 +581,24 @@ impl TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match *t.kind() { - ty::Bound(debruijn, bound_ty) => { + ty::Bound(debruijn, _) + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + self.failed = true; + t + } + ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { 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 index = self.universe_indices.len() - debruijn.as_usize() + + self.current_index.as_usize() + - 1; + let universe = self.universe_indices[index].unwrap_or_else(|| { + for i in self.universe_indices.iter_mut().take(index + 1) { + *i = i.or_else(|| Some(infcx.create_next_universe())) + } + self.universe_indices[index].unwrap() + }); 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)) @@ -562,15 +610,26 @@ impl TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { 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 } => { + ty::Const { val: ty::ConstKind::Bound(debruijn, _), ty: _ } + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + self.failed = true; + ct + } + ty::Const { val: ty::ConstKind::Bound(debruijn, bound_const), ty } + if debruijn >= self.current_index => + { 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 index = self.universe_indices.len() - debruijn.as_usize() + + self.current_index.as_usize() + - 1; + let universe = self.universe_indices[index].unwrap_or_else(|| { + for i in self.universe_indices.iter_mut().take(index + 1) { + *i = i.or_else(|| Some(infcx.create_next_universe())) + } + self.universe_indices[index].unwrap() + }); let p = ty::PlaceholderConst { universe, name: ty::BoundConst { var: bound_const, ty }, @@ -584,13 +643,14 @@ impl TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { } } +// The inverse of `BoundVarReplacer`: replaces placeholders with the bound vars from which they came. pub struct PlaceholderReplacer<'me, 'tcx> { - pub infcx: &'me InferCtxt<'me, 'tcx>, - pub mapped_regions: BTreeMap, - pub mapped_types: BTreeMap, - pub mapped_consts: BTreeMap, ty::BoundVar>, - pub universes_inverse: BTreeMap, - pub current_index: ty::DebruijnIndex, + infcx: &'me InferCtxt<'me, 'tcx>, + mapped_regions: BTreeMap, + mapped_types: BTreeMap, + mapped_consts: BTreeMap, ty::BoundVar>, + universe_indices: &'me Vec>, + current_index: ty::DebruijnIndex, } impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { @@ -599,7 +659,7 @@ impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { mapped_regions: BTreeMap, mapped_types: BTreeMap, mapped_consts: BTreeMap, ty::BoundVar>, - universes_inverse: BTreeMap, + universe_indices: &'me Vec>, value: T, ) -> T { let mut replacer = PlaceholderReplacer { @@ -607,7 +667,7 @@ impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { mapped_regions, mapped_types, mapped_consts, - universes_inverse, + universe_indices, current_index: ty::INNERMOST, }; value.super_fold_with(&mut replacer) @@ -648,13 +708,15 @@ impl TypeFolder<'tcx> for PlaceholderReplacer<'_, 'tcx> { let replace_var = self.mapped_regions.get(&p); match replace_var { Some(replace_var) => { - let db = self - .universes_inverse - .get(&p.universe) + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == 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)) + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + self.tcx().mk_region(ty::ReLateBound(db, *replace_var)) } None => r1, } @@ -673,13 +735,15 @@ impl TypeFolder<'tcx> for PlaceholderReplacer<'_, 'tcx> { let replace_var = self.mapped_types.get(&p); match replace_var { Some(replace_var) => { - let db = self - .universes_inverse - .get(&p.universe) + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == 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)) + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + self.tcx().mk_ty(ty::Bound(db, *replace_var)) } None => ty, } @@ -695,14 +759,16 @@ impl TypeFolder<'tcx> for PlaceholderReplacer<'_, 'tcx> { let replace_var = self.mapped_consts.get(&p); match replace_var { Some(replace_var) => { - let db = self - .universes_inverse - .get(&p.universe) + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let index = - ty::DebruijnIndex::from_u32(db.as_u32() + self.current_index.as_u32()); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); self.tcx() - .mk_const(ty::Const { val: ty::ConstKind::Bound(index, *replace_var), ty }) + .mk_const(ty::Const { val: ty::ConstKind::Bound(db, *replace_var), ty }) } None => ct, } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 004372dd8b74d..1f0f4a88a73a0 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -61,6 +61,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { error: false, cache: SsoHashMap::new(), anon_depth: 0, + universes: vec![], }; let result = value.fold_with(&mut normalizer); @@ -91,6 +92,7 @@ struct QueryNormalizer<'cx, 'tcx> { cache: SsoHashMap, Ty<'tcx>>, error: bool, anon_depth: usize, + universes: Vec>, } impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { @@ -98,6 +100,16 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { self.infcx.tcx } + fn fold_binder>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.universes.push(None); + let t = t.super_fold_with(self); + self.universes.pop(); + t + } + #[instrument(level = "debug", skip(self))] fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { if !ty.has_projections() { @@ -216,8 +228,19 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { 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 replaced = crate::traits::project::BoundVarReplacer::replace_bound_vars( + infcx, + &mut self.universes, + data, + ); + let (data, mapped_regions, mapped_types, mapped_consts) = match replaced { + Some(r) => r, + None => { + bug!("{:?} {:?}", data, self.universes); + //self.error = true; + //return ty.super_fold_with(self); + } + }; let data = data.super_fold_with(self); let mut orig_values = OriginalQueryValues::default(); @@ -263,7 +286,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { mapped_regions, mapped_types, mapped_consts, - universe_map, + &self.universes, normalized_ty, ) } From 09978bdcd154aeac01e06a801fe33d29e57a6bdc Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 13 Jul 2021 15:09:01 -0400 Subject: [PATCH 3/4] Conditionally call normalize_erasing_regions only if polymorhization is enabled --- compiler/rustc_middle/src/ty/layout.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index dbb5064c4f546..7e757d48209d7 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -2478,9 +2478,10 @@ impl<'tcx> ty::Instance<'tcx> { // `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping // track of a polymorphization `ParamEnv` to allow normalizing later. let mut sig = match *ty.kind() { - ty::FnDef(def_id, substs) => tcx + ty::FnDef(def_id, substs) if tcx.sess.opts.debugging_opts.polymorphize => tcx .normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id)) .subst(tcx, substs), + ty::FnDef(def_id, substs) => tcx.fn_sig(def_id).subst(tcx, substs), _ => unreachable!(), }; From cf001dc8894ae726de96f4db032f11c22f24c393 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Thu, 15 Jul 2021 10:41:35 -0400 Subject: [PATCH 4/4] Remove failed and review comments --- .../src/traits/project.rs | 142 +++++++----------- .../src/traits/query/normalize.rs | 26 ++-- 2 files changed, 69 insertions(+), 99 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 4f74f86564d15..98fde3707f70e 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -315,9 +315,11 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { fn fold>(&mut self, value: T) -> T { let value = self.selcx.infcx().resolve_vars_if_possible(value); - if value.has_escaping_bound_vars() { - bug!("Normalizing without wrapping in a `Binder`"); - } + assert!( + !value.has_escaping_bound_vars(), + "Normalizing {:?} without wrapping in a `Binder`", + value + ); if !value.has_projections() { value } else { value.fold_with(self) } } @@ -427,40 +429,36 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { // give up and fall back to pretending like we never tried! let infcx = self.selcx.infcx(); - let replaced = + let (data, mapped_regions, mapped_types, mapped_consts) = BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); - if let Some((data, mapped_regions, mapped_types, mapped_consts)) = replaced { - 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, - &self.universes, - normalized_ty, - ); - debug!( - ?self.depth, - ?ty, - ?normalized_ty, - obligations.len = ?self.obligations.len(), - "AssocTypeNormalizer: normalized type" - ); - normalized_ty - } else { - ty - } + 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, + &self.universes, + normalized_ty, + ); + debug!( + ?self.depth, + ?ty, + ?normalized_ty, + obligations.len = ?self.obligations.len(), + "AssocTypeNormalizer: normalized type" + ); + normalized_ty } _ => ty, @@ -491,14 +489,6 @@ pub struct BoundVarReplacer<'me, 'tcx> { // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy: // we don't actually create a universe until we see a bound var we have to replace. universe_indices: &'me mut Vec>, - // FIXME: So, this is a less-than-ideal solution to a problem we want to solve eventually. Ideally, we - // shouldn't need to worry about bound vars for which we haven't passed (`self.current_index`) - // or that we don't explicitly know about (`self.universe_indices`). This is true for - // `AssocTypeNormalizer` but not `QueryNormalizer` currently. When we can always know about - // any binding levels above us, we can remove this. (The alternative would be - // `outer_exclusive_binder`, but that only exists on `Ty`. Otherwise, we would have to visit - // through the `T`, which we specifically want to avoid not being lazy.) - failed: bool, } impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { @@ -508,12 +498,12 @@ impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { infcx: &'me InferCtxt<'me, 'tcx>, universe_indices: &'me mut Vec>, value: T, - ) -> Option<( + ) -> ( T, BTreeMap, BTreeMap, BTreeMap, ty::BoundVar>, - )> { + ) { let mapped_regions: BTreeMap = BTreeMap::new(); let mapped_types: BTreeMap = BTreeMap::new(); let mapped_consts: BTreeMap, ty::BoundVar> = BTreeMap::new(); @@ -525,14 +515,24 @@ impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { mapped_consts, current_index: ty::INNERMOST, universe_indices, - failed: false, }; let value = value.super_fold_with(&mut replacer); - (!replacer.failed).then(|| { - (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) - }) + (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) + } + + fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex { + let infcx = self.infcx; + let index = + self.universe_indices.len() - debruijn.as_usize() + self.current_index.as_usize() - 1; + let universe = self.universe_indices[index].unwrap_or_else(|| { + for i in self.universe_indices.iter_mut().take(index + 1) { + *i = i.or_else(|| Some(infcx.create_next_universe())) + } + self.universe_indices[index].unwrap() + }); + universe } } @@ -557,20 +557,10 @@ impl TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { if debruijn.as_usize() + 1 > self.current_index.as_usize() + self.universe_indices.len() => { - self.failed = true; - r + bug!("Bound vars outside of `self.universe_indices`"); } ty::ReLateBound(debruijn, br) if debruijn >= self.current_index => { - let infcx = self.infcx; - let index = self.universe_indices.len() - debruijn.as_usize() - + self.current_index.as_usize() - - 1; - let universe = self.universe_indices[index].unwrap_or_else(|| { - for i in self.universe_indices.iter_mut().take(index + 1) { - *i = i.or_else(|| Some(infcx.create_next_universe())) - } - self.universe_indices[index].unwrap() - }); + let universe = self.universe_for(debruijn); let p = ty::PlaceholderRegion { universe, name: br.kind }; self.mapped_regions.insert(p.clone(), br); self.infcx.tcx.mk_region(ty::RePlaceholder(p)) @@ -585,20 +575,10 @@ impl TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { if debruijn.as_usize() + 1 > self.current_index.as_usize() + self.universe_indices.len() => { - self.failed = true; - t + bug!("Bound vars outside of `self.universe_indices`"); } ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { - let infcx = self.infcx; - let index = self.universe_indices.len() - debruijn.as_usize() - + self.current_index.as_usize() - - 1; - let universe = self.universe_indices[index].unwrap_or_else(|| { - for i in self.universe_indices.iter_mut().take(index + 1) { - *i = i.or_else(|| Some(infcx.create_next_universe())) - } - self.universe_indices[index].unwrap() - }); + let universe = self.universe_for(debruijn); 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)) @@ -614,22 +594,12 @@ impl TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { if debruijn.as_usize() + 1 > self.current_index.as_usize() + self.universe_indices.len() => { - self.failed = true; - ct + bug!("Bound vars outside of `self.universe_indices`"); } ty::Const { val: ty::ConstKind::Bound(debruijn, bound_const), ty } if debruijn >= self.current_index => { - let infcx = self.infcx; - let index = self.universe_indices.len() - debruijn.as_usize() - + self.current_index.as_usize() - - 1; - let universe = self.universe_indices[index].unwrap_or_else(|| { - for i in self.universe_indices.iter_mut().take(index + 1) { - *i = i.or_else(|| Some(infcx.create_next_universe())) - } - self.universe_indices[index].unwrap() - }); + let universe = self.universe_for(debruijn); let p = ty::PlaceholderConst { universe, name: ty::BoundConst { var: bound_const, ty }, diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 1f0f4a88a73a0..94539eda0f89e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -226,21 +226,21 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { // to return `ty` because there are bound vars that we aren't yet handling in a more // complete way. + // `BoundVarReplacer` can't handle escaping bound vars. Ideally, we want this before even calling + // `QueryNormalizer`, but some const-generics tests pass escaping bound vars. + // Also, use `ty` so we get that sweet `outer_exclusive_binder` optimization + assert!(!ty.has_vars_bound_at_or_above(ty::DebruijnIndex::from_usize( + self.universes.len() + ))); + let tcx = self.infcx.tcx; let infcx = self.infcx; - let replaced = crate::traits::project::BoundVarReplacer::replace_bound_vars( - infcx, - &mut self.universes, - data, - ); - let (data, mapped_regions, mapped_types, mapped_consts) = match replaced { - Some(r) => r, - None => { - bug!("{:?} {:?}", data, self.universes); - //self.error = true; - //return ty.super_fold_with(self); - } - }; + let (data, mapped_regions, mapped_types, mapped_consts) = + crate::traits::project::BoundVarReplacer::replace_bound_vars( + infcx, + &mut self.universes, + data, + ); let data = data.super_fold_with(self); let mut orig_values = OriginalQueryValues::default();