Skip to content

Commit 420ee16

Browse files
Install bidirectional outlives predicates for RPITITs (and RPITs) correctly
1 parent 8dcb8e0 commit 420ee16

File tree

4 files changed

+121
-88
lines changed

4 files changed

+121
-88
lines changed

compiler/rustc_hir_analysis/src/collect/predicates_of.rs

+28-66
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter};
22
use crate::bounds::Bounds;
33
use crate::collect::ItemCtxt;
44
use crate::constrained_generic_params as cgp;
5-
use hir::{HirId, Lifetime, Node};
5+
use hir::{HirId, Node};
66
use rustc_data_structures::fx::FxIndexSet;
77
use rustc_hir as hir;
88
use rustc_hir::def::DefKind;
99
use rustc_hir::def_id::{DefId, LocalDefId};
1010
use rustc_hir::intravisit::{self, Visitor};
1111
use rustc_middle::ty::{self, Ty, TyCtxt};
12-
use rustc_middle::ty::{GenericPredicates, Generics, ImplTraitInTraitData, ToPredicate};
12+
use rustc_middle::ty::{GenericPredicates, ImplTraitInTraitData, ToPredicate};
1313
use rustc_span::symbol::Ident;
14-
use rustc_span::{Span, Symbol, DUMMY_SP};
14+
use rustc_span::{Span, DUMMY_SP};
1515

1616
/// Returns a list of all type predicates (explicit and implicit) for the definition with
1717
/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
@@ -55,17 +55,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
5555
use rustc_hir::*;
5656

5757
match tcx.opt_rpitit_info(def_id.to_def_id()) {
58-
Some(ImplTraitInTraitData::Trait { opaque_def_id, fn_def_id }) => {
59-
let opaque_ty_id = tcx.hir().local_def_id_to_hir_id(opaque_def_id.expect_local());
60-
let opaque_ty_node = tcx.hir().get(opaque_ty_id);
61-
let Node::Item(&Item {
62-
kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping, .. }),
63-
..
64-
}) = opaque_ty_node
65-
else {
66-
bug!("unexpected {opaque_ty_node:?}")
67-
};
68-
58+
Some(ImplTraitInTraitData::Trait { fn_def_id, .. }) => {
6959
let mut predicates = Vec::new();
7060

7161
// RPITITs should inherit the predicates of their parent. This is
@@ -78,13 +68,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
7868

7969
// We also install bidirectional outlives predicates for the RPITIT
8070
// to keep the duplicates lifetimes from opaque lowering in sync.
71+
// We only need to compute bidirectional outlives for the duplicated
72+
// opaque lifetimes, which explains the slicing below.
8173
compute_bidirectional_outlives_predicates(
8274
tcx,
83-
def_id,
84-
lifetime_mapping.iter().map(|(lifetime, def_id)| {
85-
(**lifetime, (*def_id, lifetime.ident.name, lifetime.ident.span))
86-
}),
87-
tcx.generics_of(def_id.to_def_id()),
75+
&tcx.generics_of(def_id.to_def_id()).params
76+
[tcx.generics_of(fn_def_id).params.len()..],
8877
&mut predicates,
8978
);
9079

@@ -351,21 +340,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
351340
};
352341
debug!(?lifetimes);
353342

354-
let lifetime_mapping = std::iter::zip(lifetimes, ast_generics.params)
355-
.map(|(arg, dup)| {
356-
let hir::GenericArg::Lifetime(arg) = arg else { bug!() };
357-
(**arg, dup)
358-
})
359-
.filter(|(_, dup)| matches!(dup.kind, hir::GenericParamKind::Lifetime { .. }))
360-
.map(|(lifetime, dup)| (lifetime, (dup.def_id, dup.name.ident().name, dup.span)));
361-
362-
compute_bidirectional_outlives_predicates(
363-
tcx,
364-
def_id,
365-
lifetime_mapping,
366-
generics,
367-
&mut predicates,
368-
);
343+
compute_bidirectional_outlives_predicates(tcx, &generics.params, &mut predicates);
369344
debug!(?predicates);
370345
}
371346

@@ -379,41 +354,28 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
379354
/// enforce that these lifetimes stay in sync.
380355
fn compute_bidirectional_outlives_predicates<'tcx>(
381356
tcx: TyCtxt<'tcx>,
382-
item_def_id: LocalDefId,
383-
lifetime_mapping: impl Iterator<Item = (Lifetime, (LocalDefId, Symbol, Span))>,
384-
generics: &Generics,
357+
opaque_own_params: &[ty::GenericParamDef],
385358
predicates: &mut Vec<(ty::Clause<'tcx>, Span)>,
386359
) {
387-
let icx = ItemCtxt::new(tcx, item_def_id);
388-
389-
for (arg, (dup_def, name, span)) in lifetime_mapping {
390-
let orig_region = icx.astconv().ast_region_to_region(&arg, None);
391-
if !matches!(orig_region.kind(), ty::ReEarlyBound(..)) {
392-
// There is no late-bound lifetime to actually match up here, since the lifetime doesn't
393-
// show up in the opaque's parent's args.
394-
continue;
360+
for param in opaque_own_params {
361+
let orig_lifetime = tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local());
362+
if let ty::ReEarlyBound(..) = *orig_lifetime {
363+
let dup_lifetime = ty::Region::new_early_bound(
364+
tcx,
365+
ty::EarlyBoundRegion { def_id: param.def_id, index: param.index, name: param.name },
366+
);
367+
let span = tcx.def_span(param.def_id);
368+
predicates.push((
369+
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(orig_lifetime, dup_lifetime))
370+
.to_predicate(tcx),
371+
span,
372+
));
373+
predicates.push((
374+
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(dup_lifetime, orig_lifetime))
375+
.to_predicate(tcx),
376+
span,
377+
));
395378
}
396-
397-
let Some(dup_index) = generics.param_def_id_to_index(icx.tcx, dup_def.to_def_id()) else {
398-
bug!()
399-
};
400-
401-
let dup_region = ty::Region::new_early_bound(
402-
tcx,
403-
ty::EarlyBoundRegion { def_id: dup_def.to_def_id(), index: dup_index, name },
404-
);
405-
406-
predicates.push((
407-
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(orig_region, dup_region))
408-
.to_predicate(tcx),
409-
span,
410-
));
411-
412-
predicates.push((
413-
ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(dup_region, orig_region))
414-
.to_predicate(tcx),
415-
span,
416-
));
417379
}
418380
}
419381

compiler/rustc_middle/src/ty/context.rs

+69
Original file line numberDiff line numberDiff line change
@@ -1931,6 +1931,75 @@ impl<'tcx> TyCtxt<'tcx> {
19311931
)
19321932
}
19331933

