Remap impl-trait lifetimes on HIR instead of AST lowering.
This commit is contained in:
parent
298c7462c3
commit
b6e1214ac0
35 changed files with 508 additions and 597 deletions
|
@ -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::*;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(¶m)).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)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue