Rollup merge of #129383 - cjgillot:opaque-noremap, r=compiler-errors,petrochenkov

Remap impl-trait lifetimes on HIR instead of AST lowering

Current AST->HIR lowering goes out of its way to remap lifetimes for opaque types. This is complicated and leaks into upstream and downstream code.

This PR stops trying to be clever during lowering, and prefers to do this remapping during the HIR->ty lowering. The remapping computation easily fits into the bound var resolution code. Its result can be used in by `generics_of` and `hir_ty_lowering::new_opaque` to add the proper parameters and arguments.

See an example on the doc for query `opaque_captured_lifetimes`.

Based on https://github.com/rust-lang/rust/pull/129244/

Fixes https://github.com/rust-lang/rust/issues/125249
Fixes https://github.com/rust-lang/rust/issues/126850

cc `@compiler-errors` `@spastorino`
r? `@petrochenkov`
This commit is contained in:
Jubilee 2024-10-30 14:01:36 -07:00 committed by GitHub
commit 6b60f03f15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 513 additions and 747 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,23 @@ enum Scope<'a> {
s: ScopeRef<'a>,
},
/// Remap lifetimes that appear in opaque types to fresh lifetime parameters. Given:
/// `fn foo<'a>() -> impl MyTrait<'a> { ... }`
///
/// HIR tells us that `'a` refer to the lifetime bound on `foo`.
/// However, typeck and borrowck for opaques work based on using a new generic type.
/// `type MyAnonTy<'b> = impl MyTrait<'b>;`
///
/// This scope collects the mapping `'a -> 'b`.
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 +214,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 +254,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 +270,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 +301,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 +346,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 +377,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 +550,85 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
}
}
/// Resolve the lifetimes inside the opaque type, and save them into
/// `opaque_captured_lifetimes`.
///
/// This method has special handling for opaques that capture all lifetimes,
/// like async desugaring.
#[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 captures = RefCell::new(FxIndexMap::default());
let capture_all_in_scope_lifetimes =
opaque_captures_all_in_scope_lifetimes(self.tcx, opaque);
if capture_all_in_scope_lifetimes {
let lifetime_ident = |def_id: LocalDefId| {
let name = self.tcx.item_name(def_id.to_def_id());
let span = self.tcx.def_span(def_id);
Ident::new(name, span)
};
// 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;
let mut opaque_capture_scopes = vec![(opaque.def_id, &captures)];
loop {
match *scope {
Scope::Binder { ref bound_vars, s, .. } => {
for (&original_lifetime, &def) in bound_vars.iter().rev() {
if let DefKind::LifetimeParam = self.tcx.def_kind(original_lifetime) {
let ident = lifetime_ident(original_lifetime);
self.remap_opaque_captures(&opaque_capture_scopes, def, ident);
}
}
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 {
let def = ResolvedArg::EarlyBound(param.def_id.expect_local());
let ident = lifetime_ident(param.def_id.expect_local());
self.remap_opaque_captures(&opaque_capture_scopes, def, ident);
}
}
opt_parent_item = parent_generics.parent.and_then(DefId::as_local);
}
break;
}
Scope::Opaque { captures, def_id, s } => {
opaque_capture_scopes.push((def_id, captures));
scope = s;
}
Scope::Body { .. } => {
bug!("{:?}", scope)
}
Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. }
| Scope::LateBoundary { s, .. } => {
scope = s;
}
}
}
captures.borrow_mut().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 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();
debug!(?captures);
self.map.opaque_captured_lifetimes.insert(opaque.def_id, captures);
}
#[instrument(level = "debug", skip(self))]
@ -685,67 +803,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
};
self.with(scope, |this| this.visit_ty(mt.ty));
}
hir::TyKind::OpaqueDef(opaque_ty, lifetimes) => {
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 +1186,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 +1262,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 +1282,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 +1357,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 +1373,79 @@ 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,
capture_span: 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 capture_span != decl_span {
(capture_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)
}
#[instrument(level = "trace", skip(self, opaque_capture_scopes), ret)]
fn remap_opaque_captures(
&self,
opaque_capture_scopes: &Vec<(LocalDefId, &RefCell<FxIndexMap<ResolvedArg, LocalDefId>>)>,
mut lifetime: ResolvedArg,
ident: Ident,
) -> ResolvedArg {
if let Some(&(opaque_def_id, _)) = opaque_capture_scopes.last() {
if let Err(guar) =
self.check_lifetime_is_capturable(opaque_def_id, lifetime, ident.span)
{
lifetime = ResolvedArg::Error(guar);
}
}
for &(opaque_def_id, captures) in opaque_capture_scopes.iter().rev() {
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 +1485,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
}
Scope::ObjectLifetimeDefault { s, .. }
| Scope::Opaque { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. } => {
scope = s;
@ -1425,6 +1566,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 +1643,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 +1929,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 +1944,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 +1961,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 +1973,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 +2156,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 +2179,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 +2346,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)
}