From c1aa9bf84945642c0beec4838322502b88dccf03 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Sep 2022 23:13:12 +0000 Subject: [PATCH] Fix subst issues with RPITIT --- .../src/check/compare_method.rs | 37 ++++++++++++++++--- compiler/rustc_middle/src/ty/subst.rs | 19 ++++++++-- .../src/traits/project.rs | 5 ++- .../ui/impl-trait/in-trait/issue-102301.rs | 18 +++++++++ 4 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/impl-trait/in-trait/issue-102301.rs diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs index 5e5dbedb4bd..986d5bed39e 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_method.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -551,15 +551,40 @@ pub fn collect_trait_impl_trait_tys<'tcx>( let id_substs = InternalSubsts::identity_for_item(tcx, def_id); debug!(?id_substs, ?substs); let map: FxHashMap, ty::GenericArg<'tcx>> = - substs.iter().enumerate().map(|(index, arg)| (arg, id_substs[index])).collect(); + std::iter::zip(substs, id_substs).collect(); debug!(?map); + // NOTE(compiler-errors): RPITITs, like all other RPITs, have early-bound + // region substs that are synthesized during AST lowering. These are substs + // that are appended to the parent substs (trait and trait method). However, + // we're trying to infer the unsubstituted type value of the RPITIT inside + // the *impl*, so we can later use the impl's method substitutions to normalize + // an RPITIT to a concrete type. + // + // Due to the design of RPITITs, during AST lowering, we have no idea that + // an impl method is satisfying a trait method with RPITITs in it. Therefore, + // we don't have a list ofearly-bound region substs for the RPITIT in the impl. + // Since early region parameters are index-based, we can't just rebase these + // (trait method) early-bound region substs onto the impl, since region + // parameters are index-based, and there's no guarantee that the indices from + // the trait substs and impl substs line up -- so we subtract the number of + // trait substs and add the number of impl substs to *renumber* these early- + // bound regions to their corresponding indices in the impl's substitutions list. + // + // Also, we only need to account for a difference in trait and impl substs, + // since we previously enforce that the trait method and impl method have the + // same generics. + let num_trait_substs = trait_to_impl_substs.len(); + let num_impl_substs = tcx.generics_of(impl_m.container_id(tcx)).params.len(); let ty = tcx.fold_regions(ty, |region, _| { - if let ty::ReFree(_) = region.kind() { - map[®ion.into()].expect_region() - } else { - region - } + let ty::ReFree(_) = region.kind() else { return region; }; + let ty::ReEarlyBound(e) = map[®ion.into()].expect_region().kind() + else { bug!("expected ReFree to map to ReEarlyBound"); }; + tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { + def_id: e.def_id, + name: e.name, + index: (e.index as usize - num_trait_substs + num_impl_substs) as u32, + })) }); debug!(%ty); collected_tys.insert(def_id, ty); diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 36eb2ab5157..e552d3f1cc5 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -606,9 +606,21 @@ fn fold_binder>( fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { #[cold] #[inline(never)] - fn region_param_out_of_range(data: ty::EarlyBoundRegion) -> ! { + fn region_param_out_of_range(data: ty::EarlyBoundRegion, substs: &[GenericArg<'_>]) -> ! { bug!( - "Region parameter out of range when substituting in region {} (index={})", + "Region parameter out of range when substituting in region {} (index={}, substs = {:?})", + data.name, + data.index, + substs, + ) + } + + #[cold] + #[inline(never)] + fn region_param_invalid(data: ty::EarlyBoundRegion, other: GenericArgKind<'_>) -> ! { + bug!( + "Unexpected parameter {:?} when substituting in region {} (index={})", + other, data.name, data.index ) @@ -624,7 +636,8 @@ fn region_param_out_of_range(data: ty::EarlyBoundRegion) -> ! { let rk = self.substs.get(data.index as usize).map(|k| k.unpack()); match rk { Some(GenericArgKind::Lifetime(lt)) => self.shift_region_through_binders(lt), - _ => region_param_out_of_range(data), + Some(other) => region_param_invalid(data, other), + None => region_param_out_of_range(data, self.substs), } } _ => r, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 1ca9a1c1994..693c1728931 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2254,7 +2254,10 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( } let impl_fn_def_id = leaf_def.item.def_id; - let impl_fn_substs = obligation.predicate.substs.rebase_onto(tcx, trait_fn_def_id, data.substs); + // Rebase from {trait}::{fn}::{opaque} to {impl}::{fn}::{opaque}, + // since `data.substs` are the impl substs. + let impl_fn_substs = + obligation.predicate.substs.rebase_onto(tcx, tcx.parent(trait_fn_def_id), data.substs); let cause = ObligationCause::new( obligation.cause.span, diff --git a/src/test/ui/impl-trait/in-trait/issue-102301.rs b/src/test/ui/impl-trait/in-trait/issue-102301.rs new file mode 100644 index 00000000000..a93714a658e --- /dev/null +++ b/src/test/ui/impl-trait/in-trait/issue-102301.rs @@ -0,0 +1,18 @@ +// check-pass + +#![feature(return_position_impl_trait_in_trait)] +#![allow(incomplete_features)] + +trait Foo { + fn foo>(self) -> impl Foo; +} + +struct Bar; + +impl Foo for Bar { + fn foo>(self) -> impl Foo { + self + } +} + +fn main() {}