Skip to content

Commit bf62436

Browse files
committed
Auto merge of #114602 - compiler-errors:rpit-outlives-sadness, r=oli-obk
Map RPIT duplicated lifetimes back to fn captured lifetimes Use the [`lifetime_mapping`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.OpaqueTy.html#structfield.lifetime_mapping) to map an RPIT's captured lifetimes back to the early- or late-bound lifetimes from its parent function. We may be going thru several layers of mapping, since opaques can be nested, so we introduce `TyCtxt::map_rpit_lifetime_to_fn_lifetime` to loop through several opaques worth of mapping, and handle turning it into a `ty::Region` as well. We can then use this instead of the identity substs for RPITs in `check_opaque_meets_bounds` to address #114285. We can then also use `map_rpit_lifetime_to_fn_lifetime` to properly install bidirectional-outlives predicates for both RPITs and RPITITs. This addresses #114601. I based this on #114574, but I don't actually know how much of that PR we still need, so some code may be redundant now... 🤷 --- Fixes #114597 Fixes #114579 Fixes #114285 Also fixes #114601, since it turns out we had other bugs with RPITITs and their duplicated lifetime params 😅. Supersedes #114574 r? `@oli-obk`
2 parents 617821a + 0adf704 commit bf62436

File tree

9 files changed

+194
-218
lines changed

9 files changed

+194
-218
lines changed

compiler/rustc_ast_lowering/src/lib.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -1663,11 +1663,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
16631663
);
16641664
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
16651665

1666-
let lifetime_mapping = if in_trait {
1667-
Some(&*self.arena.alloc_slice(&synthesized_lifetime_args))
1668-
} else {
1669-
None
1670-
};
1666+
let lifetime_mapping = self.arena.alloc_slice(&synthesized_lifetime_args);
16711667

16721668
let opaque_ty_item = hir::OpaqueTy {
16731669
generics: this.arena.alloc(hir::Generics {

compiler/rustc_hir/src/hir.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2675,7 +2675,7 @@ pub struct OpaqueTy<'hir> {
26752675
///
26762676
/// This mapping associated a captured lifetime (first parameter) with the new
26772677
/// early-bound lifetime that was generated for the opaque.
2678-
pub lifetime_mapping: Option<&'hir [(&'hir Lifetime, LocalDefId)]>,
2678+
pub lifetime_mapping: &'hir [(&'hir Lifetime, LocalDefId)],
26792679
/// Whether the opaque is a return-position impl trait (or async future)
26802680
/// originating from a trait method. This makes it so that the opaque is
26812681
/// lowered as an associated type.

compiler/rustc_hir_analysis/src/check/check.rs

+28-124
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,17 @@ fn check_opaque_meets_bounds<'tcx>(
407407
.build();
408408
let ocx = ObligationCtxt::new(&infcx);
409409

410-
let args = GenericArgs::identity_for_item(tcx, def_id.to_def_id());
410+
let args = match *origin {
411+
hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent) => {
412+
GenericArgs::identity_for_item(tcx, parent).extend_to(
413+
tcx,
414+
def_id.to_def_id(),
415+
|param, _| tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()).into(),
416+
)
417+
}
418+
hir::OpaqueTyOrigin::TyAlias { .. } => GenericArgs::identity_for_item(tcx, def_id),
419+
};
420+
411421
let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);
412422

413423
// `ReErased` regions appear in the "parent_args" of closures/generators.
@@ -468,9 +478,10 @@ fn check_opaque_meets_bounds<'tcx>(
468478
}
469479
}
470480
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
471-
for (key, mut ty) in infcx.take_opaque_types() {
481+
for (mut key, mut ty) in infcx.take_opaque_types() {
472482
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
473-
sanity_check_found_hidden_type(tcx, key, ty.hidden_type, defining_use_anchor, origin)?;
483+
key = infcx.resolve_vars_if_possible(key);
484+
sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
474485
}
475486
Ok(())
476487
}
@@ -479,8 +490,6 @@ fn sanity_check_found_hidden_type<'tcx>(
479490
tcx: TyCtxt<'tcx>,
480491
key: ty::OpaqueTypeKey<'tcx>,
481492
mut ty: ty::OpaqueHiddenType<'tcx>,
482-
defining_use_anchor: LocalDefId,
483-
origin: &hir::OpaqueTyOrigin,
484493
) -> Result<(), ErrorGuaranteed> {
485494
if ty.ty.is_ty_var() {
486495
// Nothing was actually constrained.
@@ -493,29 +502,23 @@ fn sanity_check_found_hidden_type<'tcx>(
493502
return Ok(());
494503
}
495504
}
505+
let strip_vars = |ty: Ty<'tcx>| {
506+
ty.fold_with(&mut BottomUpFolder {
507+
tcx,
508+
ty_op: |t| t,
509+
ct_op: |c| c,
510+
lt_op: |l| match l.kind() {
511+
RegionKind::ReVar(_) => tcx.lifetimes.re_erased,
512+
_ => l,
513+
},
514+
})
515+
};
496516
// Closures frequently end up containing erased lifetimes in their final representation.
497517
// These correspond to lifetime variables that never got resolved, so we patch this up here.
498-
ty.ty = ty.ty.fold_with(&mut BottomUpFolder {
499-
tcx,
500-
ty_op: |t| t,
501-
ct_op: |c| c,
502-
lt_op: |l| match l.kind() {
503-
RegionKind::ReVar(_) => tcx.lifetimes.re_erased,
504-
_ => l,
505-
},
506-
});
518+
ty.ty = strip_vars(ty.ty);
507519
// Get the hidden type.
508-
let mut hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args);
509-
if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
510-
if hidden_ty != ty.ty {
511-
hidden_ty = find_and_apply_rpit_args(
512-
tcx,
513-
hidden_ty,
514-
defining_use_anchor.to_def_id(),
515-
key.def_id.to_def_id(),
516-
)?;
517-
}
518-
}
520+
let hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args);
521+
let hidden_ty = strip_vars(hidden_ty);
519522