1934+
/// Given the def-id of an early-bound lifetime on an RPIT corresponding to
1935+
/// a duplicated captured lifetime, map it back to the early- or late-bound
1936+
/// lifetime of the function from which it originally as captured. If it is
1937+
/// a late-bound lifetime, this will represent the liberated (`ReFree`) lifetime
1938+
/// of the signature.
1939+
// FIXME(RPITIT): if we ever synthesize new lifetimes for RPITITs and not just
1940+
// re-use the generics of the opaque, this function will need to be tweaked slightly.
1941+
pub fn map_rpit_lifetime_to_fn_lifetime(
1942+
self,
1943+
mut rpit_lifetime_param_def_id: LocalDefId,
1944+
) -> ty::Region<'tcx> {
1945+
debug_assert!(
1946+
matches!(self.def_kind(rpit_lifetime_param_def_id), DefKind::LifetimeParam),
1947+
"{rpit_lifetime_param_def_id:?} is a {}",
1948+
self.def_descr(rpit_lifetime_param_def_id.to_def_id())
1949+
);
1950+
1951+
loop {
1952+
let parent = self.local_parent(rpit_lifetime_param_def_id);
1953+
let hir::OpaqueTy { lifetime_mapping, .. } =
1954+
self.hir().get_by_def_id(parent).expect_item().expect_opaque_ty();
1955+
1956+
let Some((lifetime, _)) = lifetime_mapping
1957+
.iter()
1958+
.find(|(_, duplicated_param)| *duplicated_param == rpit_lifetime_param_def_id)
1959+
else {
1960+
bug!("duplicated lifetime param should be present");
1961+
};
1962+
1963+
match self.named_bound_var(lifetime.hir_id) {
1964+
Some(resolve_bound_vars::ResolvedArg::EarlyBound(ebv)) => {
1965+
let new_parent = self.parent(ebv);
1966+
1967+
// If we map to another opaque, then it should be a parent
1968+
// of the opaque we mapped from. Continue mapping.
1969+
if matches!(self.def_kind(new_parent), DefKind::OpaqueTy) {
1970+
debug_assert_eq!(self.parent(parent.to_def_id()), new_parent);
1971+
rpit_lifetime_param_def_id = ebv.expect_local();
1972+
continue;
1973+
}
1974+
1975+
let generics = self.generics_of(new_parent);
1976+
return ty::Region::new_early_bound(
1977+
self,
1978+
ty::EarlyBoundRegion {
1979+
def_id: ebv,
1980+
index: generics
1981+
.param_def_id_to_index(self, ebv)
1982+
.expect("early-bound var should be present in fn generics"),
1983+
name: self.hir().name(self.local_def_id_to_hir_id(ebv.expect_local())),
1984+
},
1985+
);
1986+
}
1987+
Some(resolve_bound_vars::ResolvedArg::LateBound(_, _, lbv)) => {
1988+
let new_parent = self.parent(lbv);
1989+
return ty::Region::new_free(
1990+
self,
1991+
new_parent,
1992+
ty::BoundRegionKind::BrNamed(
1993+
lbv,
1994+
self.hir().name(self.local_def_id_to_hir_id(lbv.expect_local())),
1995+
),
1996+
);
1997+
}
1998+
_ => bug!(),
1999+
}
2000+
}
2001+
}
2002+
19342003
/// Whether the `def_id` counts as const fn in the current crate, considering all active
19352004
/// feature gates
19362005
pub fn is_const_fn(self, def_id: DefId) -> bool {

compiler/rustc_ty_utils/src/implied_bounds.rs

+13-22
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use rustc_data_structures::fx::FxHashMap;
22
use rustc_hir as hir;
33
use rustc_hir::def::DefKind;
44
use rustc_hir::def_id::LocalDefId;
5-
use rustc_middle::middle::resolve_bound_vars as rbv;
65
use rustc_middle::query::Providers;
76
use rustc_middle::ty::{self, Ty, TyCtxt};
87
use rustc_span::Span;
@@ -52,9 +51,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
5251
tcx.arena.alloc_from_iter(tys.into_iter().map(|ty| (ty, impl_spans.next().unwrap())))
5352
}
5453
DefKind::AssocTy if let Some(data) = tcx.opt_rpitit_info(def_id.to_def_id()) => match data {
55-
ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id } => {
56-
let hir::OpaqueTy { lifetime_mapping, .. } =
57-
*tcx.hir().expect_item(opaque_def_id.expect_local()).expect_opaque_ty();
54+
ty::ImplTraitInTraitData::Trait { fn_def_id, .. } => {
5855
// We need to remap all of the late-bound lifetimes in theassumed wf types
5956
// of the fn (which are represented as ReFree) to the early-bound lifetimes
6057
// of the RPITIT (which are represented by ReEarlyBound owned by the opaque).
@@ -66,28 +63,22 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
6663
// predicates we insert in the `explicit_predicates_of` query for RPITITs.
6764
let mut mapping = FxHashMap::default();
6865
let generics = tcx.generics_of(def_id);
69-
for &(lifetime, new_early_bound_def_id) in
70-
lifetime_mapping
71-
{
72-
if let Some(rbv::ResolvedArg::LateBound(_, _, def_id)) =
73-
tcx.named_bound_var(lifetime.hir_id)
74-
{
75-
let name = tcx.hir().name(lifetime.hir_id);
76-
let index = generics
77-
.param_def_id_to_index(tcx, new_early_bound_def_id.to_def_id())
78-
.unwrap();
66+
67+
// For each captured opaque lifetime, if it's late-bound (`ReFree` in this case,
68+
// since it has been liberated), map it back to the early-bound lifetime of
69+
// the GAT. Since RPITITs also have all of the fn's generics, we slice only
70+
// the end of the list corresponding to the opaque's generics.
71+
for param in &generics.params[tcx.generics_of(fn_def_id).params.len()..] {
72+
let orig_lt = tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local());
73+
if matches!(*orig_lt, ty::ReFree(..)) {
7974
mapping.insert(
80-
ty::Region::new_free(
81-
tcx,
82-
fn_def_id,
83-
ty::BoundRegionKind::BrNamed(def_id, name),
84-
),
75+
orig_lt,
8576
ty::Region::new_early_bound(
8677
tcx,
8778
ty::EarlyBoundRegion {
88-
def_id: new_early_bound_def_id.to_def_id(),
89-
index,
90-
name,
79+
def_id: param.def_id,
80+
index: param.index,
81+
name: param.name,
9182
},
9283
),
9384
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// check-pass
2+
3+
#![feature(return_position_impl_trait_in_trait)]
4+
5+
trait Foo {
6+
fn early<'a, T: 'a>(x: &'a T) -> impl Iterator<Item = impl Into<&'a T>>;
7+
8+
fn late<'a, T>(x: &'a T) -> impl Iterator<Item = impl Into<&'a T>>;
9+
}
10+
11+
fn main() {}

0 commit comments

Comments
 (0)