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:
commit
6b60f03f15
38 changed files with 513 additions and 747 deletions
|
@ -45,16 +45,14 @@ use rustc_ast::ptr::P;
|
|||
use rustc_ast::{self as ast, *};
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
|
||||
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalDefIdMap};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_hir::{
|
||||
self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, MissingLifetimeKind,
|
||||
ParamName, TraitCandidate,
|
||||
self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, ParamName, TraitCandidate,
|
||||
};
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use rustc_macros::extension;
|
||||
|
@ -83,7 +81,6 @@ mod expr;
|
|||
mod format;
|
||||
mod index;
|
||||
mod item;
|
||||
mod lifetime_collector;
|
||||
mod pat;
|
||||
mod path;
|
||||
|
||||
|
@ -149,12 +146,6 @@ struct LoweringContext<'a, 'hir> {
|
|||
allow_async_iterator: Lrc<[Symbol]>,
|
||||
allow_for_await: Lrc<[Symbol]>,
|
||||
allow_async_fn_traits: Lrc<[Symbol]>,
|
||||
|
||||
/// Mapping from generics `def_id`s to TAIT generics `def_id`s.
|
||||
/// For each captured lifetime (e.g., 'a), we create a new lifetime parameter that is a generic
|
||||
/// defined on the TAIT, so we have type Foo<'a1> = ... and we establish a mapping in this
|
||||
/// field from the original parameter 'a to the new parameter 'a1.
|
||||
generics_def_id_map: Vec<LocalDefIdMap<LocalDefId>>,
|
||||
}
|
||||
|
||||
impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
|
@ -199,7 +190,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
|
||||
// interact with `gen`/`async gen` blocks
|
||||
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
|
||||
generics_def_id_map: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -528,54 +518,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
/// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name
|
||||
/// resolver (if any).
|
||||
fn orig_opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> {
|
||||
self.resolver.node_id_to_def_id.get(&node).copied()
|
||||
}
|
||||
|
||||
/// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name
|
||||
/// resolver (if any), after applying any remapping from `get_remapped_def_id`.
|
||||
///
|
||||
/// For example, in a function like `fn foo<'a>(x: &'a u32)`,
|
||||
/// invoking with the id from the `ast::Lifetime` node found inside
|
||||
/// the `&'a u32` type would return the `LocalDefId` of the
|
||||
/// `'a` parameter declared on `foo`.
|
||||
///
|
||||
/// This function also applies remapping from `get_remapped_def_id`.
|
||||
/// These are used when synthesizing opaque types from `-> impl Trait` return types and so forth.
|
||||
/// For example, in a function like `fn foo<'a>() -> impl Debug + 'a`,
|
||||
/// we would create an opaque type `type FooReturn<'a1> = impl Debug + 'a1`.
|
||||
/// When lowering the `Debug + 'a` bounds, we add a remapping to map `'a` to `'a1`.
|
||||
fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> {
|
||||
self.orig_opt_local_def_id(node).map(|local_def_id| self.get_remapped_def_id(local_def_id))
|
||||
self.resolver.node_id_to_def_id.get(&node).copied()
|
||||
}
|
||||
|
||||
fn local_def_id(&self, node: NodeId) -> LocalDefId {
|
||||
self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{node:?}`"))
|
||||
}
|
||||
|
||||
/// Get the previously recorded `to` local def id given the `from` local def id, obtained using
|
||||
/// `generics_def_id_map` field.
|
||||
fn get_remapped_def_id(&self, local_def_id: LocalDefId) -> LocalDefId {
|
||||
// `generics_def_id_map` is a stack of mappings. As we go deeper in impl traits nesting we
|
||||
// push new mappings, so we first need to get the latest (innermost) mappings, hence `iter().rev()`.
|
||||
//
|
||||
// Consider:
|
||||
//
|
||||
// `fn test<'a, 'b>() -> impl Trait<&'a u8, Ty = impl Sized + 'b> {}`
|
||||
//
|
||||
// We would end with a generics_def_id_map like:
|
||||
//
|
||||
// `[[fn#'b -> impl_trait#'b], [fn#'b -> impl_sized#'b]]`
|
||||
//
|
||||
// for the opaque type generated on `impl Sized + 'b`, we want the result to be: impl_sized#'b.
|
||||
// So, if we were trying to find first from the start (outermost) would give the wrong result, impl_trait#'b.
|
||||
self.generics_def_id_map
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|map| map.get(&local_def_id).copied())
|
||||
.unwrap_or(local_def_id)
|
||||
}
|
||||
|
||||
/// Freshen the `LoweringContext` and ready it to lower a nested item.
|
||||
/// The lowered item is registered into `self.children`.
|
||||
///
|
||||
|
@ -647,27 +597,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
result
|
||||
}
|
||||
|
||||
/// Installs the remapping `remap` in scope while `f` is being executed.
|
||||
/// This causes references to the `LocalDefId` keys to be changed to
|
||||
/// refer to the values instead.
|
||||
///
|
||||
/// The remapping is used when one piece of AST expands to multiple
|
||||
/// pieces of HIR. For example, the function `fn foo<'a>(...) -> impl Debug + 'a`,
|
||||
/// expands to both a function definition (`foo`) and a TAIT for the return value,
|
||||
/// both of which have a lifetime parameter `'a`. The remapping allows us to
|
||||
/// rewrite the `'a` in the return value to refer to the
|
||||
/// `'a` declared on the TAIT, instead of the function.
|
||||
fn with_remapping<R>(
|
||||
&mut self,
|
||||
remap: LocalDefIdMap<LocalDefId>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
self.generics_def_id_map.push(remap);
|
||||
let res = f(self);
|
||||
self.generics_def_id_map.pop();
|
||||
res
|
||||
}
|
||||
|
||||
fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInfo<'hir> {
|
||||
let attrs = std::mem::take(&mut self.attrs);
|
||||
let mut bodies = std::mem::take(&mut self.bodies);
|
||||
|
@ -1499,27 +1428,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// frequently opened issues show.
|
||||
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
|
||||
|
||||
// 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.
|
||||
let always_capture_in_scope = match origin {
|
||||
_ if self.tcx.features().lifetime_capture_rules_2024() => true,
|
||||
hir::OpaqueTyOrigin::TyAlias { .. } => true,
|
||||
hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => in_trait_or_impl.is_some(),
|
||||
hir::OpaqueTyOrigin::AsyncFn { .. } => {
|
||||
unreachable!("should be using `lower_coroutine_fn_ret_ty`")
|
||||
}
|
||||
};
|
||||
let captured_lifetimes_to_duplicate = lifetime_collector::lifetimes_for_opaque(
|
||||
self.resolver,
|
||||
always_capture_in_scope,
|
||||
opaque_ty_node_id,
|
||||
bounds,
|
||||
span,
|
||||
);
|
||||
debug!(?captured_lifetimes_to_duplicate);
|
||||
|
||||
// Feature gate for RPITIT + use<..>
|
||||
match origin {
|
||||
rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => {
|
||||
|
@ -1542,22 +1450,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
_ => {}
|
||||
}
|
||||
|
||||
self.lower_opaque_inner(
|
||||
opaque_ty_node_id,
|
||||
origin,
|
||||
captured_lifetimes_to_duplicate,
|
||||
span,
|
||||
opaque_ty_span,
|
||||
|this| this.lower_param_bounds(bounds, itctx),
|
||||
)
|
||||
self.lower_opaque_inner(opaque_ty_node_id, origin, opaque_ty_span, |this| {
|
||||
this.lower_param_bounds(bounds, itctx)
|
||||
})
|
||||
}
|
||||
|
||||
fn lower_opaque_inner(
|
||||
&mut self,
|
||||
opaque_ty_node_id: NodeId,
|
||||
origin: hir::OpaqueTyOrigin,
|
||||
captured_lifetimes_to_duplicate: FxIndexSet<Lifetime>,
|
||||
span: Span,
|
||||
opaque_ty_span: Span,
|
||||
lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
|
||||
) -> hir::TyKind<'hir> {
|
||||
|
@ -1565,145 +1466,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let opaque_ty_hir_id = self.lower_node_id(opaque_ty_node_id);
|
||||
debug!(?opaque_ty_def_id, ?opaque_ty_hir_id);
|
||||
|
||||
// Map from captured (old) lifetime to synthetic (new) lifetime.
|
||||
// Used to resolve lifetimes in the bounds of the opaque.
|
||||
let mut captured_to_synthesized_mapping = LocalDefIdMap::default();
|
||||
// List of (early-bound) synthetic lifetimes that are owned by the opaque.
|
||||
// This is used to create the `hir::Generics` owned by the opaque.
|
||||
let mut synthesized_lifetime_definitions = vec![];
|
||||
// Pairs of lifetime arg (that resolves to the captured lifetime)
|
||||
// and the def-id of the (early-bound) synthetic lifetime definition.
|
||||
// This is used both to create generics for the `TyKind::OpaqueDef` that
|
||||
// we return, and also as a captured lifetime mapping for RPITITs.
|
||||
let mut synthesized_lifetime_args = vec![];
|
||||
|
||||
for lifetime in captured_lifetimes_to_duplicate {
|
||||
let res = self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error);
|
||||
let (old_def_id, missing_kind) = match res {
|
||||
LifetimeRes::Param { param: old_def_id, binder: _ } => (old_def_id, None),
|
||||
|
||||
LifetimeRes::Fresh { param, kind, .. } => {
|
||||
debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
|
||||
if let Some(old_def_id) = self.orig_opt_local_def_id(param) {
|
||||
(old_def_id, Some(kind))
|
||||
} else {
|
||||
self.dcx()
|
||||
.span_delayed_bug(lifetime.ident.span, "no def-id for fresh lifetime");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Opaques do not capture `'static`
|
||||
LifetimeRes::Static { .. } | LifetimeRes::Error => {
|
||||
continue;
|
||||
}
|
||||
|
||||
res => {
|
||||
let bug_msg = format!(
|
||||
"Unexpected lifetime resolution {:?} for {:?} at {:?}",
|
||||
res, lifetime.ident, lifetime.ident.span
|
||||
);
|
||||
span_bug!(lifetime.ident.span, "{}", bug_msg);
|
||||
}
|
||||
};
|
||||
|
||||
if captured_to_synthesized_mapping.get(&old_def_id).is_none() {
|
||||
// Create a new lifetime parameter local to the opaque.
|
||||
let duplicated_lifetime_node_id = self.next_node_id();
|
||||
let duplicated_lifetime_def_id = self.create_def(
|
||||
opaque_ty_def_id,
|
||||
duplicated_lifetime_node_id,
|
||||
lifetime.ident.name,
|
||||
DefKind::LifetimeParam,
|
||||
self.lower_span(lifetime.ident.span),
|
||||
);
|
||||
captured_to_synthesized_mapping.insert(old_def_id, duplicated_lifetime_def_id);
|
||||
// FIXME: Instead of doing this, we could move this whole loop
|
||||
// into the `with_hir_id_owner`, then just directly construct
|
||||
// the `hir::GenericParam` here.
|
||||
synthesized_lifetime_definitions.push((
|
||||
duplicated_lifetime_node_id,
|
||||
duplicated_lifetime_def_id,
|
||||
self.lower_ident(lifetime.ident),
|
||||
missing_kind,
|
||||
));
|
||||
|
||||
// Now make an arg that we can use for the generic params of the opaque tykind.
|
||||
let id = self.next_node_id();
|
||||
let lifetime_arg = self.new_named_lifetime_with_res(id, lifetime.ident, res);
|
||||
let duplicated_lifetime_def_id = self.local_def_id(duplicated_lifetime_node_id);
|
||||
synthesized_lifetime_args.push((lifetime_arg, duplicated_lifetime_def_id))
|
||||
}
|
||||
}
|
||||
|
||||
let opaque_ty_def = self.with_def_id_parent(opaque_ty_def_id, |this| {
|
||||
// Install the remapping from old to new (if any). This makes sure that
|
||||
// any lifetimes that would have resolved to the def-id of captured
|
||||
// lifetimes are remapped to the new *synthetic* lifetimes of the opaque.
|
||||
let bounds = this
|
||||
.with_remapping(captured_to_synthesized_mapping, |this| lower_item_bounds(this));
|
||||
|
||||
let generic_params =
|
||||
this.arena.alloc_from_iter(synthesized_lifetime_definitions.iter().map(
|
||||
|&(new_node_id, new_def_id, ident, missing_kind)| {
|
||||
let hir_id = this.lower_node_id(new_node_id);
|
||||
let (name, kind) = if ident.name == kw::UnderscoreLifetime {
|
||||
(
|
||||
hir::ParamName::Fresh,
|
||||
hir::LifetimeParamKind::Elided(
|
||||
missing_kind.unwrap_or(MissingLifetimeKind::Underscore),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
(hir::ParamName::Plain(ident), hir::LifetimeParamKind::Explicit)
|
||||
};
|
||||
|
||||
hir::GenericParam {
|
||||
hir_id,
|
||||
def_id: new_def_id,
|
||||
name,
|
||||
span: ident.span,
|
||||
pure_wrt_drop: false,
|
||||
kind: hir::GenericParamKind::Lifetime { kind },
|
||||
colon_span: None,
|
||||
source: hir::GenericParamSource::Generics,
|
||||
}
|
||||
},
|
||||
));
|
||||
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
|
||||
|
||||
let lifetime_mapping = self.arena.alloc_slice(&synthesized_lifetime_args);
|
||||
|
||||
trace!("registering opaque type with id {:#?}", opaque_ty_def_id);
|
||||
let bounds = lower_item_bounds(this);
|
||||
let opaque_ty_def = hir::OpaqueTy {
|
||||
hir_id: opaque_ty_hir_id,
|
||||
def_id: opaque_ty_def_id,
|
||||
generics: this.arena.alloc(hir::Generics {
|
||||
params: generic_params,
|
||||
predicates: &[],
|
||||
has_where_clause_predicates: false,
|
||||
where_clause_span: this.lower_span(span),
|
||||
span: this.lower_span(span),
|
||||
}),
|
||||
bounds,
|
||||
origin,
|
||||
lifetime_mapping,
|
||||
span: this.lower_span(opaque_ty_span),
|
||||
};
|
||||
this.arena.alloc(opaque_ty_def)
|
||||
});
|
||||
|
||||
let generic_args = self.arena.alloc_from_iter(
|
||||
synthesized_lifetime_args
|
||||
.iter()
|
||||
.map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)),
|
||||
);
|
||||
|
||||
// Create the `Foo<...>` reference itself. Note that the `type
|
||||
// Foo = impl Trait` is, internally, created as a child of the
|
||||
// async fn, so the *type parameters* are inherited. It's
|
||||
// only the lifetime parameters that we must supply.
|
||||
hir::TyKind::OpaqueDef(opaque_ty_def, generic_args)
|
||||
hir::TyKind::OpaqueDef(opaque_ty_def)
|
||||
}
|
||||
|
||||
fn lower_precise_capturing_args(
|
||||
|
@ -1885,13 +1660,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let opaque_ty_span =
|
||||
self.mark_span_with_reason(DesugaringKind::Async, span, allowed_features);
|
||||
|
||||
let captured_lifetimes = self
|
||||
.resolver
|
||||
.extra_lifetime_params(opaque_ty_node_id)
|
||||
.into_iter()
|
||||
.map(|(ident, id, _)| Lifetime { id, ident })
|
||||
.collect();
|
||||
|
||||
let in_trait_or_impl = match fn_kind {
|
||||
FnDeclKind::Trait => Some(hir::RpitContext::Trait),
|
||||
FnDeclKind::Impl => Some(hir::RpitContext::TraitImpl),
|
||||
|
@ -1902,8 +1670,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let opaque_ty_ref = self.lower_opaque_inner(
|
||||
opaque_ty_node_id,
|
||||
hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, in_trait_or_impl },
|
||||
captured_lifetimes,
|
||||
span,
|
||||
opaque_ty_span,
|
||||
|this| {
|
||||
let bound = this.lower_coroutine_fn_output_type_to_bound(
|
||||
|
@ -2000,10 +1766,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
res: LifetimeRes,
|
||||
) -> &'hir hir::Lifetime {
|
||||
let res = match res {
|
||||
LifetimeRes::Param { param, .. } => {
|
||||
let param = self.get_remapped_def_id(param);
|
||||
hir::LifetimeName::Param(param)
|
||||
}
|
||||
LifetimeRes::Param { param, .. } => hir::LifetimeName::Param(param),
|
||||
LifetimeRes::Fresh { param, .. } => {
|
||||
let param = self.local_def_id(param);
|
||||
hir::LifetimeName::Param(param)
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor};
|
||||
use rustc_ast::{
|
||||
GenericBound, GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind,
|
||||
};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir::def::{DefKind, LifetimeRes, Res};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::ResolverAstLowering;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::{Ident, kw};
|
||||
|
||||
use super::ResolverAstLoweringExt;
|
||||
|
||||
struct LifetimeCollectVisitor<'ast> {
|
||||
resolver: &'ast mut ResolverAstLowering,
|
||||
always_capture_in_scope: bool,
|
||||
current_binders: Vec<NodeId>,
|
||||
collected_lifetimes: FxIndexSet<Lifetime>,
|
||||
}
|
||||
|
||||
impl<'ast> LifetimeCollectVisitor<'ast> {
|
||||
fn new(resolver: &'ast mut ResolverAstLowering, always_capture_in_scope: bool) -> Self {
|
||||
Self {
|
||||
resolver,
|
||||
always_capture_in_scope,
|
||||
current_binders: Vec::new(),
|
||||
collected_lifetimes: FxIndexSet::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_opaque(&mut self, opaque_ty_node_id: NodeId, bounds: &'ast GenericBounds, span: Span) {
|
||||
// If we're edition 2024 or within a TAIT or RPITIT, *and* there is no
|
||||
// `use<>` statement to override the default capture behavior, then
|
||||
// capture all of the in-scope lifetimes.
|
||||
if (self.always_capture_in_scope || span.at_least_rust_2024())
|
||||
&& bounds.iter().all(|bound| !matches!(bound, GenericBound::Use(..)))
|
||||
{
|
||||
for (ident, id, _) in self.resolver.extra_lifetime_params(opaque_ty_node_id) {
|
||||
self.record_lifetime_use(Lifetime { id, ident });
|
||||
}
|
||||
}
|
||||
|
||||
// We also recurse on the bounds to make sure we capture all the lifetimes
|
||||
// mentioned in the bounds. These may disagree with the `use<>` list, in which
|
||||
// case we will error on these later. We will also recurse to visit any
|
||||
// nested opaques, which may *implicitly* capture lifetimes.
|
||||
for bound in bounds {
|
||||
self.visit_param_bound(bound, BoundKind::Bound);
|
||||
}
|
||||
}
|
||||
|
||||
fn record_lifetime_use(&mut self, lifetime: Lifetime) {
|
||||
match self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error) {
|
||||
LifetimeRes::Param { binder, .. } | LifetimeRes::Fresh { binder, .. } => {
|
||||
if !self.current_binders.contains(&binder) {
|
||||
self.collected_lifetimes.insert(lifetime);
|
||||
}
|
||||
}
|
||||
LifetimeRes::Static { .. } | LifetimeRes::Error => {
|
||||
self.collected_lifetimes.insert(lifetime);
|
||||
}
|
||||
LifetimeRes::Infer => {}
|
||||
res => {
|
||||
let bug_msg = format!(
|
||||
"Unexpected lifetime resolution {:?} for {:?} at {:?}",
|
||||
res, lifetime.ident, lifetime.ident.span
|
||||
);
|
||||
span_bug!(lifetime.ident.span, "{}", bug_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This collect lifetimes that are elided, for nodes like `Foo<T>` where there are no explicit
|
||||
/// lifetime nodes. Is equivalent to having "pseudo" nodes introduced for each of the node ids
|
||||
/// in the list start..end.
|
||||
fn record_elided_anchor(&mut self, node_id: NodeId, span: Span) {
|
||||
if let Some(LifetimeRes::ElidedAnchor { start, end }) =
|
||||
self.resolver.get_lifetime_res(node_id)
|
||||
{
|
||||
for i in start..end {
|
||||
let lifetime = Lifetime { id: i, ident: Ident::new(kw::UnderscoreLifetime, span) };
|
||||
self.record_lifetime_use(lifetime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> {
|
||||
fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: LifetimeCtxt) {
|
||||
self.record_lifetime_use(*lifetime);
|
||||
}
|
||||
|
||||
fn visit_path_segment(&mut self, path_segment: &'ast PathSegment) {
|
||||
self.record_elided_anchor(path_segment.id, path_segment.ident.span);
|
||||
visit::walk_path_segment(self, path_segment);
|
||||
}
|
||||
|
||||
fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef) {
|
||||
self.current_binders.push(t.trait_ref.ref_id);
|
||||
|
||||
visit::walk_poly_trait_ref(self, t);
|
||||
|
||||
self.current_binders.pop();
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, t: &'ast Ty) {
|
||||
match &t.kind {
|
||||
TyKind::Path(None, _) => {
|
||||
// We can sometimes encounter bare trait objects
|
||||
// which are represented in AST as paths.
|
||||
if let Some(partial_res) = self.resolver.get_partial_res(t.id)
|
||||
&& let Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) =
|
||||
partial_res.full_res()
|
||||
{
|
||||
self.current_binders.push(t.id);
|
||||
visit::walk_ty(self, t);
|
||||
self.current_binders.pop();
|
||||
} else {
|
||||
visit::walk_ty(self, t);
|
||||
}
|
||||
}
|
||||
TyKind::BareFn(_) => {
|
||||
self.current_binders.push(t.id);
|
||||
visit::walk_ty(self, t);
|
||||
self.current_binders.pop();
|
||||
}
|
||||
TyKind::Ref(None, _) | TyKind::PinnedRef(None, _) => {
|
||||
self.record_elided_anchor(t.id, t.span);
|
||||
visit::walk_ty(self, t);
|
||||
}
|
||||
TyKind::ImplTrait(opaque_ty_node_id, bounds) => {
|
||||
self.visit_opaque(*opaque_ty_node_id, bounds, t.span)
|
||||
}
|
||||
_ => {
|
||||
visit::walk_ty(self, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lifetimes_for_opaque(
|
||||
resolver: &mut ResolverAstLowering,
|
||||
always_capture_in_scope: bool,
|
||||
opaque_ty_node_id: NodeId,
|
||||
bounds: &GenericBounds,
|
||||
span: Span,
|
||||
) -> FxIndexSet<Lifetime> {
|
||||
let mut visitor = LifetimeCollectVisitor::new(resolver, always_capture_in_scope);
|
||||
visitor.visit_opaque(opaque_ty_node_id, bounds, span);
|
||||
visitor.collected_lifetimes
|
||||
}
|
|
@ -830,7 +830,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
///
|
||||
/// [`OpaqueDef`]: hir::TyKind::OpaqueDef
|
||||
fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
|
||||
let hir::TyKind::OpaqueDef(opaque_ty, _) = hir_ty.kind else {
|
||||
let hir::TyKind::OpaqueDef(opaque_ty) = hir_ty.kind else {
|
||||
span_bug!(
|
||||
hir_ty.span,
|
||||
"lowered return type of async fn is not OpaqueDef: {:?}",
|
||||
|
|
|
@ -2627,7 +2627,6 @@ impl<'hir> Ty<'hir> {
|
|||
}
|
||||
TyKind::Tup(tys) => tys.iter().any(Self::is_suggestable_infer_ty),
|
||||
TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => mut_ty.ty.is_suggestable_infer_ty(),
|
||||
TyKind::OpaqueDef(_, generic_args) => are_suggestable_generic_args(generic_args),
|
||||
TyKind::Path(QPath::TypeRelative(ty, segment)) => {
|
||||
ty.is_suggestable_infer_ty() || are_suggestable_generic_args(segment.args().args)
|
||||
}
|
||||
|
@ -2746,19 +2745,8 @@ pub struct BareFnTy<'hir> {
|
|||
pub struct OpaqueTy<'hir> {
|
||||
pub hir_id: HirId,
|
||||
pub def_id: LocalDefId,
|
||||
pub generics: &'hir Generics<'hir>,
|
||||
pub bounds: GenericBounds<'hir>,
|
||||
pub origin: OpaqueTyOrigin,
|
||||
/// Return-position impl traits (and async futures) must "reify" any late-bound
|
||||
/// lifetimes that are captured from the function signature they originate from.
|
||||
///
|
||||
/// This is done by generating a new early-bound lifetime parameter local to the
|
||||
/// opaque which is instantiated in the function signature with the late-bound
|
||||
/// lifetime.
|
||||
///
|
||||
/// This mapping associated a captured lifetime (first parameter) with the new
|
||||
/// early-bound lifetime that was generated for the opaque.
|
||||
pub lifetime_mapping: &'hir [(&'hir Lifetime, LocalDefId)],
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
|
@ -2861,12 +2849,7 @@ pub enum TyKind<'hir> {
|
|||
/// Type parameters may be stored in each `PathSegment`.
|
||||
Path(QPath<'hir>),
|
||||
/// An opaque type definition itself. This is only used for `impl Trait`.
|
||||
///
|
||||
/// The generic argument list contains the lifetimes (and in the future
|
||||
/// possibly parameters) that are actually bound on the `impl Trait`.
|
||||
///
|
||||
/// The last parameter specifies whether this opaque appears in a trait definition.
|
||||
OpaqueDef(&'hir OpaqueTy<'hir>, &'hir [GenericArg<'hir>]),
|
||||
OpaqueDef(&'hir OpaqueTy<'hir>),
|
||||
/// A trait object type `Bound1 + Bound2 + Bound3`
|
||||
/// where `Bound` is a trait or a lifetime.
|
||||
TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax),
|
||||
|
@ -3991,7 +3974,6 @@ impl<'hir> Node<'hir> {
|
|||
| Node::TraitItem(TraitItem { generics, .. })
|
||||
| Node::ImplItem(ImplItem { generics, .. }) => Some(generics),
|
||||
Node::Item(item) => item.kind.generics(),
|
||||
Node::OpaqueTy(opaque) => Some(opaque.generics),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -896,9 +896,8 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
|
|||
TyKind::Path(ref qpath) => {
|
||||
try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span));
|
||||
}
|
||||
TyKind::OpaqueDef(opaque, lifetimes) => {
|
||||
TyKind::OpaqueDef(opaque) => {
|
||||
try_visit!(visitor.visit_opaque_ty(opaque));
|
||||
walk_list!(visitor, visit_generic_arg, lifetimes);
|
||||
}
|
||||
TyKind::Array(ref ty, ref length) => {
|
||||
try_visit!(visitor.visit_ty(ty));
|
||||
|
@ -1188,10 +1187,8 @@ pub fn walk_poly_trait_ref<'v, V: Visitor<'v>>(
|
|||
}
|
||||
|
||||
pub fn walk_opaque_ty<'v, V: Visitor<'v>>(visitor: &mut V, opaque: &'v OpaqueTy<'v>) -> V::Result {
|
||||
let &OpaqueTy { hir_id, def_id: _, generics, bounds, origin: _, lifetime_mapping: _, span: _ } =
|
||||
opaque;
|
||||
let &OpaqueTy { hir_id, def_id: _, bounds, origin: _, span: _ } = opaque;
|
||||
try_visit!(visitor.visit_id(hir_id));
|
||||
try_visit!(walk_generics(visitor, generics));
|
||||
walk_list!(visitor, visit_param_bound, bounds);
|
||||
V::Result::output()
|
||||
}
|
||||
|
|
|
@ -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,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -659,8 +659,6 @@ impl<'a> State<'a> {
|
|||
|
||||
fn print_opaque_ty(&mut self, o: &hir::OpaqueTy<'_>) {
|
||||
self.head("opaque");
|
||||
self.print_generic_params(o.generics.params);
|
||||
self.print_where_clause(o.generics);
|
||||
self.word("{");
|
||||
self.print_bounds("impl", o.bounds);
|
||||
self.word("}");
|
||||
|
|
|
@ -69,7 +69,7 @@ declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
|
||||
fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx>) {
|
||||
let hir::TyKind::OpaqueDef(opaque, _) = &ty.kind else {
|
||||
let hir::TyKind::OpaqueDef(opaque) = &ty.kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::ItemLocalId;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap};
|
||||
use rustc_macros::{Decodable, Encodable, HashStable, TyDecodable, TyEncodable};
|
||||
|
||||
use crate::ty;
|
||||
|
@ -54,4 +54,6 @@ pub struct ResolveBoundVars {
|
|||
pub defs: SortedMap<ItemLocalId, ResolvedArg>,
|
||||
|
||||
pub late_bound_vars: SortedMap<ItemLocalId, Vec<ty::BoundVariableKind>>,
|
||||
|
||||
pub opaque_captured_lifetimes: LocalDefIdMap<Vec<(ResolvedArg, LocalDefId)>>,
|
||||
}
|
||||
|
|
|
@ -1781,6 +1781,23 @@ rustc_queries! {
|
|||
-> &'tcx SortedMap<ItemLocalId, Vec<ty::BoundVariableKind>> {
|
||||
desc { |tcx| "looking up late bound vars inside `{}`", tcx.def_path_str(owner_id) }
|
||||
}
|
||||
/// For an opaque type, return the list of (captured lifetime, inner generic param).
|
||||
/// ```ignore (illustrative)
|
||||
/// fn foo<'a: 'a, 'b, T>(&'b u8) -> impl Into<Self> + 'b { ... }
|
||||
/// ```
|
||||
///
|
||||
/// We would return `[('a, '_a), ('b, '_b)]`, with `'a` early-bound and `'b` late-bound.
|
||||
///
|
||||
/// After hir_ty_lowering, we get:
|
||||
/// ```ignore (pseudo-code)
|
||||
/// opaque foo::<'a>::opaque<'_a, '_b>: Into<Foo<'_a>> + '_b;
|
||||
/// ^^^^^^^^ inner generic params
|
||||
/// fn foo<'a>: for<'b> fn(&'b u8) -> foo::<'a>::opaque::<'a, 'b>
|
||||
/// ^^^^^^ captured lifetimes
|
||||
/// ```
|
||||
query opaque_captured_lifetimes(def_id: LocalDefId) -> &'tcx [(ResolvedArg, LocalDefId)] {
|
||||
desc { |tcx| "listing captured lifetimes for opaque `{}`", tcx.def_path_str(def_id) }
|
||||
}
|
||||
|
||||
/// Computes the visibility of the provided `def_id`.
|
||||
///
|
||||
|
|
|
@ -3060,7 +3060,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
|
||||
loop {
|
||||
let parent = self.local_parent(opaque_lifetime_param_def_id);
|
||||
let hir::OpaqueTy { lifetime_mapping, .. } = self.hir().expect_opaque_ty(parent);
|
||||
let lifetime_mapping = self.opaque_captured_lifetimes(parent);
|
||||
|
||||
let Some((lifetime, _)) = lifetime_mapping
|
||||
.iter()
|
||||
|
@ -3069,8 +3069,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
bug!("duplicated lifetime param should be present");
|
||||
};
|
||||
|
||||
match self.named_bound_var(lifetime.hir_id) {
|
||||
Some(resolve_bound_vars::ResolvedArg::EarlyBound(ebv)) => {
|
||||
match *lifetime {
|
||||
resolve_bound_vars::ResolvedArg::EarlyBound(ebv) => {
|
||||
let new_parent = self.local_parent(ebv);
|
||||
|
||||
// If we map to another opaque, then it should be a parent
|
||||
|
@ -3089,7 +3089,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
name: self.item_name(ebv.to_def_id()),
|
||||
});
|
||||
}
|
||||
Some(resolve_bound_vars::ResolvedArg::LateBound(_, _, lbv)) => {
|
||||
resolve_bound_vars::ResolvedArg::LateBound(_, _, lbv) => {
|
||||
let new_parent = self.local_parent(lbv);
|
||||
return ty::Region::new_late_param(
|
||||
self,
|
||||
|
@ -3100,13 +3100,13 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
),
|
||||
);
|
||||
}
|
||||
Some(resolve_bound_vars::ResolvedArg::Error(guar)) => {
|
||||
resolve_bound_vars::ResolvedArg::Error(guar) => {
|
||||
return ty::Region::new_error(self, guar);
|
||||
}
|
||||
_ => {
|
||||
return ty::Region::new_error_with_message(
|
||||
self,
|
||||
lifetime.ident.span,
|
||||
self.def_span(opaque_lifetime_param_def_id),
|
||||
"cannot resolve lifetime",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1354,6 +1354,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(tcx))]
|
||||
pub fn fn_sig(self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> {
|
||||
match self.kind() {
|
||||
FnDef(def_id, args) => tcx.fn_sig(*def_id).instantiate(tcx, args),
|
||||
|
|
|
@ -841,10 +841,9 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
|
|||
self.r.record_partial_res(ty.id, PartialRes::new(res));
|
||||
visit::walk_ty(self, ty)
|
||||
}
|
||||
TyKind::ImplTrait(node_id, _) => {
|
||||
TyKind::ImplTrait(..) => {
|
||||
let candidates = self.lifetime_elision_candidates.take();
|
||||
visit::walk_ty(self, ty);
|
||||
self.record_lifetime_params_for_impl_trait(*node_id);
|
||||
self.lifetime_elision_candidates = candidates;
|
||||
}
|
||||
TyKind::TraitObject(bounds, ..) => {
|
||||
|
@ -977,14 +976,6 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
|
|||
sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)),
|
||||
&sig.decl.output,
|
||||
);
|
||||
|
||||
if let Some((coro_node_id, _)) = sig
|
||||
.header
|
||||
.coroutine_kind
|
||||
.map(|coroutine_kind| coroutine_kind.return_id())
|
||||
{
|
||||
this.record_lifetime_params_for_impl_trait(coro_node_id);
|
||||
}
|
||||
},
|
||||
);
|
||||
return;
|
||||
|
@ -1026,10 +1017,6 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
|
|||
.map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)),
|
||||
&declaration.output,
|
||||
);
|
||||
|
||||
if let Some((async_node_id, _)) = coro_node_id {
|
||||
this.record_lifetime_params_for_impl_trait(async_node_id);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -1220,7 +1207,6 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
|
|||
}
|
||||
},
|
||||
AssocItemConstraintKind::Bound { ref bounds } => {
|
||||
self.record_lifetime_params_for_impl_trait(constraint.id);
|
||||
walk_list!(self, visit_param_bound, bounds, BoundKind::Bound);
|
||||
}
|
||||
}
|
||||
|
@ -4795,30 +4781,6 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Construct the list of in-scope lifetime parameters for impl trait lowering.
|
||||
/// We include all lifetime parameters, either named or "Fresh".
|
||||
/// The order of those parameters does not matter, as long as it is
|
||||
/// deterministic.
|
||||
fn record_lifetime_params_for_impl_trait(&mut self, impl_trait_node_id: NodeId) {
|
||||
let mut extra_lifetime_params = vec![];
|
||||
|
||||
for rib in self.lifetime_ribs.iter().rev() {
|
||||
extra_lifetime_params
|
||||
.extend(rib.bindings.iter().map(|(&ident, &(node_id, res))| (ident, node_id, res)));
|
||||
match rib.kind {
|
||||
LifetimeRibKind::Item => break,
|
||||
LifetimeRibKind::AnonymousCreateParameter { binder, .. } => {
|
||||
if let Some(earlier_fresh) = self.r.extra_lifetime_params_map.get(&binder) {
|
||||
extra_lifetime_params.extend(earlier_fresh);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.r.extra_lifetime_params_map.insert(impl_trait_node_id, extra_lifetime_params);
|
||||
}
|
||||
|
||||
fn resolve_and_cache_rustdoc_path(&mut self, path_str: &str, ns: Namespace) -> Option<Res> {
|
||||
// FIXME: This caching may be incorrect in case of multiple `macro_rules`
|
||||
// items with the same name in the same module.
|
||||
|
|
|
@ -284,7 +284,7 @@ pub fn suggest_new_region_bound(
|
|||
}
|
||||
match fn_return.kind {
|
||||
// FIXME(precise_captures): Suggest adding to `use<...>` list instead.
|
||||
TyKind::OpaqueDef(opaque, _) => {
|
||||
TyKind::OpaqueDef(opaque) => {
|
||||
// Get the identity type for this RPIT
|
||||
let did = opaque.def_id.to_def_id();
|
||||
let ty = Ty::new_opaque(tcx, did, ty::GenericArgs::identity_for_item(tcx, did));
|
||||
|
|
|
@ -862,22 +862,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
self.add_lt_suggs.push(lt.suggestion(self.new_lt));
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) {
|
||||
let hir::TyKind::OpaqueDef(opaque_ty, _) = ty.kind else {
|
||||
return hir::intravisit::walk_ty(self, ty);
|
||||
};
|
||||
if let Some(&(_, b)) =
|
||||
opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle)
|
||||
{
|
||||
let prev_needle =
|
||||
std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b));
|
||||
for bound in opaque_ty.bounds {
|
||||
self.visit_param_bound(bound);
|
||||
}
|
||||
self.needle = prev_needle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (lifetime_def_id, lifetime_scope) =
|
||||
|
|
|
@ -361,7 +361,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
})
|
||||
| hir::Node::TraitItem(hir::TraitItem { generics, .. })
|
||||
| hir::Node::ImplItem(hir::ImplItem { generics, .. })
|
||||
| hir::Node::OpaqueTy(hir::OpaqueTy { generics, .. })
|
||||
if param_ty =>
|
||||
{
|
||||
// We skip the 0'th arg (self) because we do not want
|
||||
|
@ -424,10 +423,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
| hir::ItemKind::Const(_, generics, _)
|
||||
| hir::ItemKind::TraitAlias(generics, _),
|
||||
..
|
||||
})
|
||||
| hir::Node::OpaqueTy(hir::OpaqueTy { generics, .. })
|
||||
if !param_ty =>
|
||||
{
|
||||
}) if !param_ty => {
|
||||
// Missing generic type parameter bound.
|
||||
if suggest_arbitrary_trait_bound(
|
||||
self.tcx,
|
||||
|
|
|
@ -190,7 +190,7 @@ fn associated_types_for_impl_traits_in_associated_fn(
|
|||
|
||||
impl<'tcx> Visitor<'tcx> for RPITVisitor {
|
||||
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
|
||||
if let hir::TyKind::OpaqueDef(opaq, _) = ty.kind
|
||||
if let hir::TyKind::OpaqueDef(opaq) = ty.kind
|
||||
&& self.rpits.insert(opaq.def_id)
|
||||
{
|
||||
for bound in opaq.bounds {
|
||||
|
|
|
@ -1829,7 +1829,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
|
|||
Array(Box::new(clean_ty(ty, cx)), length.into())
|
||||
}
|
||||
TyKind::Tup(tys) => Tuple(tys.iter().map(|ty| clean_ty(ty, cx)).collect()),
|
||||
TyKind::OpaqueDef(ty, _) => {
|
||||
TyKind::OpaqueDef(ty) => {
|
||||
ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect())
|
||||
}
|
||||
TyKind::Path(_) => clean_qpath(ty, cx),
|
||||
|
|
|
@ -420,15 +420,6 @@ impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> {
|
|||
|
||||
fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
|
||||
match ty.kind {
|
||||
TyKind::OpaqueDef(opaque, bounds) => {
|
||||
let len = self.lts.len();
|
||||
self.visit_opaque_ty(opaque);
|
||||
self.lts.truncate(len);
|
||||
self.lts.extend(bounds.iter().filter_map(|bound| match bound {
|
||||
GenericArg::Lifetime(&l) => Some(l),
|
||||
_ => None,
|
||||
}));
|
||||
},
|
||||
TyKind::BareFn(&BareFnTy { decl, .. }) => {
|
||||
let mut sub_visitor = RefVisitor::new(self.cx);
|
||||
sub_visitor.visit_fn_decl(decl);
|
||||
|
|
|
@ -4,9 +4,11 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
Block, Body, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnDecl,
|
||||
FnRetTy, GenericArg, GenericBound, ImplItem, Item, LifetimeName, Node, TraitRef, Ty, TyKind,
|
||||
FnRetTy, GenericBound, ImplItem, Item, Node, OpaqueTy, TraitRef, Ty, TyKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{Span, sym};
|
||||
|
@ -44,21 +46,22 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
decl: &'tcx FnDecl<'_>,
|
||||
body: &'tcx Body<'_>,
|
||||
span: Span,
|
||||
def_id: LocalDefId,
|
||||
fn_def_id: LocalDefId,
|
||||
) {
|
||||
if let Some(header) = kind.header()
|
||||
&& !header.asyncness.is_async()
|
||||
// Check that this function returns `impl Future`
|
||||
&& let FnRetTy::Return(ret_ty) = decl.output
|
||||
&& let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty)
|
||||
&& let TyKind::OpaqueDef(opaque) = ret_ty.kind
|
||||
&& let Some(trait_ref) = future_trait_ref(cx, opaque)
|
||||
&& let Some(output) = future_output_ty(trait_ref)
|
||||
&& captures_all_lifetimes(decl.inputs, &output_lifetimes)
|
||||
&& captures_all_lifetimes(cx, fn_def_id, opaque.def_id)
|
||||
// Check that the body of the function consists of one async block
|
||||
&& let ExprKind::Block(block, _) = body.value.kind
|
||||
&& block.stmts.is_empty()
|
||||
&& let Some(closure_body) = desugared_async_block(cx, block)
|
||||
&& let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) =
|
||||
cx.tcx.hir_node_by_def_id(def_id)
|
||||
cx.tcx.hir_node_by_def_id(fn_def_id)
|
||||
{
|
||||
let header_span = span.with_hi(ret_ty.span.hi());
|
||||
|
||||
|
@ -101,12 +104,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
}
|
||||
}
|
||||
|
||||
fn future_trait_ref<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ty: &'tcx Ty<'tcx>,
|
||||
) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> {
|
||||
if let TyKind::OpaqueDef(opaque, bounds) = ty.kind
|
||||
&& let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
|
||||
fn future_trait_ref<'tcx>(cx: &LateContext<'tcx>, opaque: &'tcx OpaqueTy<'tcx>) -> Option<&'tcx TraitRef<'tcx>> {
|
||||
if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
|
||||
if let GenericBound::Trait(poly) = bound {
|
||||
Some(&poly.trait_ref)
|
||||
} else {
|
||||
|
@ -115,18 +114,7 @@ fn future_trait_ref<'tcx>(
|
|||
})
|
||||
&& trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait()
|
||||
{
|
||||
let output_lifetimes = bounds
|
||||
.iter()
|
||||
.filter_map(|bound| {
|
||||
if let GenericArg::Lifetime(lt) = bound {
|
||||
Some(lt.res)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
return Some((trait_ref, output_lifetimes));
|
||||
return Some(trait_ref);
|
||||
}
|
||||
|
||||
None
|
||||
|
@ -145,27 +133,35 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t
|
|||
None
|
||||
}
|
||||
|
||||
fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) -> bool {
|
||||
let input_lifetimes: Vec<LifetimeName> = inputs
|
||||
.iter()
|
||||
.filter_map(|ty| {
|
||||
if let TyKind::Ref(lt, _) = ty.kind {
|
||||
Some(lt.res)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
fn captures_all_lifetimes(cx: &LateContext<'_>, fn_def_id: LocalDefId, opaque_def_id: LocalDefId) -> bool {
|
||||
let early_input_params = ty::GenericArgs::identity_for_item(cx.tcx, fn_def_id);
|
||||
let late_input_params = cx.tcx.late_bound_vars(cx.tcx.local_def_id_to_hir_id(fn_def_id));
|
||||
|
||||
// The lint should trigger in one of these cases:
|
||||
// - There are no input lifetimes
|
||||
// - There's only one output lifetime bound using `+ '_`
|
||||
// - All input lifetimes are explicitly bound to the output
|
||||
input_lifetimes.is_empty()
|
||||
|| (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Infer))
|
||||
|| input_lifetimes
|
||||
.iter()
|
||||
.all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt))
|
||||
let num_early_lifetimes = early_input_params
|
||||
.iter()
|
||||
.filter(|param| param.as_region().is_some())
|
||||
.count();
|
||||
let num_late_lifetimes = late_input_params
|
||||
.iter()
|
||||
.filter(|param_kind| matches!(param_kind, ty::BoundVariableKind::Region(_)))
|
||||
.count();
|
||||
|
||||
// There is no lifetime, so they are all captured.
|
||||
if num_early_lifetimes == 0 && num_late_lifetimes == 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
// By construction, each captured lifetime only appears once in `opaque_captured_lifetimes`.
|
||||
let num_captured_lifetimes = cx
|
||||
.tcx
|
||||
.opaque_captured_lifetimes(opaque_def_id)
|
||||
.iter()
|
||||
.filter(|&(lifetime, _)| match *lifetime {
|
||||
ResolvedArg::EarlyBound(_) | ResolvedArg::LateBound(ty::INNERMOST, _, _) => true,
|
||||
_ => false,
|
||||
})
|
||||
.count();
|
||||
num_captured_lifetimes == num_early_lifetimes + num_late_lifetimes
|
||||
}
|
||||
|
||||
fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
|
||||
|
|
|
@ -1231,16 +1231,13 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
}
|
||||
},
|
||||
TyKind::Path(ref qpath) => self.hash_qpath(qpath),
|
||||
TyKind::OpaqueDef(_, arg_list) => {
|
||||
self.hash_generic_args(arg_list);
|
||||
},
|
||||
TyKind::TraitObject(_, lifetime, _) => {
|
||||
self.hash_lifetime(lifetime);
|
||||
},
|
||||
TyKind::Typeof(anon_const) => {
|
||||
self.hash_body(anon_const.body);
|
||||
},
|
||||
TyKind::Err(_) | TyKind::Infer | TyKind::Never | TyKind::InferDelegation(..) | TyKind::AnonAdt(_) => {},
|
||||
TyKind::Err(_) | TyKind::Infer | TyKind::Never | TyKind::InferDelegation(..) | TyKind::OpaqueDef(_) | TyKind::AnonAdt(_) => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ error: the following explicit lifetimes could be elided: 'a
|
|||
--> tests/ui/issue_4266.rs:10:21
|
||||
|
|
||||
LL | async fn one_to_one<'a>(s: &'a str) -> &'a str {
|
||||
| ^^ ^^
|
||||
| ^^ ^^ ^^
|
||||
|
||||
error: methods called `new` usually take no `self`
|
||||
--> tests/ui/issue_4266.rs:31:22
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
//@ known-bug: rust-lang/rust#125185
|
||||
#![feature(return_position_impl_trait_in_trait, return_type_notation)]
|
||||
|
||||
trait IntFactory {
|
||||
fn stream(&self) -> impl IntFactory<stream(..): IntFactory<stream(..): Send> + Send>;
|
||||
}
|
||||
|
||||
pub fn main() {}
|
|
@ -1,7 +1,8 @@
|
|||
//@ known-bug: #131648
|
||||
#![feature(return_type_notation)]
|
||||
|
||||
trait IntFactory {
|
||||
fn stream(self) -> impl IntFactory<stream(..): Send>;
|
||||
//~^ ERROR cycle detected when resolving lifetimes for `IntFactory::stream`
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,27 @@
|
|||
error[E0391]: cycle detected when resolving lifetimes for `IntFactory::stream`
|
||||
--> $DIR/impl-trait-in-trait.rs:4:5
|
||||
|
|
||||
LL | fn stream(self) -> impl IntFactory<stream(..): Send>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: ...which requires computing function signature of `IntFactory::stream`...
|
||||
--> $DIR/impl-trait-in-trait.rs:4:5
|
||||
|
|
||||
LL | fn stream(self) -> impl IntFactory<stream(..): Send>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires looking up late bound vars inside `IntFactory::stream`...
|
||||
--> $DIR/impl-trait-in-trait.rs:4:5
|
||||
|
|
||||
LL | fn stream(self) -> impl IntFactory<stream(..): Send>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: ...which again requires resolving lifetimes for `IntFactory::stream`, completing the cycle
|
||||
note: cycle used when listing captured lifetimes for opaque `IntFactory::stream::{opaque#0}`
|
||||
--> $DIR/impl-trait-in-trait.rs:4:24
|
||||
|
|
||||
LL | fn stream(self) -> impl IntFactory<stream(..): Send>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0391`.
|
|
@ -1,4 +1,5 @@
|
|||
//@ known-bug: rust-lang/rust#126850
|
||||
//@ check-pass
|
||||
|
||||
fn bug<T>() -> impl Iterator<
|
||||
Item = [(); {
|
||||
|found: &String| Some(false);
|
9
tests/ui/impl-trait/in-trait/return-type-notation.rs
Normal file
9
tests/ui/impl-trait/in-trait/return-type-notation.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
#![allow(incomplete_features)]
|
||||
#![feature(return_type_notation)]
|
||||
|
||||
trait IntFactory {
|
||||
fn stream(&self) -> impl IntFactory<stream(..): IntFactory<stream(..): Send> + Send>;
|
||||
//~^ ERROR cycle detected when resolving lifetimes for `IntFactory::stream`
|
||||
}
|
||||
|
||||
pub fn main() {}
|
27
tests/ui/impl-trait/in-trait/return-type-notation.stderr
Normal file
27
tests/ui/impl-trait/in-trait/return-type-notation.stderr
Normal file
|
@ -0,0 +1,27 @@
|
|||
error[E0391]: cycle detected when resolving lifetimes for `IntFactory::stream`
|
||||
--> $DIR/return-type-notation.rs:5:5
|
||||
|
|
||||
LL | fn stream(&self) -> impl IntFactory<stream(..): IntFactory<stream(..): Send> + Send>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: ...which requires computing function signature of `IntFactory::stream`...
|
||||
--> $DIR/return-type-notation.rs:5:5
|
||||
|
|
||||
LL | fn stream(&self) -> impl IntFactory<stream(..): IntFactory<stream(..): Send> + Send>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: ...which requires looking up late bound vars inside `IntFactory::stream`...
|
||||
--> $DIR/return-type-notation.rs:5:5
|
||||
|
|
||||
LL | fn stream(&self) -> impl IntFactory<stream(..): IntFactory<stream(..): Send> + Send>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: ...which again requires resolving lifetimes for `IntFactory::stream`, completing the cycle
|
||||
note: cycle used when listing captured lifetimes for opaque `IntFactory::stream::{opaque#0}`
|
||||
--> $DIR/return-type-notation.rs:5:25
|
||||
|
|
||||
LL | fn stream(&self) -> impl IntFactory<stream(..): IntFactory<stream(..): Send> + Send>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0391`.
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
trait Foo<'i> {
|
||||
fn implicit_capture_early<'a: 'a>() -> impl Sized {}
|
||||
//~^ [Self: o, 'i: o, 'a: *, 'a: o, 'i: o]
|
||||
//~^ [Self: o, 'i: o, 'a: *, 'i: o, 'a: o]
|
||||
|
||||
fn explicit_capture_early<'a: 'a>() -> impl Sized + use<'i, 'a, Self> {}
|
||||
//~^ [Self: o, 'i: o, 'a: *, 'i: o, 'a: o]
|
||||
|
@ -13,12 +13,12 @@ trait Foo<'i> {
|
|||
//~^ [Self: o, 'i: o, 'a: *, 'i: o]
|
||||
|
||||
fn implicit_capture_late<'a>(_: &'a ()) -> impl Sized {}
|
||||
//~^ [Self: o, 'i: o, 'a: o, 'i: o]
|
||||
//~^ [Self: o, 'i: o, 'i: o, 'a: o]
|
||||
|
||||
fn explicit_capture_late<'a>(_: &'a ()) -> impl Sized + use<'i, 'a, Self> {}
|
||||
//~^ [Self: o, 'i: o, 'i: o, 'a: o]
|
||||
|
||||
fn not_cpatured_late<'a>(_: &'a ()) -> impl Sized + use<'i, Self> {}
|
||||
fn not_captured_late<'a>(_: &'a ()) -> impl Sized + use<'i, Self> {}
|
||||
//~^ [Self: o, 'i: o, 'i: o]
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: [Self: o, 'i: o, 'a: *, 'a: o, 'i: o]
|
||||
error: [Self: o, 'i: o, 'a: *, 'i: o, 'a: o]
|
||||
--> $DIR/variance.rs:6:44
|
||||
|
|
||||
LL | fn implicit_capture_early<'a: 'a>() -> impl Sized {}
|
||||
|
@ -16,7 +16,7 @@ error: [Self: o, 'i: o, 'a: *, 'i: o]
|
|||
LL | fn not_captured_early<'a: 'a>() -> impl Sized + use<'i, Self> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: [Self: o, 'i: o, 'a: o, 'i: o]
|
||||
error: [Self: o, 'i: o, 'i: o, 'a: o]
|
||||
--> $DIR/variance.rs:15:48
|
||||
|
|
||||
LL | fn implicit_capture_late<'a>(_: &'a ()) -> impl Sized {}
|
||||
|
@ -31,7 +31,7 @@ LL | fn explicit_capture_late<'a>(_: &'a ()) -> impl Sized + use<'i, 'a, Sel
|
|||
error: [Self: o, 'i: o, 'i: o]
|
||||
--> $DIR/variance.rs:21:44
|
||||
|
|
||||
LL | fn not_cpatured_late<'a>(_: &'a ()) -> impl Sized + use<'i, Self> {}
|
||||
LL | fn not_captured_late<'a>(_: &'a ()) -> impl Sized + use<'i, Self> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
|
|
@ -8,6 +8,7 @@ trait Test<'a> {}
|
|||
|
||||
pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>;
|
||||
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
|
||||
//~| ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
|
||||
|
||||
impl Trait<'_> for () {
|
||||
type Assoc = ();
|
||||
|
|
|
@ -10,6 +10,18 @@ note: lifetime declared here
|
|||
LL | pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>;
|
||||
| ^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
|
||||
--> $DIR/escaping-bound-var.rs:9:57
|
||||
|
|
||||
LL | pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>;
|
||||
| ^^
|
||||
|
|
||||
note: lifetime declared here
|
||||
--> $DIR/escaping-bound-var.rs:9:25
|
||||
|
|
||||
LL | pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>;
|
||||
| ^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0657`.
|
||||
|
|
|
@ -11,11 +11,11 @@ type NotCapturedEarly<'a> = impl Sized; //~ ['a: *, 'a: o]
|
|||
type CapturedEarly<'a> = impl Sized + Captures<'a>; //~ ['a: *, 'a: o]
|
||||
//~^ ERROR: unconstrained opaque type
|
||||
|
||||
type NotCapturedLate<'a> = dyn for<'b> Iterator<Item = impl Sized>; //~ ['a: *, 'b: o, 'a: o]
|
||||
type NotCapturedLate<'a> = dyn for<'b> Iterator<Item = impl Sized>; //~ ['a: *, 'a: o, 'b: o]
|
||||
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
|
||||
//~| ERROR: unconstrained opaque type
|
||||
|
||||
type Captured<'a> = dyn for<'b> Iterator<Item = impl Sized + Captures<'a>>; //~ ['a: *, 'b: o, 'a: o]
|
||||
type Captured<'a> = dyn for<'b> Iterator<Item = impl Sized + Captures<'a>>; //~ ['a: *, 'a: o, 'b: o]
|
||||
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
|
||||
//~| ERROR: unconstrained opaque type
|
||||
|
||||
|
@ -31,24 +31,24 @@ trait Foo<'i> {
|
|||
}
|
||||
|
||||
impl<'i> Foo<'i> for &'i () {
|
||||
type ImplicitCapture<'a> = impl Sized; //~ ['i: *, 'a: *, 'a: o, 'i: o]
|
||||
type ImplicitCapture<'a> = impl Sized; //~ ['i: *, 'a: *, 'i: o, 'a: o]
|
||||
//~^ ERROR: unconstrained opaque type
|
||||
|
||||
type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ ['i: *, 'a: *, 'a: o, 'i: o]
|
||||
type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ ['i: *, 'a: *, 'i: o, 'a: o]
|
||||
//~^ ERROR: unconstrained opaque type
|
||||
|
||||
type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ ['i: *, 'a: *, 'a: o, 'i: o]
|
||||
type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ ['i: *, 'a: *, 'i: o, 'a: o]
|
||||
//~^ ERROR: unconstrained opaque type
|
||||
}
|
||||
|
||||
impl<'i> Foo<'i> for () {
|
||||
type ImplicitCapture<'a> = impl Sized; //~ ['i: *, 'a: *, 'a: o, 'i: o]
|
||||
type ImplicitCapture<'a> = impl Sized; //~ ['i: *, 'a: *, 'i: o, 'a: o]
|
||||
//~^ ERROR: unconstrained opaque type
|
||||
|
||||
type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ ['i: *, 'a: *, 'a: o, 'i: o]
|
||||
type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ ['i: *, 'a: *, 'i: o, 'a: o]
|
||||
//~^ ERROR: unconstrained opaque type
|
||||
|
||||
type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ ['i: *, 'a: *, 'a: o, 'i: o]
|
||||
type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ ['i: *, 'a: *, 'i: o, 'a: o]
|
||||
//~^ ERROR: unconstrained opaque type
|
||||
}
|
||||
|
||||
|
|
|
@ -122,13 +122,13 @@ error: ['a: *, 'a: o]
|
|||
LL | type CapturedEarly<'a> = impl Sized + Captures<'a>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: ['a: *, 'b: o, 'a: o]
|
||||
error: ['a: *, 'a: o, 'b: o]
|
||||
--> $DIR/variance.rs:14:56
|
||||
|
|
||||
LL | type NotCapturedLate<'a> = dyn for<'b> Iterator<Item = impl Sized>;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: ['a: *, 'b: o, 'a: o]
|
||||
error: ['a: *, 'a: o, 'b: o]
|
||||
--> $DIR/variance.rs:18:49
|
||||
|
|
||||
LL | type Captured<'a> = dyn for<'b> Iterator<Item = impl Sized + Captures<'a>>;
|
||||
|
@ -140,37 +140,37 @@ error: ['a: *, 'b: *, T: o, 'a: o, 'b: o]
|
|||
LL | type Bar<'a, 'b: 'b, T> = impl Sized;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: ['i: *, 'a: *, 'a: o, 'i: o]
|
||||
error: ['i: *, 'a: *, 'i: o, 'a: o]
|
||||
--> $DIR/variance.rs:34:32
|
||||
|
|
||||
LL | type ImplicitCapture<'a> = impl Sized;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: ['i: *, 'a: *, 'a: o, 'i: o]
|
||||
error: ['i: *, 'a: *, 'i: o, 'a: o]
|
||||
--> $DIR/variance.rs:37:42
|
||||
|
|
||||
LL | type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: ['i: *, 'a: *, 'a: o, 'i: o]
|
||||
error: ['i: *, 'a: *, 'i: o, 'a: o]
|
||||
--> $DIR/variance.rs:40:39
|
||||
|
|
||||
LL | type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: ['i: *, 'a: *, 'a: o, 'i: o]
|
||||
error: ['i: *, 'a: *, 'i: o, 'a: o]
|
||||
--> $DIR/variance.rs:45:32
|
||||
|
|
||||
LL | type ImplicitCapture<'a> = impl Sized;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: ['i: *, 'a: *, 'a: o, 'i: o]
|
||||
error: ['i: *, 'a: *, 'i: o, 'a: o]
|
||||
--> $DIR/variance.rs:48:42
|
||||
|
|
||||
LL | type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: ['i: *, 'a: *, 'a: o, 'i: o]
|
||||
error: ['i: *, 'a: *, 'i: o, 'a: o]
|
||||
--> $DIR/variance.rs:51:39
|
||||
|
|
||||
LL | type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>;
|
||||
|
|
Loading…
Add table
Reference in a new issue