520523
// If the hidden types differ, emit a type mismatch diagnostic.
521524
if hidden_ty == ty.ty {
@@ -527,105 +530,6 @@ fn sanity_check_found_hidden_type<'tcx>(
527530
}
528531
}
529532

530-
/// In case it is in a nested opaque type, find that opaque type's
531-
/// usage in the function signature and use the generic arguments from the usage site.
532-
/// We need to do because RPITs ignore the lifetimes of the function,
533-
/// as they have their own copies of all the lifetimes they capture.
534-
/// So the only way to get the lifetimes represented in terms of the function,
535-
/// is to look how they are used in the function signature (or do some other fancy
536-
/// recording of this mapping at ast -> hir lowering time).
537-
///
538-
/// As an example:
539-
/// ```text
540-
/// trait Id {
541-
/// type Assoc;
542-
/// }
543-
/// impl<'a> Id for &'a () {
544-
/// type Assoc = &'a ();
545-
/// }
546-
/// fn func<'a>(x: &'a ()) -> impl Id<Assoc = impl Sized + 'a> { x }
547-
/// // desugared to
548-
/// fn func<'a>(x: &'a () -> Outer<'a> where <Outer<'a> as Id>::Assoc = Inner<'a> {
549-
/// // Note that in contrast to other nested items, RPIT type aliases can
550-
/// // access their parents' generics.
551-
///
552-
/// // hidden type is `&'aDupOuter ()`
553-
/// // During wfcheck the hidden type of `Inner<'aDupOuter>` is `&'a ()`, but
554-
/// // `typeof(Inner<'aDupOuter>) = &'aDupOuter ()`.
555-
/// // So we walk the signature of `func` to find the use of `Inner<'a>`
556-
/// // and then use that to replace the lifetimes in the hidden type, obtaining
557-
/// // `&'a ()`.
558-
/// type Outer<'aDupOuter> = impl Id<Assoc = Inner<'aDupOuter>>;
559-
///
560-
/// // hidden type is `&'aDupInner ()`
561-
/// type Inner<'aDupInner> = impl Sized + 'aDupInner;
562-
///
563-
/// x
564-
/// }
565-
/// ```
566-
fn find_and_apply_rpit_args<'tcx>(
567-
tcx: TyCtxt<'tcx>,
568-
mut hidden_ty: Ty<'tcx>,
569-
function: DefId,
570-
opaque: DefId,
571-
) -> Result<Ty<'tcx>, ErrorGuaranteed> {
572-
// Find use of the RPIT in the function signature and thus find the right args to
573-
// convert it into the parameter space of the function signature. This is needed,
574-
// because that's what `type_of` returns, against which we compare later.
575-
let ret = tcx.fn_sig(function).instantiate_identity().output();
576-
struct Visitor<'tcx> {
577-
tcx: TyCtxt<'tcx>,
578-
opaque: DefId,
579-
seen: FxHashSet<DefId>,
580-
}
581-
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for Visitor<'tcx> {
582-
type BreakTy = GenericArgsRef<'tcx>;
583-
584-
#[instrument(level = "trace", skip(self), ret)]
585-
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
586-
trace!("{:#?}", t.kind());
587-
match t.kind() {
588-
ty::Alias(ty::Opaque, alias) => {
589-
trace!(?alias.def_id);
590-
if alias.def_id == self.opaque {
591-
return ControlFlow::Break(alias.args);
592-
} else if self.seen.insert(alias.def_id) {
593-
for clause in self
594-
.tcx
595-
.explicit_item_bounds(alias.def_id)
596-
.iter_instantiated_copied(self.tcx, alias.args)
597-
{
598-
trace!(?clause);
599-
clause.visit_with(self)?;
600-
}
601-
}
602-
}
603-
ty::Alias(ty::Weak, alias) => {
604-
self.tcx
605-
.type_of(alias.def_id)
606-
.instantiate(self.tcx, alias.args)
607-
.visit_with(self)?;
608-
}
609-
_ => (),
610-
}
611-
612-
t.super_visit_with(self)
613-
}
614-
}
615-
if let ControlFlow::Break(args) =
616-
ret.visit_with(&mut Visitor { tcx, opaque, seen: Default::default() })
617-
{
618-
trace!(?args);
619-
trace!("expected: {hidden_ty:#?}");
620-
hidden_ty = ty::EarlyBinder::bind(hidden_ty).instantiate(tcx, args);
621-
trace!("expected: {hidden_ty:#?}");
622-
} else {
623-
tcx.sess
624-
.delay_span_bug(tcx.def_span(function), format!("{ret:?} does not contain {opaque:?}"));
625-
}
626-
Ok(hidden_ty)
627-
}
628-
629533
fn is_enum_of_nonnullable_ptr<'tcx>(
630534
tcx: TyCtxt<'tcx>,
631535
adt_def: AdtDef<'tcx>,

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: Some(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

0 commit comments

Comments
 (0)