1
Fork 0

Remap impl-trait lifetimes on HIR instead of AST lowering.

This commit is contained in:
Camille GILLOT 2024-08-20 00:48:43 +00:00
parent 298c7462c3
commit b6e1214ac0
35 changed files with 508 additions and 597 deletions

View file

@ -1302,7 +1302,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
}
}
#[instrument(level = "debug", skip(tcx))]
#[instrument(level = "debug", skip(tcx), ret)]
fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFnSig<'_>> {
use rustc_hir::Node::*;
use rustc_hir::*;

View file

@ -426,6 +426,21 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
});
}
if let Node::OpaqueTy(&hir::OpaqueTy { .. }) = node {
assert!(own_params.is_empty());
let lifetimes = tcx.opaque_captured_lifetimes(def_id);
debug!(?lifetimes);
own_params.extend(lifetimes.iter().map(|&(_, param)| ty::GenericParamDef {
name: tcx.item_name(param.to_def_id()),
index: next_index(),
def_id: param.to_def_id(),
pure_wrt_drop: false,
kind: ty::GenericParamDefKind::Lifetime,
}))
}
let param_def_id_to_index =
own_params.iter().map(|param| (param.def_id, param.index)).collect();

View file

@ -329,13 +329,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
// We create bi-directional Outlives predicates between the original
// and the duplicated parameter, to ensure that they do not get out of sync.
if let Node::OpaqueTy(..) = node {
let opaque_ty_node = tcx.parent_hir_node(hir_id);
let Node::Ty(&hir::Ty { kind: TyKind::OpaqueDef(_, lifetimes), .. }) = opaque_ty_node
else {
bug!("unexpected {opaque_ty_node:?}")
};
debug!(?lifetimes);
compute_bidirectional_outlives_predicates(tcx, &generics.own_params, &mut predicates);
debug!(?predicates);
}

View file

@ -6,12 +6,14 @@
//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file
//! is also responsible for assigning their semantics to implicit lifetimes in trait objects.
use core::ops::ControlFlow;
use std::cell::RefCell;
use std::fmt;
use std::ops::ControlFlow;
use rustc_ast::visit::walk_list;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::sorted_map::SortedMap;
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{self, Visitor};
@ -25,7 +27,7 @@ use rustc_middle::query::Providers;
use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor};
use rustc_middle::{bug, span_bug};
use rustc_span::Span;
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::def_id::{DefId, LocalDefId, LocalDefIdMap};
use rustc_span::symbol::{Ident, sym};
use tracing::{debug, debug_span, instrument};
@ -80,6 +82,9 @@ struct NamedVarMap {
// - trait refs
// - bound types (like `T` in `for<'a> T<'a>: Foo`)
late_bound_vars: ItemLocalMap<Vec<ty::BoundVariableKind>>,
// List captured variables for each opaque type.
opaque_captured_lifetimes: LocalDefIdMap<Vec<(ResolvedArg, LocalDefId)>>,
}
struct BoundVarContext<'a, 'tcx> {
@ -147,6 +152,20 @@ enum Scope<'a> {
s: ScopeRef<'a>,
},
/// Resolve the lifetimes in the bounds to the lifetime defs in the generics.
/// `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to
/// `type MyAnonTy<'b> = impl MyTrait<'b>;`
/// ^ ^ this gets resolved in the scope of
/// the opaque_ty generics
Opaque {
/// The opaque type we are traversing.
def_id: LocalDefId,
/// Mapping from each captured lifetime `'a` to the duplicate generic parameter `'b`.
captures: &'a RefCell<FxIndexMap<ResolvedArg, LocalDefId>>,
s: ScopeRef<'a>,
},
/// Disallows capturing late-bound vars from parent scopes.
///
/// This is necessary for something like `for<T> [(); { /* references T */ }]:`,
@ -192,6 +211,12 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
.field("where_bound_origin", where_bound_origin)
.field("s", &"..")
.finish(),
Scope::Opaque { captures, def_id, s: _ } => f
.debug_struct("Opaque")
.field("def_id", def_id)
.field("captures", &captures.borrow())
.field("s", &"..")
.finish(),
Scope::Body { id, s: _ } => {
f.debug_struct("Body").field("id", id).field("s", &"..").finish()
}
@ -226,6 +251,12 @@ pub(crate) fn provide(providers: &mut Providers) {
is_late_bound_map,
object_lifetime_default,
late_bound_vars_map: |tcx, id| &tcx.resolve_bound_vars(id).late_bound_vars,
opaque_captured_lifetimes: |tcx, id| {
&tcx.resolve_bound_vars(tcx.local_def_id_to_hir_id(id).owner)
.opaque_captured_lifetimes
.get(&id)
.map_or(&[][..], |x| &x[..])
},
..*providers
};
@ -236,8 +267,11 @@ pub(crate) fn provide(providers: &mut Providers) {
/// `named_variable_map`, `is_late_bound_map`, etc.
#[instrument(level = "debug", skip(tcx))]
fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBoundVars {
let mut named_variable_map =
NamedVarMap { defs: Default::default(), late_bound_vars: Default::default() };
let mut named_variable_map = NamedVarMap {
defs: Default::default(),
late_bound_vars: Default::default(),
opaque_captured_lifetimes: Default::default(),
};
let mut visitor = BoundVarContext {
tcx,
map: &mut named_variable_map,
@ -264,13 +298,16 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou
let defs = named_variable_map.defs.into_sorted_stable_ord();
let late_bound_vars = named_variable_map.late_bound_vars.into_sorted_stable_ord();
let opaque_captured_lifetimes = named_variable_map.opaque_captured_lifetimes;
let rl = ResolveBoundVars {
defs: SortedMap::from_presorted_elements(defs),
late_bound_vars: SortedMap::from_presorted_elements(late_bound_vars),
opaque_captured_lifetimes,
};
debug!(?rl.defs);
debug!(?rl.late_bound_vars);
debug!(?rl.opaque_captured_lifetimes);
rl
}
@ -306,6 +343,26 @@ fn generic_param_def_as_bound_arg(param: &ty::GenericParamDef) -> ty::BoundVaria
}
}
/// Whether this opaque always captures lifetimes in scope.
/// Right now, this is all RPITIT and TAITs, and when `lifetime_capture_rules_2024`
/// is enabled. We don't check the span of the edition, since this is done
/// on a per-opaque basis to account for nested opaques.
fn opaque_captures_all_in_scope_lifetimes<'tcx>(
tcx: TyCtxt<'tcx>,
opaque: &'tcx hir::OpaqueTy<'tcx>,
) -> bool {
match opaque.origin {
// if the opaque has the `use<...>` syntax, the user is telling us that they only want
// to account for those lifetimes, so do not try to be clever.
_ if opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..))) => false,
hir::OpaqueTyOrigin::AsyncFn { .. } | hir::OpaqueTyOrigin::TyAlias { .. } => true,
_ if tcx.features().lifetime_capture_rules_2024() || opaque.span.at_least_rust_2024() => {
true
}
hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => in_trait_or_impl.is_some(),
}
}
impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
/// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref.
fn poly_trait_ref_binder_info(&mut self) -> (Vec<ty::BoundVariableKind>, BinderScopeType) {
@ -317,7 +374,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
break (vec![], BinderScopeType::Normal);
}
Scope::ObjectLifetimeDefault { s, .. } | Scope::LateBoundary { s, .. } => {
Scope::Opaque { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
| Scope::LateBoundary { s, .. } => {
scope = s;
}
@ -488,29 +547,100 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
}
}
/// Resolve the lifetimes that are applied to the opaque type.
/// These are resolved in the current scope.
/// `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to
/// `fn foo<'a>() -> MyAnonTy<'a> { ... }`
/// ^ ^this gets resolved in the current scope
#[instrument(level = "debug", skip(self))]
fn visit_opaque_ty(&mut self, opaque: &'tcx rustc_hir::OpaqueTy<'tcx>) {
// We want to start our early-bound indices at the end of the parent scope,
// not including any parent `impl Trait`s.
let mut bound_vars = FxIndexMap::default();
debug!(?opaque.generics.params);
for param in opaque.generics.params {
let arg = ResolvedArg::early(param);
bound_vars.insert(param.def_id, arg);
let mut captures = FxIndexMap::default();
let capture_all_in_scope_lifetimes =
opaque_captures_all_in_scope_lifetimes(self.tcx, opaque);
if capture_all_in_scope_lifetimes {
let mut create_def_for_duplicated_param = |original_lifetime: LocalDefId, def| {
captures.entry(def).or_insert_with(|| {
let name = self.tcx.item_name(original_lifetime.to_def_id());
let span = self.tcx.def_span(original_lifetime);
let feed = self.tcx.create_def(opaque.def_id, name, DefKind::LifetimeParam);
feed.def_span(span);
feed.def_ident_span(Some(span));
feed.def_id()
});
};
// We list scopes outwards, this causes us to see lifetime parameters in reverse
// declaration order. In order to make it consistent with what `generics_of` might
// give, we will reverse the IndexMap after early captures.
let mut scope = self.scope;
loop {
match *scope {
Scope::Binder { ref bound_vars, s, .. } => {
for (&original_lifetime, &(mut def)) in bound_vars.iter().rev() {
if let DefKind::LifetimeParam = self.tcx.def_kind(original_lifetime) {
if let Err(guar) =
self.check_lifetime_is_capturable(opaque.def_id, def, None)
{
def = ResolvedArg::Error(guar);
}
create_def_for_duplicated_param(original_lifetime, def);
}
}
scope = s;
}
Scope::Root { mut opt_parent_item } => {
while let Some(parent_item) = opt_parent_item {
let parent_generics = self.tcx.generics_of(parent_item);
for param in parent_generics.own_params.iter().rev() {
if let ty::GenericParamDefKind::Lifetime = param.kind {
create_def_for_duplicated_param(
param.def_id.expect_local(),
ResolvedArg::EarlyBound(param.def_id.expect_local()),
);
}
}
opt_parent_item = parent_generics.parent.and_then(DefId::as_local);
}
break;
}
Scope::Opaque { captures: outer_captures, .. } => {
for (_, &duplicated_param) in outer_captures.borrow().iter().rev() {
create_def_for_duplicated_param(
duplicated_param,
ResolvedArg::EarlyBound(duplicated_param),
);
}
break;
}
Scope::Body { .. } => {
bug!("{:?}", scope)
}
Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. }
| Scope::LateBoundary { s, .. } => {
scope = s;
}
}
}
captures.reverse();
}
let hir_id = self.tcx.local_def_id_to_hir_id(opaque.def_id);
let scope = Scope::Binder {
hir_id,
bound_vars,
s: self.scope,
scope_type: BinderScopeType::Normal,
where_bound_origin: None,
};
let captures = RefCell::new(captures);
let scope = Scope::Opaque { captures: &captures, def_id: opaque.def_id, s: self.scope };
self.with(scope, |this| {
let scope = Scope::TraitRefBoundary { s: this.scope };
this.with(scope, |this| intravisit::walk_opaque_ty(this, opaque))
})
});
let captures = captures.into_inner().into_iter().collect();
self.map.opaque_captured_lifetimes.insert(opaque.def_id, captures);
}
#[instrument(level = "debug", skip(self))]
@ -685,66 +815,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
};
self.with(scope, |this| this.visit_ty(mt.ty));
}
hir::TyKind::OpaqueDef(opaque_ty, lifetimes) => {
hir::TyKind::OpaqueDef(opaque_ty) => {
self.visit_opaque_ty(opaque_ty);
// Resolve the lifetimes in the bounds to the lifetime defs in the generics.
// `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to
// `type MyAnonTy<'b> = impl MyTrait<'b>;`
// ^ ^ this gets resolved in the scope of
// the opaque_ty generics
// Resolve the lifetimes that are applied to the opaque type.
// These are resolved in the current scope.
// `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to
// `fn foo<'a>() -> MyAnonTy<'a> { ... }`
// ^ ^this gets resolved in the current scope
for lifetime in lifetimes {
let hir::GenericArg::Lifetime(lifetime) = lifetime else { continue };
self.visit_lifetime(lifetime);
// Check for predicates like `impl for<'a> Trait<impl OtherTrait<'a>>`
// and ban them. Type variables instantiated inside binders aren't
// well-supported at the moment, so this doesn't work.
// In the future, this should be fixed and this error should be removed.
let def = self.map.defs.get(&lifetime.hir_id.local_id).copied();
let Some(ResolvedArg::LateBound(_, _, lifetime_def_id)) = def else { continue };
let lifetime_hir_id = self.tcx.local_def_id_to_hir_id(lifetime_def_id);
let bad_place = match self.tcx.hir_node(self.tcx.parent_hir_id(lifetime_hir_id))
{
// Opaques do not declare their own lifetimes, so if a lifetime comes from an opaque
// it must be a reified late-bound lifetime from a trait goal.
hir::Node::OpaqueTy(_) => "higher-ranked lifetime from outer `impl Trait`",
// Other items are fine.
hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => {
continue;
}
hir::Node::Ty(hir::Ty { kind: hir::TyKind::BareFn(_), .. }) => {
"higher-ranked lifetime from function pointer"
}
hir::Node::Ty(hir::Ty { kind: hir::TyKind::TraitObject(..), .. }) => {
"higher-ranked lifetime from `dyn` type"
}
_ => "higher-ranked lifetime",
};
let (span, label) = if lifetime.ident.span == self.tcx.def_span(lifetime_def_id)
{
(opaque_ty.span, Some(opaque_ty.span))
} else {
(lifetime.ident.span, None)
};
// Ensure that the parent of the def is an item, not HRTB
self.tcx.dcx().emit_err(errors::OpaqueCapturesHigherRankedLifetime {
span,
label,
decl_span: self.tcx.def_span(lifetime_def_id),
bad_place,
});
self.uninsert_lifetime_on_error(lifetime, def.unwrap());
}
}
_ => intravisit::walk_ty(self, ty),
}
@ -1129,6 +1201,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
let mut scope = self.scope;
let mut outermost_body = None;
let mut crossed_late_boundary = None;
let mut opaque_capture_scopes = vec![];
let result = loop {
match *scope {
Scope::Body { id, s } => {
@ -1204,6 +1277,12 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
scope = s;
}
Scope::Opaque { captures, def_id, s } => {
opaque_capture_scopes.push((def_id, captures));
late_depth = 0;
scope = s;
}
Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. } => {
@ -1218,6 +1297,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
};
if let Some(mut def) = result {
def = self.remap_opaque_captures(opaque_capture_scopes, def, lifetime_ref.ident);
if let ResolvedArg::EarlyBound(..) = def {
// Do not free early-bound regions, only late-bound ones.
} else if let ResolvedArg::LateBound(_, _, param_def_id) = def
@ -1291,6 +1372,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
Scope::Root { .. } => break,
Scope::Binder { s, .. }
| Scope::Body { s, .. }
| Scope::Opaque { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. }
@ -1306,6 +1388,78 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
);
}
/// Check for predicates like `impl for<'a> Trait<impl OtherTrait<'a>>`
/// and ban them. Type variables instantiated inside binders aren't
/// well-supported at the moment, so this doesn't work.
/// In the future, this should be fixed and this error should be removed.
fn check_lifetime_is_capturable(
&self,
opaque_def_id: LocalDefId,
lifetime: ResolvedArg,
span: Option<Span>,
) -> Result<(), ErrorGuaranteed> {
let ResolvedArg::LateBound(_, _, lifetime_def_id) = lifetime else { return Ok(()) };
let lifetime_hir_id = self.tcx.local_def_id_to_hir_id(lifetime_def_id);
let bad_place = match self.tcx.hir_node(self.tcx.parent_hir_id(lifetime_hir_id)) {
// Opaques do not declare their own lifetimes, so if a lifetime comes from an opaque
// it must be a reified late-bound lifetime from a trait goal.
hir::Node::OpaqueTy(_) => "higher-ranked lifetime from outer `impl Trait`",
// Other items are fine.
hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => return Ok(()),
hir::Node::Ty(hir::Ty { kind: hir::TyKind::BareFn(_), .. }) => {
"higher-ranked lifetime from function pointer"
}
hir::Node::Ty(hir::Ty { kind: hir::TyKind::TraitObject(..), .. }) => {
"higher-ranked lifetime from `dyn` type"
}
_ => "higher-ranked lifetime",
};
let decl_span = self.tcx.def_span(lifetime_def_id);
let (span, label) = if let Some(span) = span
&& span != decl_span
{
(span, None)
} else {
let opaque_span = self.tcx.def_span(opaque_def_id);
(opaque_span, Some(opaque_span))
};
// Ensure that the parent of the def is an item, not HRTB
let guar = self.tcx.dcx().emit_err(errors::OpaqueCapturesHigherRankedLifetime {
span,
label,
decl_span,
bad_place,
});
Err(guar)
}
fn remap_opaque_captures(
&self,
opaque_capture_scopes: Vec<(LocalDefId, &RefCell<FxIndexMap<ResolvedArg, LocalDefId>>)>,
mut lifetime: ResolvedArg,
ident: Ident,
) -> ResolvedArg {
for (opaque_def_id, captures) in opaque_capture_scopes.into_iter().rev() {
if let Err(guar) =
self.check_lifetime_is_capturable(opaque_def_id, lifetime, Some(ident.span))
{
return ResolvedArg::Error(guar);
}
let mut captures = captures.borrow_mut();
let remapped = *captures.entry(lifetime).or_insert_with(|| {
let feed = self.tcx.create_def(opaque_def_id, ident.name, DefKind::LifetimeParam);
feed.def_span(ident.span);
feed.def_ident_span(Some(ident.span));
feed.def_id()
});
lifetime = ResolvedArg::EarlyBound(remapped);
}
lifetime
}
fn resolve_type_ref(&mut self, param_def_id: LocalDefId, hir_id: HirId) {
// Walk up the scope chain, tracking the number of fn scopes
// that we pass through, until we find a lifetime with the
@ -1345,6 +1499,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
}
Scope::ObjectLifetimeDefault { s, .. }
| Scope::Opaque { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. } => {
scope = s;
@ -1425,6 +1580,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
Scope::Root { .. } => break,
Scope::Binder { s, .. }
| Scope::Body { s, .. }
| Scope::Opaque { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. }
@ -1501,6 +1657,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
Scope::Binder { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
| Scope::Opaque { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. }
| Scope::LateBoundary { s, .. } => {
@ -1786,7 +1943,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
let mut late_depth = 0;
let mut scope = self.scope;
let lifetime = loop {
let mut opaque_capture_scopes = vec![];
let mut lifetime = loop {
match *scope {
Scope::Binder { s, scope_type, .. } => {
match scope_type {
@ -1800,7 +1958,15 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
Scope::Body { .. } | Scope::ObjectLifetimeDefault { lifetime: None, .. } => return,
Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l,
Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => {
break l.shifted(late_depth);
}
Scope::Opaque { captures, def_id, s } => {
opaque_capture_scopes.push((def_id, captures));
late_depth = 0;
scope = s;
}
Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. }
@ -1809,7 +1975,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
}
}
};
self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth));
lifetime = self.remap_opaque_captures(opaque_capture_scopes, lifetime, lifetime_ref.ident);
self.insert_lifetime(lifetime_ref, lifetime);
}
#[instrument(level = "debug", skip(self))]
@ -1818,18 +1987,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
self.map.defs.insert(lifetime_ref.hir_id.local_id, def);
}
/// Sometimes we resolve a lifetime, but later find that it is an
/// error (esp. around impl trait). In that case, we remove the
/// entry into `map.defs` so as not to confuse later code.
fn uninsert_lifetime_on_error(
&mut self,
lifetime_ref: &'tcx hir::Lifetime,
bad_def: ResolvedArg,
) {
let old_value = self.map.defs.remove(&lifetime_ref.hir_id.local_id);
assert_eq!(old_value, Some(bad_def));
}
// When we have a return type notation type in a where clause, like
// `where <T as Trait>::method(..): Send`, we need to introduce new bound
// vars to the existing where clause's binder, to represent the lifetimes
@ -2013,18 +2170,22 @@ fn is_late_bound_map(
tcx: TyCtxt<'_>,
owner_id: hir::OwnerId,
) -> Option<&FxIndexSet<hir::ItemLocalId>> {
let decl = tcx.hir().fn_decl_by_hir_id(owner_id.into())?;
let sig = tcx.hir().fn_sig_by_hir_id(owner_id.into())?;
let generics = tcx.hir().get_generics(owner_id.def_id)?;
let mut late_bound = FxIndexSet::default();
let mut constrained_by_input = ConstrainedCollector { regions: Default::default(), tcx };
for arg_ty in decl.inputs {
for arg_ty in sig.decl.inputs {
constrained_by_input.visit_ty(arg_ty);
}
let mut appears_in_output = AllCollector::default();
intravisit::walk_fn_ret_ty(&mut appears_in_output, &decl.output);
let mut appears_in_output =
AllCollector { tcx, has_fully_capturing_opaque: false, regions: Default::default() };
intravisit::walk_fn_ret_ty(&mut appears_in_output, &sig.decl.output);
if appears_in_output.has_fully_capturing_opaque {
appears_in_output.regions.extend(generics.params.iter().map(|param| param.def_id));
}
debug!(?constrained_by_input.regions);
@ -2032,7 +2193,8 @@ fn is_late_bound_map(
//
// Subtle point: because we disallow nested bindings, we can just
// ignore binders here and scrape up all names we see.
let mut appears_in_where_clause = AllCollector::default();
let mut appears_in_where_clause =
AllCollector { tcx, has_fully_capturing_opaque: true, regions: Default::default() };
appears_in_where_clause.visit_generics(generics);
debug!(?appears_in_where_clause.regions);
@ -2198,17 +2360,26 @@ fn is_late_bound_map(
}
}
#[derive(Default)]
struct AllCollector {
struct AllCollector<'tcx> {
tcx: TyCtxt<'tcx>,
has_fully_capturing_opaque: bool,
regions: FxHashSet<LocalDefId>,
}
impl<'v> Visitor<'v> for AllCollector {
impl<'v> Visitor<'v> for AllCollector<'v> {
fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) {
if let hir::LifetimeName::Param(def_id) = lifetime_ref.res {
self.regions.insert(def_id);
}
}
fn visit_opaque_ty(&mut self, opaque: &'v hir::OpaqueTy<'v>) {
if !self.has_fully_capturing_opaque {
self.has_fully_capturing_opaque =
opaque_captures_all_in_scope_lifetimes(self.tcx, opaque);
}
intravisit::walk_opaque_ty(self, opaque);
}
}
}

View file

@ -294,13 +294,23 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
lifetime: &hir::Lifetime,
reason: RegionInferReason<'_>,
) -> ty::Region<'tcx> {
if let Some(resolved) = self.tcx().named_bound_var(lifetime.hir_id) {
self.lower_resolved_lifetime(resolved)
} else {
self.re_infer(lifetime.ident.span, reason)
}
}
/// Lower a lifetime from the HIR to our internal notion of a lifetime called a *region*.
#[instrument(level = "debug", skip(self), ret)]
pub fn lower_resolved_lifetime(&self, resolved: rbv::ResolvedArg) -> ty::Region<'tcx> {
let tcx = self.tcx();
let lifetime_name = |def_id| tcx.hir().name(tcx.local_def_id_to_hir_id(def_id));
match tcx.named_bound_var(lifetime.hir_id) {
Some(rbv::ResolvedArg::StaticLifetime) => tcx.lifetimes.re_static,
match resolved {
rbv::ResolvedArg::StaticLifetime => tcx.lifetimes.re_static,
Some(rbv::ResolvedArg::LateBound(debruijn, index, def_id)) => {
rbv::ResolvedArg::LateBound(debruijn, index, def_id) => {
let name = lifetime_name(def_id);
let br = ty::BoundRegion {
var: ty::BoundVar::from_u32(index),
@ -309,7 +319,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ty::Region::new_bound(tcx, debruijn, br)
}
Some(rbv::ResolvedArg::EarlyBound(def_id)) => {
rbv::ResolvedArg::EarlyBound(def_id) => {
let name = tcx.hir().ty_param_name(def_id);
let item_def_id = tcx.hir().ty_param_owner(def_id);
let generics = tcx.generics_of(item_def_id);
@ -317,7 +327,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ty::Region::new_early_param(tcx, ty::EarlyParamRegion { index, name })
}
Some(rbv::ResolvedArg::Free(scope, id)) => {
rbv::ResolvedArg::Free(scope, id) => {
let name = lifetime_name(id);
ty::Region::new_late_param(
tcx,
@ -328,9 +338,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// (*) -- not late-bound, won't change
}
Some(rbv::ResolvedArg::Error(guar)) => ty::Region::new_error(tcx, guar),
None => self.re_infer(lifetime.ident.span, reason),
rbv::ResolvedArg::Error(guar) => ty::Region::new_error(tcx, guar),
}
}
@ -2094,13 +2102,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
self.lower_path(opt_self_ty, path, hir_ty.hir_id, false)
}
&hir::TyKind::OpaqueDef(opaque_ty, lifetimes) => {
let local_def_id = opaque_ty.def_id;
&hir::TyKind::OpaqueDef(opaque_ty) => {
// If this is an RPITIT and we are using the new RPITIT lowering scheme, we
// generate the def_id of an associated type for the trait and return as
// type a projection.
match opaque_ty.origin {
let in_trait = match opaque_ty.origin {
hir::OpaqueTyOrigin::FnReturn {
in_trait_or_impl: Some(hir::RpitContext::Trait),
..
@ -2108,11 +2114,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
| hir::OpaqueTyOrigin::AsyncFn {
in_trait_or_impl: Some(hir::RpitContext::Trait),
..
} => self.lower_opaque_ty(
tcx.associated_type_for_impl_trait_in_trait(local_def_id).to_def_id(),
lifetimes,
true,
),
} => true,
hir::OpaqueTyOrigin::FnReturn {
in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl),
..
@ -2121,10 +2123,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl),
..
}
| hir::OpaqueTyOrigin::TyAlias { .. } => {
self.lower_opaque_ty(local_def_id.to_def_id(), lifetimes, false)
}
}
| hir::OpaqueTyOrigin::TyAlias { .. } => false,
};
self.lower_opaque_ty(opaque_ty.def_id, in_trait)
}
// If we encounter a type relative path with RTN generics, then it must have
// *not* gone through `lower_ty_maybe_return_type_notation`, and therefore
@ -2264,40 +2266,34 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
/// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR.
#[instrument(level = "debug", skip_all, ret)]
fn lower_opaque_ty(
&self,
def_id: DefId,
lifetimes: &[hir::GenericArg<'_>],
in_trait: bool,
) -> Ty<'tcx> {
debug!(?def_id, ?lifetimes);
#[instrument(level = "debug", skip(self), ret)]
fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: bool) -> Ty<'tcx> {
let tcx = self.tcx();
let lifetimes = tcx.opaque_captured_lifetimes(def_id);
debug!(?lifetimes);
// If this is an RPITIT and we are using the new RPITIT lowering scheme, we
// generate the def_id of an associated type for the trait and return as
// type a projection.
let def_id = if in_trait {
tcx.associated_type_for_impl_trait_in_trait(def_id).to_def_id()
} else {
def_id.to_def_id()
};
let generics = tcx.generics_of(def_id);
debug!(?generics);
// We use `generics.count() - lifetimes.len()` here instead of `generics.parent_count`
// since return-position impl trait in trait squashes all of the generics from its source fn
// into its own generics, so the opaque's "own" params isn't always just lifetimes.
let offset = generics.count() - lifetimes.len();
let args = ty::GenericArgs::for_item(tcx, def_id, |param, _| {
// We use `generics.count() - lifetimes.len()` here instead of `generics.parent_count`
// since return-position impl trait in trait squashes all of the generics from its source fn
// into its own generics, so the opaque's "own" params isn't always just lifetimes.
if let Some(i) = (param.index as usize).checked_sub(generics.count() - lifetimes.len())
{
// Resolve our own lifetime parameters.
let GenericParamDefKind::Lifetime { .. } = param.kind else {
span_bug!(
tcx.def_span(param.def_id),
"only expected lifetime for opaque's own generics, got {:?}",
param
);
};
let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] else {
bug!(
"expected lifetime argument for param {param:?}, found {:?}",
&lifetimes[i]
)
};
self.lower_lifetime(lifetime, RegionInferReason::Param(&param)).into()
if let Some(i) = (param.index as usize).checked_sub(offset) {
let (lifetime, _) = lifetimes[i];
self.lower_resolved_lifetime(lifetime).into()
} else {
tcx.mk_param_from_def(param)
}