Rollup merge of #99728 - cjgillot:ast-lifetimes-anon-clean, r=petrochenkov

Clean up HIR-based lifetime resolution

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

Fixes #98932.

r? `@petrochenkov`
This commit is contained in:
Guillaume Gomez 2022-07-27 17:55:07 +02:00 committed by GitHub
commit c37ee1a7e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 202 additions and 747 deletions

View file

@ -1883,29 +1883,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} }
hir::LifetimeName::Param(param, ParamName::Fresh) hir::LifetimeName::Param(param, ParamName::Fresh)
} }
LifetimeRes::Anonymous { binder, elided } => { LifetimeRes::Infer => hir::LifetimeName::Infer,
let mut l_name = None;
if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() {
if !captured_lifetimes.binders_to_ignore.contains(&binder) {
let p_id = self.next_node_id();
let p_def_id = self.create_def(
captured_lifetimes.parent_def_id,
p_id,
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
);
captured_lifetimes
.captures
.insert(p_def_id, (span, p_id, ParamName::Fresh, res));
l_name = Some(hir::LifetimeName::Param(p_def_id, ParamName::Fresh));
}
self.captured_lifetimes = Some(captured_lifetimes);
};
l_name.unwrap_or(if elided {
hir::LifetimeName::Implicit
} else {
hir::LifetimeName::Underscore
})
}
LifetimeRes::Static => hir::LifetimeName::Static, LifetimeRes::Static => hir::LifetimeName::Static,
LifetimeRes::Error => hir::LifetimeName::Error, LifetimeRes::Error => hir::LifetimeName::Error,
res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span), res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span),

View file

@ -589,8 +589,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
hir::LifetimeName::Param(_, hir::ParamName::Fresh) hir::LifetimeName::Param(_, hir::ParamName::Fresh)
| hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::ImplicitObjectLifetimeDefault
| hir::LifetimeName::Implicit | hir::LifetimeName::Infer => {
| hir::LifetimeName::Underscore => {
// In this case, the user left off the lifetime; so // In this case, the user left off the lifetime; so
// they wrote something like: // they wrote something like:
// //

View file

@ -738,13 +738,8 @@ pub enum LifetimeRes {
binder: NodeId, binder: NodeId,
}, },
/// This variant is used for anonymous lifetimes that we did not resolve during /// This variant is used for anonymous lifetimes that we did not resolve during
/// late resolution. Shifting the work to the HIR lifetime resolver. /// late resolution. Those lifetimes will be inferred by typechecking.
Anonymous { Infer,
/// Id of the introducing place. See `Param`.
binder: NodeId,
/// Whether this lifetime was spelled or elided.
elided: bool,
},
/// Explicit `'static` lifetime. /// Explicit `'static` lifetime.
Static, Static,
/// Resolution failure. /// Resolution failure.

View file

@ -90,9 +90,6 @@ pub enum LifetimeName {
/// User-given names or fresh (synthetic) names. /// User-given names or fresh (synthetic) names.
Param(LocalDefId, ParamName), Param(LocalDefId, ParamName),
/// User wrote nothing (e.g., the lifetime in `&u32`).
Implicit,
/// Implicit lifetime in a context like `dyn Foo`. This is /// Implicit lifetime in a context like `dyn Foo`. This is
/// distinguished from implicit lifetimes elsewhere because the /// distinguished from implicit lifetimes elsewhere because the
/// lifetime that they default to must appear elsewhere within the /// lifetime that they default to must appear elsewhere within the
@ -110,8 +107,9 @@ pub enum LifetimeName {
/// that was already reported. /// that was already reported.
Error, Error,
/// User wrote specifies `'_`. /// User wrote an anonymous lifetime, either `'_` or nothing.
Underscore, /// The semantics of this lifetime should be inferred by typechecking code.
Infer,
/// User wrote `'static`. /// User wrote `'static`.
Static, Static,
@ -120,10 +118,8 @@ pub enum LifetimeName {
impl LifetimeName { impl LifetimeName {
pub fn ident(&self) -> Ident { pub fn ident(&self) -> Ident {
match *self { match *self {
LifetimeName::ImplicitObjectLifetimeDefault LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Error => Ident::empty(),
| LifetimeName::Implicit LifetimeName::Infer => Ident::with_dummy_span(kw::UnderscoreLifetime),
| LifetimeName::Error => Ident::empty(),
LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime),
LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime), LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime),
LifetimeName::Param(_, param_name) => param_name.ident(), LifetimeName::Param(_, param_name) => param_name.ident(),
} }
@ -132,8 +128,7 @@ impl LifetimeName {
pub fn is_anonymous(&self) -> bool { pub fn is_anonymous(&self) -> bool {
match *self { match *self {
LifetimeName::ImplicitObjectLifetimeDefault LifetimeName::ImplicitObjectLifetimeDefault
| LifetimeName::Implicit | LifetimeName::Infer
| LifetimeName::Underscore
| LifetimeName::Param(_, ParamName::Fresh) | LifetimeName::Param(_, ParamName::Fresh)
| LifetimeName::Error => true, | LifetimeName::Error => true,
LifetimeName::Static | LifetimeName::Param(..) => false, LifetimeName::Static | LifetimeName::Param(..) => false,
@ -142,9 +137,7 @@ impl LifetimeName {
pub fn is_elided(&self) -> bool { pub fn is_elided(&self) -> bool {
match self { match self {
LifetimeName::ImplicitObjectLifetimeDefault LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Infer => true,
| LifetimeName::Implicit
| LifetimeName::Underscore => true,
// It might seem surprising that `Fresh` counts as // It might seem surprising that `Fresh` counts as
// *not* elided -- but this is because, as far as the code // *not* elided -- but this is because, as far as the code

View file

@ -496,9 +496,8 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime
| LifetimeName::Param(_, ParamName::Error) | LifetimeName::Param(_, ParamName::Error)
| LifetimeName::Static | LifetimeName::Static
| LifetimeName::Error | LifetimeName::Error
| LifetimeName::Implicit
| LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::ImplicitObjectLifetimeDefault
| LifetimeName::Underscore => {} | LifetimeName::Infer => {}
} }
} }

View file

@ -100,23 +100,6 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
// the lifetime of the TyRptr // the lifetime of the TyRptr
let hir_id = lifetime.hir_id; let hir_id = lifetime.hir_id;
match (self.tcx.named_region(hir_id), self.bound_region) { match (self.tcx.named_region(hir_id), self.bound_region) {
// Find the index of the anonymous region that was part of the
// error. We will then search the function parameters for a bound
// region at the right depth with the same index
(
Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)),
ty::BrAnon(br_index),
) => {
debug!(
"LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}",
debruijn_index, anon_index, br_index
);
if debruijn_index == self.current_index && anon_index == br_index {
self.found_type = Some(arg);
return; // we can stop visiting now
}
}
// Find the index of the named region that was part of the // Find the index of the named region that was part of the
// error. We will then search the function parameters for a bound // error. We will then search the function parameters for a bound
// region at the right depth with the same index // region at the right depth with the same index
@ -151,8 +134,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
rl::Region::Static rl::Region::Static
| rl::Region::Free(_, _) | rl::Region::Free(_, _)
| rl::Region::EarlyBound(_, _) | rl::Region::EarlyBound(_, _)
| rl::Region::LateBound(_, _, _) | rl::Region::LateBound(_, _, _),
| rl::Region::LateBoundAnon(_, _, _),
) )
| None, | None,
_, _,
@ -206,16 +188,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
match (self.tcx.named_region(lifetime.hir_id), self.bound_region) { match (self.tcx.named_region(lifetime.hir_id), self.bound_region) {
// the lifetime of the TyPath! // the lifetime of the TyPath!
(
Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)),
ty::BrAnon(br_index),
) => {
if debruijn_index == self.current_index && anon_index == br_index {
self.found_it = true;
return;
}
}
(Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => {
debug!("EarlyBound id={:?} def_id={:?}", id, def_id); debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
if id == def_id { if id == def_id {
@ -239,7 +211,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
rl::Region::Static rl::Region::Static
| rl::Region::EarlyBound(_, _) | rl::Region::EarlyBound(_, _)
| rl::Region::LateBound(_, _, _) | rl::Region::LateBound(_, _, _)
| rl::Region::LateBoundAnon(_, _, _)
| rl::Region::Free(_, _), | rl::Region::Free(_, _),
) )
| None, | None,

View file

@ -12,7 +12,6 @@ pub enum Region {
Static, Static,
EarlyBound(/* index */ u32, /* lifetime decl */ DefId), EarlyBound(/* index */ u32, /* lifetime decl */ DefId),
LateBound(ty::DebruijnIndex, /* late-bound index */ u32, /* lifetime decl */ DefId), LateBound(ty::DebruijnIndex, /* late-bound index */ u32, /* lifetime decl */ DefId),
LateBoundAnon(ty::DebruijnIndex, /* late-bound index */ u32, /* anon index */ u32),
Free(DefId, /* lifetime decl */ DefId), Free(DefId, /* lifetime decl */ DefId),
} }

View file

@ -262,9 +262,6 @@ enum LifetimeRibKind {
/// error on default object bounds (e.g., `Box<dyn Foo>`). /// error on default object bounds (e.g., `Box<dyn Foo>`).
AnonymousReportError, AnonymousReportError,
/// Pass responsibility to `resolve_lifetime` code for all cases.
AnonymousPassThrough(NodeId),
/// Replace all anonymous lifetimes by provided lifetime. /// Replace all anonymous lifetimes by provided lifetime.
Elided(LifetimeRes), Elided(LifetimeRes),
@ -698,14 +695,25 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
}, },
|this| { |this| {
this.visit_generic_params(&bare_fn.generic_params, false); this.visit_generic_params(&bare_fn.generic_params, false);
this.with_lifetime_rib(
LifetimeRibKind::AnonymousCreateParameter {
binder: ty.id,
report_in_path: false,
},
|this| {
this.resolve_fn_signature( this.resolve_fn_signature(
ty.id, ty.id,
None,
false, false,
// We don't need to deal with patterns in parameters, because // We don't need to deal with patterns in parameters, because
// they are not possible for foreign or bodiless functions. // they are not possible for foreign or bodiless functions.
bare_fn.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), bare_fn
.decl
.inputs
.iter()
.map(|Param { ty, .. }| (None, &**ty)),
&bare_fn.decl.output, &bare_fn.decl.output,
)
},
); );
}, },
) )
@ -785,12 +793,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
| FnKind::Fn(_, _, sig, _, generics, None) => { | FnKind::Fn(_, _, sig, _, generics, None) => {
self.visit_fn_header(&sig.header); self.visit_fn_header(&sig.header);
self.visit_generics(generics); self.visit_generics(generics);
self.resolve_fn_signature( self.with_lifetime_rib(
LifetimeRibKind::AnonymousCreateParameter {
binder: fn_id,
report_in_path: false,
},
|this| {
this.resolve_fn_signature(
fn_id, fn_id,
None,
sig.decl.has_self(), sig.decl.has_self(),
sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)),
&sig.decl.output, &sig.decl.output,
)
},
); );
return; return;
} }
@ -815,15 +830,22 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
let declaration = &sig.decl; let declaration = &sig.decl;
let async_node_id = sig.header.asyncness.opt_return_id(); let async_node_id = sig.header.asyncness.opt_return_id();
this.with_lifetime_rib(
LifetimeRibKind::AnonymousCreateParameter {
binder: fn_id,
report_in_path: async_node_id.is_some(),
},
|this| {
this.resolve_fn_signature( this.resolve_fn_signature(
fn_id, fn_id,
async_node_id,
declaration.has_self(), declaration.has_self(),
declaration declaration
.inputs .inputs
.iter() .iter()
.map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)),
&declaration.output, &declaration.output,
)
},
); );
// Construct the list of in-scope lifetime parameters for async lowering. // Construct the list of in-scope lifetime parameters for async lowering.
@ -868,7 +890,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
let previous_state = replace(&mut this.in_func_body, true); let previous_state = replace(&mut this.in_func_body, true);
// Resolve the function body, potentially inside the body of an async closure // Resolve the function body, potentially inside the body of an async closure
this.with_lifetime_rib( this.with_lifetime_rib(
LifetimeRibKind::AnonymousPassThrough(fn_id), LifetimeRibKind::Elided(LifetimeRes::Infer),
|this| this.visit_block(body), |this| this.visit_block(body),
); );
@ -896,7 +918,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
this.with_lifetime_rib( this.with_lifetime_rib(
match binder { match binder {
ClosureBinder::NotPresent => { ClosureBinder::NotPresent => {
LifetimeRibKind::AnonymousPassThrough(fn_id) LifetimeRibKind::Elided(LifetimeRes::Infer)
} }
ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError, ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError,
}, },
@ -908,7 +930,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
let previous_state = replace(&mut this.in_func_body, true); let previous_state = replace(&mut this.in_func_body, true);
// Resolve the function body, potentially inside the body of an async closure // Resolve the function body, potentially inside the body of an async closure
this.with_lifetime_rib( this.with_lifetime_rib(
LifetimeRibKind::AnonymousPassThrough(fn_id), LifetimeRibKind::Elided(LifetimeRes::Infer),
|this| this.visit_expr(body), |this| this.visit_expr(body),
); );
@ -1038,12 +1060,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
kind: LifetimeBinderKind::PolyTrait, kind: LifetimeBinderKind::PolyTrait,
.. ..
} => { } => {
self.resolve_fn_signature( self.with_lifetime_rib(
LifetimeRibKind::AnonymousCreateParameter {
binder,
report_in_path: false,
},
|this| {
this.resolve_fn_signature(
binder, binder,
None,
false, false,
p_args.inputs.iter().map(|ty| (None, &**ty)), p_args.inputs.iter().map(|ty| (None, &**ty)),
&p_args.output, &p_args.output,
)
},
); );
break; break;
} }
@ -1053,8 +1082,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
visit::walk_generic_args(self, path_span, args); visit::walk_generic_args(self, path_span, args);
break; break;
} }
LifetimeRibKind::AnonymousPassThrough(..) LifetimeRibKind::AnonymousCreateParameter { .. }
| LifetimeRibKind::AnonymousCreateParameter { .. }
| LifetimeRibKind::AnonymousReportError | LifetimeRibKind::AnonymousReportError
| LifetimeRibKind::Elided(_) | LifetimeRibKind::Elided(_)
| LifetimeRibKind::ElisionFailure | LifetimeRibKind::ElisionFailure
@ -1415,8 +1443,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
| LifetimeRibKind::AnonymousReportError | LifetimeRibKind::AnonymousReportError
| LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many), | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many),
// An anonymous lifetime is legal here, go ahead. // An anonymous lifetime is legal here, go ahead.
LifetimeRibKind::AnonymousPassThrough(_) LifetimeRibKind::AnonymousCreateParameter { .. } => {
| LifetimeRibKind::AnonymousCreateParameter { .. } => {
Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt }) Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt })
} }
// Only report if eliding the lifetime would have the same // Only report if eliding the lifetime would have the same
@ -1527,14 +1554,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate);
return; return;
} }
LifetimeRibKind::AnonymousPassThrough(node_id) => {
self.record_lifetime_res(
lifetime.id,
LifetimeRes::Anonymous { binder: node_id, elided },
elision_candidate,
);
return;
}
LifetimeRibKind::Elided(res) => { LifetimeRibKind::Elided(res) => {
self.record_lifetime_res(lifetime.id, res, elision_candidate); self.record_lifetime_res(lifetime.id, res, elision_candidate);
return; return;
@ -1632,6 +1651,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
| PathSource::Struct | PathSource::Struct
| PathSource::TupleStruct(..) => false, | PathSource::TupleStruct(..) => false,
}; };
if !missing && !segment.has_generic_args {
continue;
}
let elided_lifetime_span = if segment.has_generic_args { let elided_lifetime_span = if segment.has_generic_args {
// If there are brackets, but not generic arguments, then use the opening bracket // If there are brackets, but not generic arguments, then use the opening bracket
@ -1653,37 +1675,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
if !missing { if !missing {
// Do not create a parameter for patterns and expressions. // Do not create a parameter for patterns and expressions.
for rib in self.lifetime_ribs.iter().rev() {
match rib.kind {
LifetimeRibKind::AnonymousPassThrough(binder) => {
let res = LifetimeRes::Anonymous { binder, elided: true };
for id in node_ids { for id in node_ids {
self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named); self.record_lifetime_res(
} id,
break; LifetimeRes::Infer,
} LifetimeElisionCandidate::Named,
// `LifetimeRes::Error`, which would usually be used in the case of );
// `ReportError`, is unsuitable here, as we don't emit an error yet. Instead,
// we simply resolve to an implicit lifetime, which will be checked later, at
// which point a suitable error will be emitted.
LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => {
// FIXME(cjgillot) This resolution is wrong, but this does not matter
// since these cases are erroneous anyway. Lifetime resolution should
// emit a "missing lifetime specifier" diagnostic.
let res =
LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true };
for id in node_ids {
self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named);
}
break;
}
LifetimeRibKind::AnonymousCreateParameter { .. }
| LifetimeRibKind::Elided(_)
| LifetimeRibKind::ElisionFailure
| LifetimeRibKind::Generics { .. }
| LifetimeRibKind::ConstGeneric
| LifetimeRibKind::AnonConst => {}
}
} }
continue; continue;
} }
@ -1750,19 +1747,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
} }
break; break;
} }
// `PassThrough` is the normal case.
LifetimeRibKind::AnonymousPassThrough(binder) => {
let res = LifetimeRes::Anonymous { binder, elided: true };
let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
for id in node_ids {
self.record_lifetime_res(
id,
res,
replace(&mut candidate, LifetimeElisionCandidate::Ignore),
);
}
break;
}
LifetimeRibKind::Elided(res) => { LifetimeRibKind::Elided(res) => {
let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
for id in node_ids { for id in node_ids {
@ -1837,15 +1821,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
) )
} }
match res { match res {
LifetimeRes::Param { .. } LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => {
| LifetimeRes::Fresh { .. }
| LifetimeRes::Anonymous { .. }
| LifetimeRes::Static => {
if let Some(ref mut candidates) = self.lifetime_elision_candidates { if let Some(ref mut candidates) = self.lifetime_elision_candidates {
candidates.insert(res, candidate); candidates.insert(res, candidate);
} }
} }
LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {} LifetimeRes::Infer | LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {}
} }
} }
@ -1864,18 +1845,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
fn resolve_fn_signature( fn resolve_fn_signature(
&mut self, &mut self,
fn_id: NodeId, fn_id: NodeId,
async_node_id: Option<NodeId>,
has_self: bool, has_self: bool,
inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone, inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone,
output_ty: &'ast FnRetTy, output_ty: &'ast FnRetTy,
) { ) {
// Add each argument to the rib. // Add each argument to the rib.
let parameter_rib = LifetimeRibKind::AnonymousCreateParameter { let elision_lifetime = self.resolve_fn_params(has_self, inputs);
binder: fn_id,
report_in_path: async_node_id.is_some(),
};
let elision_lifetime =
self.with_lifetime_rib(parameter_rib, |this| this.resolve_fn_params(has_self, inputs));
debug!(?elision_lifetime); debug!(?elision_lifetime);
let outer_failures = take(&mut self.diagnostic_metadata.current_elision_failures); let outer_failures = take(&mut self.diagnostic_metadata.current_elision_failures);
@ -2268,9 +2243,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| {
this.visit_ty(ty); this.visit_ty(ty);
}); });
this.with_lifetime_rib( this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
LifetimeRibKind::AnonymousPassThrough(item.id),
|this| {
if let Some(expr) = expr { if let Some(expr) = expr {
let constant_item_kind = match item.kind { let constant_item_kind = match item.kind {
ItemKind::Const(..) => ConstantItemKind::Const, ItemKind::Const(..) => ConstantItemKind::Const,
@ -2286,8 +2259,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|this| this.visit_expr(expr), |this| this.visit_expr(expr),
); );
} }
}, });
);
}); });
} }
@ -2544,7 +2516,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
// Type parameters can already be used and as associated consts are // Type parameters can already be used and as associated consts are
// not used as part of the type system, this is far less surprising. // not used as part of the type system, this is far less surprising.
self.with_lifetime_rib( self.with_lifetime_rib(
LifetimeRibKind::AnonymousPassThrough(item.id), LifetimeRibKind::Elided(LifetimeRes::Infer),
|this| { |this| {
this.with_constant_rib( this.with_constant_rib(
IsRepeatExpr::No, IsRepeatExpr::No,
@ -2717,17 +2689,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
// //
// Type parameters can already be used and as associated consts are // Type parameters can already be used and as associated consts are
// not used as part of the type system, this is far less surprising. // not used as part of the type system, this is far less surprising.
self.with_lifetime_rib( self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
LifetimeRibKind::AnonymousPassThrough(item.id),
|this| {
this.with_constant_rib( this.with_constant_rib(
IsRepeatExpr::No, IsRepeatExpr::No,
HasGenericParams::Yes, HasGenericParams::Yes,
None, None,
|this| this.visit_expr(expr), |this| this.visit_expr(expr),
) )
}, });
);
} }
} }
AssocItemKind::Fn(box Fn { generics, .. }) => { AssocItemKind::Fn(box Fn { generics, .. }) => {

View file

@ -1,4 +1,3 @@
// ignore-tidy-filelength
//! Resolution of early vs late bound lifetimes. //! Resolution of early vs late bound lifetimes.
//! //!
//! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this //! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this
@ -21,10 +20,9 @@ use rustc_middle::hir::nested_filter;
use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::middle::resolve_lifetime::*;
use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::symbol::{sym, Ident};
use rustc_span::Span; use rustc_span::Span;
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::Cell;
use std::fmt; use std::fmt;
use std::mem::take; use std::mem::take;
@ -33,8 +31,6 @@ trait RegionExt {
fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region); fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region);
fn late_anon(named_late_bound_vars: u32, index: &Cell<u32>) -> Region;
fn id(&self) -> Option<DefId>; fn id(&self) -> Option<DefId>;
fn shifted(self, amount: u32) -> Region; fn shifted(self, amount: u32) -> Region;
@ -65,16 +61,9 @@ impl RegionExt for Region {
(def_id, Region::LateBound(depth, idx, def_id.to_def_id())) (def_id, Region::LateBound(depth, idx, def_id.to_def_id()))
} }
fn late_anon(named_late_bound_vars: u32, index: &Cell<u32>) -> Region {
let i = index.get();
index.set(i + 1);
let depth = ty::INNERMOST;
Region::LateBoundAnon(depth, named_late_bound_vars + i, i)
}
fn id(&self) -> Option<DefId> { fn id(&self) -> Option<DefId> {
match *self { match *self {
Region::Static | Region::LateBoundAnon(..) => None, Region::Static => None,
Region::EarlyBound(_, id) | Region::LateBound(_, _, id) | Region::Free(_, id) => { Region::EarlyBound(_, id) | Region::LateBound(_, _, id) | Region::Free(_, id) => {
Some(id) Some(id)
@ -87,9 +76,6 @@ impl RegionExt for Region {
Region::LateBound(debruijn, idx, id) => { Region::LateBound(debruijn, idx, id) => {
Region::LateBound(debruijn.shifted_in(amount), idx, id) Region::LateBound(debruijn.shifted_in(amount), idx, id)
} }
Region::LateBoundAnon(debruijn, index, anon_index) => {
Region::LateBoundAnon(debruijn.shifted_in(amount), index, anon_index)
}
_ => self, _ => self,
} }
} }
@ -99,9 +85,6 @@ impl RegionExt for Region {
Region::LateBound(debruijn, index, id) => { Region::LateBound(debruijn, index, id) => {
Region::LateBound(debruijn.shifted_out_to_binder(binder), index, id) Region::LateBound(debruijn.shifted_out_to_binder(binder), index, id)
} }
Region::LateBoundAnon(debruijn, index, anon_index) => {
Region::LateBoundAnon(debruijn.shifted_out_to_binder(binder), index, anon_index)
}
_ => self, _ => self,
} }
} }
@ -193,10 +176,6 @@ enum Scope<'a> {
s: ScopeRef<'a>, s: ScopeRef<'a>,
/// In some cases not allowing late bounds allows us to avoid ICEs.
/// This is almost ways set to true.
allow_late_bound: bool,
/// If this binder comes from a where clause, specify how it was created. /// If this binder comes from a where clause, specify how it was created.
/// This is used to diagnose inaccessible lifetimes in APIT: /// This is used to diagnose inaccessible lifetimes in APIT:
/// ```ignore (illustrative) /// ```ignore (illustrative)
@ -215,9 +194,8 @@ enum Scope<'a> {
}, },
/// A scope which either determines unspecified lifetimes or errors /// A scope which either determines unspecified lifetimes or errors
/// on them (e.g., due to ambiguity). For more details, see `Elide`. /// on them (e.g., due to ambiguity).
Elision { Elision {
elide: Elide,
s: ScopeRef<'a>, s: ScopeRef<'a>,
}, },
@ -273,7 +251,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
opaque_type_parent, opaque_type_parent,
scope_type, scope_type,
hir_id, hir_id,
allow_late_bound,
where_bound_origin, where_bound_origin,
s: _, s: _,
} => f } => f
@ -283,16 +260,13 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
.field("opaque_type_parent", opaque_type_parent) .field("opaque_type_parent", opaque_type_parent)
.field("scope_type", scope_type) .field("scope_type", scope_type)
.field("hir_id", hir_id) .field("hir_id", hir_id)
.field("allow_late_bound", allow_late_bound)
.field("where_bound_origin", where_bound_origin) .field("where_bound_origin", where_bound_origin)
.field("s", &"..") .field("s", &"..")
.finish(), .finish(),
Scope::Body { id, s: _ } => { Scope::Body { id, s: _ } => {
f.debug_struct("Body").field("id", id).field("s", &"..").finish() f.debug_struct("Body").field("id", id).field("s", &"..").finish()
} }
Scope::Elision { elide, s: _ } => { Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(),
f.debug_struct("Elision").field("elide", elide).field("s", &"..").finish()
}
Scope::ObjectLifetimeDefault { lifetime, s: _ } => f Scope::ObjectLifetimeDefault { lifetime, s: _ } => f
.debug_struct("ObjectLifetimeDefault") .debug_struct("ObjectLifetimeDefault")
.field("lifetime", lifetime) .field("lifetime", lifetime)
@ -309,21 +283,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
} }
} }
#[derive(Clone, Debug)]
enum Elide {
/// Use a fresh anonymous late-bound lifetime each time, by
/// incrementing the counter to generate sequential indices. All
/// anonymous lifetimes must start *after* named bound vars.
FreshLateAnon(u32, Cell<u32>),
/// Always use this one lifetime.
Exact(Region),
/// Less or more than one lifetime were found, error on unspecified.
Error,
/// Forbid lifetime elision inside of a larger scope where it would be
/// permitted. For example, in let position impl trait.
Forbid,
}
type ScopeRef<'a> = &'a Scope<'a>; type ScopeRef<'a> = &'a Scope<'a>;
const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root;
@ -486,9 +445,6 @@ fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::
let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local())); let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local()));
ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name)) ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name))
} }
Region::LateBoundAnon(_, _, anon_idx) => {
ty::BoundVariableKind::Region(ty::BrAnon(*anon_idx))
}
_ => bug!("{:?} is not a late region", region), _ => bug!("{:?} is not a late region", region),
} }
} }
@ -623,7 +579,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
next_early_index, next_early_index,
opaque_type_parent: false, opaque_type_parent: false,
scope_type: BinderScopeType::Normal, scope_type: BinderScopeType::Normal,
allow_late_bound: true,
where_bound_origin: None, where_bound_origin: None,
}; };
@ -664,8 +619,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
} }
hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => { hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => {
// No lifetime parameters, but implied 'static. // No lifetime parameters, but implied 'static.
let scope = Scope::Elision { elide: Elide::Exact(Region::Static), s: ROOT_SCOPE }; self.with(Scope::Elision { s: self.scope }, |this| {
self.with(scope, |this| intravisit::walk_item(this, item)); intravisit::walk_item(this, item)
});
} }
hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => { hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => {
// Opaque types are visited when we visit the // Opaque types are visited when we visit the
@ -741,7 +697,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
opaque_type_parent: true, opaque_type_parent: true,
scope_type: BinderScopeType::Normal, scope_type: BinderScopeType::Normal,
s: ROOT_SCOPE, s: ROOT_SCOPE,
allow_late_bound: false,
where_bound_origin: None, where_bound_origin: None,
}; };
self.with(scope, |this| { self.with(scope, |this| {
@ -794,7 +749,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
next_early_index, next_early_index,
opaque_type_parent: false, opaque_type_parent: false,
scope_type: BinderScopeType::Normal, scope_type: BinderScopeType::Normal,
allow_late_bound: true,
where_bound_origin: None, where_bound_origin: None,
}; };
self.with(scope, |this| { self.with(scope, |this| {
@ -819,13 +773,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
// `Box<dyn Debug + 'static>`. // `Box<dyn Debug + 'static>`.
self.resolve_object_lifetime_default(lifetime) self.resolve_object_lifetime_default(lifetime)
} }
LifetimeName::Implicit | LifetimeName::Underscore => { LifetimeName::Infer => {
// If the user writes `'_`, we use the *ordinary* elision // If the user writes `'_`, we use the *ordinary* elision
// rules. So the `'_` in e.g., `Box<dyn Debug + '_>` will be // rules. So the `'_` in e.g., `Box<dyn Debug + '_>` will be
// resolved the same as the `'_` in `&'_ Foo`. // resolved the same as the `'_` in `&'_ Foo`.
// //
// cc #48468 // cc #48468
self.resolve_elided_lifetimes(&[lifetime])
} }
LifetimeName::Param(..) | LifetimeName::Static => { LifetimeName::Param(..) | LifetimeName::Static => {
// If the user wrote an explicit name, use that. // If the user wrote an explicit name, use that.
@ -860,7 +813,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
// position impl Trait // position impl Trait
let scope = Scope::TraitRefBoundary { s: self.scope }; let scope = Scope::TraitRefBoundary { s: self.scope };
self.with(scope, |this| { self.with(scope, |this| {
let scope = Scope::Elision { elide: Elide::Forbid, s: this.scope }; let scope = Scope::Elision { s: this.scope };
this.with(scope, |this| { this.with(scope, |this| {
intravisit::walk_item(this, opaque_ty); intravisit::walk_item(this, opaque_ty);
}) })
@ -936,7 +889,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
let mut index = self.next_early_index_for_opaque_type(); let mut index = self.next_early_index_for_opaque_type();
debug!(?index); debug!(?index);
let mut elision = None;
let mut lifetimes = FxIndexMap::default(); let mut lifetimes = FxIndexMap::default();
let mut non_lifetime_count = 0; let mut non_lifetime_count = 0;
debug!(?generics.params); debug!(?generics.params);
@ -945,15 +897,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
GenericParamKind::Lifetime { .. } => { GenericParamKind::Lifetime { .. } => {
let (def_id, reg) = Region::early(self.tcx.hir(), &mut index, &param); let (def_id, reg) = Region::early(self.tcx.hir(), &mut index, &param);
lifetimes.insert(def_id, reg); lifetimes.insert(def_id, reg);
if let hir::ParamName::Plain(Ident {
name: kw::UnderscoreLifetime,
..
}) = param.name
{
// Pick the elided lifetime "definition" if one exists
// and use it to make an elision scope.
elision = Some(reg);
}
} }
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
non_lifetime_count += 1; non_lifetime_count += 1;
@ -963,31 +906,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
let next_early_index = index + non_lifetime_count; let next_early_index = index + non_lifetime_count;
self.map.late_bound_vars.insert(ty.hir_id, vec![]); self.map.late_bound_vars.insert(ty.hir_id, vec![]);
if let Some(elision_region) = elision {
let scope =
Scope::Elision { elide: Elide::Exact(elision_region), s: self.scope };
self.with(scope, |this| {
let scope = Scope::Binder {
hir_id: ty.hir_id,
lifetimes,
next_early_index,
s: this.scope,
opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: false,
where_bound_origin: None,
};
this.with(scope, |this| {
this.visit_generics(generics);
let scope = Scope::TraitRefBoundary { s: this.scope };
this.with(scope, |this| {
for bound in bounds {
this.visit_param_bound(bound);
}
})
});
});
} else {
let scope = Scope::Binder { let scope = Scope::Binder {
hir_id: ty.hir_id, hir_id: ty.hir_id,
lifetimes, lifetimes,
@ -995,7 +913,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
s: self.scope, s: self.scope,
opaque_type_parent: false, opaque_type_parent: false,
scope_type: BinderScopeType::Normal, scope_type: BinderScopeType::Normal,
allow_late_bound: false,
where_bound_origin: None, where_bound_origin: None,
}; };
self.with(scope, |this| { self.with(scope, |this| {
@ -1008,7 +925,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
}) })
}); });
} }
}
_ => intravisit::walk_ty(self, ty), _ => intravisit::walk_ty(self, ty),
} }
} }
@ -1051,7 +967,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
s: self.scope, s: self.scope,
opaque_type_parent: true, opaque_type_parent: true,
scope_type: BinderScopeType::Normal, scope_type: BinderScopeType::Normal,
allow_late_bound: false,
where_bound_origin: None, where_bound_origin: None,
}; };
self.with(scope, |this| { self.with(scope, |this| {
@ -1113,7 +1028,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
s: self.scope, s: self.scope,
opaque_type_parent: true, opaque_type_parent: true,
scope_type: BinderScopeType::Normal, scope_type: BinderScopeType::Normal,
allow_late_bound: true,
where_bound_origin: None, where_bound_origin: None,
}; };
self.with(scope, |this| { self.with(scope, |this| {
@ -1135,15 +1049,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
#[tracing::instrument(level = "debug", skip(self))] #[tracing::instrument(level = "debug", skip(self))]
fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
match lifetime_ref.name { match lifetime_ref.name {
hir::LifetimeName::ImplicitObjectLifetimeDefault
| hir::LifetimeName::Implicit
| hir::LifetimeName::Underscore => self.resolve_elided_lifetimes(&[lifetime_ref]),
hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static), hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static),
hir::LifetimeName::Param(param_def_id, _) => { hir::LifetimeName::Param(param_def_id, _) => {
self.resolve_lifetime_ref(param_def_id, lifetime_ref) self.resolve_lifetime_ref(param_def_id, lifetime_ref)
} }
// If we've already reported an error, just ignore `lifetime_ref`. // If we've already reported an error, just ignore `lifetime_ref`.
hir::LifetimeName::Error => {} hir::LifetimeName::Error => {}
// Those will be resolved by typechecking.
hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => {}
} }
} }
@ -1156,12 +1069,21 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
} }
} }
fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) { fn visit_fn(
&mut self,
fk: intravisit::FnKind<'tcx>,
fd: &'tcx hir::FnDecl<'tcx>,
body_id: hir::BodyId,
_: Span,
_: hir::HirId,
) {
let output = match fd.output { let output = match fd.output {
hir::FnRetTy::DefaultReturn(_) => None, hir::FnRetTy::DefaultReturn(_) => None,
hir::FnRetTy::Return(ref ty) => Some(&**ty), hir::FnRetTy::Return(ref ty) => Some(&**ty),
}; };
self.visit_fn_like_elision(&fd.inputs, output); self.visit_fn_like_elision(&fd.inputs, output, matches!(fk, intravisit::FnKind::Closure));
intravisit::walk_fn_kind(self, fk);
self.visit_nested_body(body_id)
} }
fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) { fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
@ -1219,7 +1141,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
next_early_index, next_early_index,
opaque_type_parent: false, opaque_type_parent: false,
scope_type: BinderScopeType::Normal, scope_type: BinderScopeType::Normal,
allow_late_bound: true,
where_bound_origin: Some(origin), where_bound_origin: Some(origin),
}; };
this.with(scope, |this| { this.with(scope, |this| {
@ -1292,7 +1213,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
next_early_index: self.next_early_index(), next_early_index: self.next_early_index(),
opaque_type_parent: false, opaque_type_parent: false,
scope_type, scope_type,
allow_late_bound: true,
where_bound_origin: None, where_bound_origin: None,
}; };
self.with(scope, |this| { self.with(scope, |this| {
@ -1343,7 +1263,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
next_early_index, next_early_index,
opaque_type_parent: false, opaque_type_parent: false,
scope_type, scope_type,
allow_late_bound: true,
where_bound_origin: None, where_bound_origin: None,
}; };
self.with(scope, |this| { self.with(scope, |this| {
@ -1597,7 +1516,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
s: self.scope, s: self.scope,
opaque_type_parent: true, opaque_type_parent: true,
scope_type: BinderScopeType::Normal, scope_type: BinderScopeType::Normal,
allow_late_bound: true,
where_bound_origin: None, where_bound_origin: None,
}; };
self.with(scope, walk); self.with(scope, walk);
@ -1773,30 +1691,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
); );
if generic_args.parenthesized { if generic_args.parenthesized {
self.visit_fn_like_elision(generic_args.inputs(), Some(generic_args.bindings[0].ty())); self.visit_fn_like_elision(
generic_args.inputs(),
Some(generic_args.bindings[0].ty()),
false,
);
return; return;
} }
let mut elide_lifetimes = true; for arg in generic_args.args {
let lifetimes: Vec<_> = generic_args if let hir::GenericArg::Lifetime(lt) = arg {
.args self.visit_lifetime(lt);
.iter()
.filter_map(|arg| match arg {
hir::GenericArg::Lifetime(lt) => {
if !lt.is_elided() {
elide_lifetimes = false;
} }
Some(lt)
}
_ => None,
})
.collect();
// We short-circuit here if all are elided in order to pluralize
// possible errors
if elide_lifetimes {
self.resolve_elided_lifetimes(&lifetimes);
} else {
lifetimes.iter().for_each(|lt| self.visit_lifetime(lt));
} }
// Figure out if this is a type/trait segment, // Figure out if this is a type/trait segment,
@ -2052,380 +1958,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
&mut self, &mut self,
inputs: &'tcx [hir::Ty<'tcx>], inputs: &'tcx [hir::Ty<'tcx>],
output: Option<&'tcx hir::Ty<'tcx>>, output: Option<&'tcx hir::Ty<'tcx>>,
in_closure: bool,
) { ) {
debug!("visit_fn_like_elision: enter"); self.with(Scope::Elision { s: self.scope }, |this| {
let mut scope = &*self.scope;
let hir_id = loop {
match scope {
Scope::Binder { hir_id, allow_late_bound: true, .. } => {
break *hir_id;
}
Scope::ObjectLifetimeDefault { ref s, .. }
| Scope::Elision { ref s, .. }
| Scope::Supertrait { ref s, .. }
| Scope::TraitRefBoundary { ref s, .. } => {
scope = *s;
}
Scope::Root
| Scope::Body { .. }
| Scope::Binder { allow_late_bound: false, .. } => {
// See issues #83907 and #83693. Just bail out from looking inside.
// See the issue #95023 for not allowing late bound
self.tcx.sess.delay_span_bug(
rustc_span::DUMMY_SP,
"In fn_like_elision without appropriate scope above",
);
return;
}
}
};
// While not strictly necessary, we gather anon lifetimes *before* actually
// visiting the argument types.
let mut gather = GatherAnonLifetimes { anon_count: 0 };
for input in inputs {
gather.visit_ty(input);
}
trace!(?gather.anon_count);
let late_bound_vars = self.map.late_bound_vars.entry(hir_id).or_default();
let named_late_bound_vars = late_bound_vars.len() as u32;
late_bound_vars.extend(
(0..gather.anon_count).map(|var| ty::BoundVariableKind::Region(ty::BrAnon(var))),
);
let arg_scope = Scope::Elision {
elide: Elide::FreshLateAnon(named_late_bound_vars, Cell::new(0)),
s: self.scope,
};
self.with(arg_scope, |this| {
for input in inputs { for input in inputs {
this.visit_ty(input); this.visit_ty(input);
} }
if !in_closure && let Some(output) = output {
this.visit_ty(output);
}
}); });
if in_closure && let Some(output) = output {
let Some(output) = output else { return };
debug!("determine output");
// Figure out if there's a body we can get argument names from,
// and whether there's a `self` argument (treated specially).
let mut assoc_item_kind = None;
let mut impl_self = None;
let parent = self.tcx.hir().get_parent_node(output.hir_id);
match self.tcx.hir().get(parent) {
// `fn` definitions and methods.
Node::Item(&hir::Item { kind: hir::ItemKind::Fn(..), .. }) => {}
Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => {
if let hir::ItemKind::Trait(.., ref trait_items) =
self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind
{
assoc_item_kind =
trait_items.iter().find(|ti| ti.id.hir_id() == parent).map(|ti| ti.kind);
}
}
Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, _), .. }) => {
if let hir::ItemKind::Impl(hir::Impl { ref self_ty, ref items, .. }) =
self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind
{
impl_self = Some(self_ty);
assoc_item_kind =
items.iter().find(|ii| ii.id.hir_id() == parent).map(|ii| ii.kind);
}
}
// Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds).
Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => {},
Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => {},
// Everything else (only closures?) doesn't
// actually enjoy elision in return types.
_ => {
self.visit_ty(output); self.visit_ty(output);
return;
}
};
let has_self = match assoc_item_kind {
Some(hir::AssocItemKind::Fn { has_self }) => has_self,
_ => false,
};
// In accordance with the rules for lifetime elision, we can determine
// what region to use for elision in the output type in two ways.
// First (determined here), if `self` is by-reference, then the
// implied output region is the region of the self parameter.
if has_self {
struct SelfVisitor<'a> {
map: &'a NamedRegionMap,
impl_self: Option<&'a hir::TyKind<'a>>,
lifetime: Set1<Region>,
}
impl SelfVisitor<'_> {
// Look for `self: &'a Self` - also desugared from `&'a self`,
// and if that matches, use it for elision and return early.
fn is_self_ty(&self, res: Res) -> bool {
if let Res::SelfTy { .. } = res {
return true;
}
// Can't always rely on literal (or implied) `Self` due
// to the way elision rules were originally specified.
if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) =
self.impl_self
{
match path.res {
// Permit the types that unambiguously always
// result in the same type constructor being used
// (it can't differ between `Self` and `self`).
Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _)
| Res::PrimTy(_) => return res == path.res,
_ => {}
}
}
false
}
}
impl<'a> Visitor<'a> for SelfVisitor<'a> {
fn visit_ty(&mut self, ty: &'a hir::Ty<'a>) {
if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.kind {
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.kind
{
if self.is_self_ty(path.res) {
if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
self.lifetime.insert(*lifetime);
}
}
}
}
intravisit::walk_ty(self, ty)
}
}
let mut visitor = SelfVisitor {
map: self.map,
impl_self: impl_self.map(|ty| &ty.kind),
lifetime: Set1::Empty,
};
visitor.visit_ty(&inputs[0]);
if let Set1::One(lifetime) = visitor.lifetime {
let scope = Scope::Elision { elide: Elide::Exact(lifetime), s: self.scope };
self.with(scope, |this| this.visit_ty(output));
return;
}
}
// Second, if there was exactly one lifetime (either a substitution or a
// reference) in the arguments, then any anonymous regions in the output
// have that lifetime.
let mut possible_implied_output_region = None;
let mut lifetime_count = 0;
for input in inputs.iter().skip(has_self as usize) {
let mut gather = GatherLifetimes {
map: self.map,
outer_index: ty::INNERMOST,
have_bound_regions: false,
lifetimes: Default::default(),
};
gather.visit_ty(input);
lifetime_count += gather.lifetimes.len();
if lifetime_count == 1 && gather.lifetimes.len() == 1 {
// there's a chance that the unique lifetime of this
// iteration will be the appropriate lifetime for output
// parameters, so lets store it.
possible_implied_output_region = gather.lifetimes.iter().cloned().next();
}
}
let elide = if lifetime_count == 1 {
Elide::Exact(possible_implied_output_region.unwrap())
} else {
Elide::Error
};
debug!(?elide);
let scope = Scope::Elision { elide, s: self.scope };
self.with(scope, |this| this.visit_ty(output));
struct GatherLifetimes<'a> {
map: &'a NamedRegionMap,
outer_index: ty::DebruijnIndex,
have_bound_regions: bool,
lifetimes: FxHashSet<Region>,
}
impl<'v, 'a> Visitor<'v> for GatherLifetimes<'a> {
fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
if let hir::TyKind::BareFn(_) = ty.kind {
self.outer_index.shift_in(1);
}
match ty.kind {
hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
for bound in bounds {
self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
}
// Stay on the safe side and don't include the object
// lifetime default (which may not end up being used).
if !lifetime.is_elided() {
self.visit_lifetime(lifetime);
}
}
_ => {
intravisit::walk_ty(self, ty);
}
}
if let hir::TyKind::BareFn(_) = ty.kind {
self.outer_index.shift_out(1);
}
}
fn visit_generic_param(&mut self, param: &hir::GenericParam<'_>) {
if let hir::GenericParamKind::Lifetime { .. } = param.kind {
// FIXME(eddyb) Do we want this? It only makes a difference
// if this `for<'a>` lifetime parameter is never used.
self.have_bound_regions = true;
}
intravisit::walk_generic_param(self, param);
}
fn visit_poly_trait_ref(
&mut self,
trait_ref: &hir::PolyTraitRef<'_>,
modifier: hir::TraitBoundModifier,
) {
self.outer_index.shift_in(1);
intravisit::walk_poly_trait_ref(self, trait_ref, modifier);
self.outer_index.shift_out(1);
}
fn visit_param_bound(&mut self, bound: &hir::GenericBound<'_>) {
if let hir::GenericBound::LangItemTrait { .. } = bound {
self.outer_index.shift_in(1);
intravisit::walk_param_bound(self, bound);
self.outer_index.shift_out(1);
} else {
intravisit::walk_param_bound(self, bound);
}
}
fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
match lifetime {
Region::LateBound(debruijn, _, _)
| Region::LateBoundAnon(debruijn, _, _)
if debruijn < self.outer_index =>
{
self.have_bound_regions = true;
}
_ => {
// FIXME(jackh726): nested trait refs?
self.lifetimes.insert(lifetime.shifted_out_to_binder(self.outer_index));
}
}
}
}
}
struct GatherAnonLifetimes {
anon_count: u32,
}
impl<'v> Visitor<'v> for GatherAnonLifetimes {
#[instrument(skip(self), level = "trace")]
fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
// If we enter a `BareFn`, then we enter a *new* binding scope
if let hir::TyKind::BareFn(_) = ty.kind {
return;
}
intravisit::walk_ty(self, ty);
}
fn visit_generic_args(
&mut self,
path_span: Span,
generic_args: &'v hir::GenericArgs<'v>,
) {
// parenthesized args enter a new elision scope
if generic_args.parenthesized {
return;
}
intravisit::walk_generic_args(self, path_span, generic_args)
}
#[instrument(skip(self), level = "trace")]
fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
if lifetime_ref.is_elided() {
self.anon_count += 1;
}
}
}
}
fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
debug!("resolve_elided_lifetimes(lifetime_refs={:?})", lifetime_refs);
if lifetime_refs.is_empty() {
return;
}
let mut late_depth = 0;
let mut scope = self.scope;
loop {
match *scope {
// Do not assign any resolution, it will be inferred.
Scope::Body { .. } => return,
Scope::Root => break,
Scope::Binder { s, scope_type, .. } => {
match scope_type {
BinderScopeType::Normal => late_depth += 1,
BinderScopeType::Concatenating => {}
}
scope = s;
}
Scope::Elision {
elide: Elide::FreshLateAnon(named_late_bound_vars, ref counter),
..
} => {
for lifetime_ref in lifetime_refs {
let lifetime =
Region::late_anon(named_late_bound_vars, counter).shifted(late_depth);
self.insert_lifetime(lifetime_ref, lifetime);
}
return;
}
Scope::Elision { elide: Elide::Exact(l), .. } => {
let lifetime = l.shifted(late_depth);
for lifetime_ref in lifetime_refs {
self.insert_lifetime(lifetime_ref, lifetime);
}
return;
}
Scope::Elision { elide: Elide::Error, .. }
| Scope::Elision { elide: Elide::Forbid, .. } => break,
Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s, .. } => {
scope = s;
}
}
}
for lt in lifetime_refs {
self.tcx.sess.delay_span_bug(lt.span, "Missing lifetime specifier");
} }
} }

View file

@ -221,14 +221,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
tcx.mk_region(ty::ReLateBound(debruijn, br)) tcx.mk_region(ty::ReLateBound(debruijn, br))
} }
Some(rl::Region::LateBoundAnon(debruijn, index, anon_index)) => {
let br = ty::BoundRegion {
var: ty::BoundVar::from_u32(index),
kind: ty::BrAnon(anon_index),
};
tcx.mk_region(ty::ReLateBound(debruijn, br))
}
Some(rl::Region::EarlyBound(index, id)) => { Some(rl::Region::EarlyBound(index, id)) => {
let name = lifetime_name(id.expect_local()); let name = lifetime_name(id.expect_local());
tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name })) tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name }))

View file

@ -1346,16 +1346,8 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
match self.tcx.named_region(lt.hir_id) { match self.tcx.named_region(lt.hir_id) {
Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {} Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {}
Some( Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {}
rl::Region::LateBound(debruijn, _, _) Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => {
| rl::Region::LateBoundAnon(debruijn, _, _),
) if debruijn < self.outer_index => {}
Some(
rl::Region::LateBound(..)
| rl::Region::LateBoundAnon(..)
| rl::Region::Free(..),
)
| None => {
self.has_late_bound_regions = Some(lt.span); self.has_late_bound_regions = Some(lt.span);
} }
} }

View file

@ -0,0 +1,24 @@
// check-pass
// Verify that we do not ICE when anonymous lifetimes appear inside an AnonConst.
pub struct EntriesBuffer(Box<[[u8; HashesEntry::LEN]; 5]>);
impl EntriesBuffer {
pub fn iter_child_buffers(&mut self) -> impl Iterator<Item = &mut [u8; HashesEntry::LEN]> {
self.0.iter_mut()
}
pub fn iter_child_buffers_explicit(
&mut self,
) -> impl Iterator<Item = &mut [u8; HashesEntry::<'_>::LEN]> {
self.0.iter_mut()
}
}
pub struct HashesEntry<'a>(&'a [u8]);
impl HashesEntry<'_> {
pub const LEN: usize = 1;
}
fn main() {}

View file

@ -615,7 +615,7 @@ struct BodyLifetimeChecker {
impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
// for lifetimes as parameters of generics // for lifetimes as parameters of generics
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
if lifetime.name.ident().name != kw::Empty && lifetime.name.ident().name != kw::StaticLifetime { if lifetime.name.ident().name != kw::UnderscoreLifetime && lifetime.name.ident().name != kw::StaticLifetime {
self.lifetimes_used_in_body = true; self.lifetimes_used_in_body = true;
} }
} }

View file

@ -166,7 +166,7 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName])
// - There's only one output lifetime bound using `+ '_` // - There's only one output lifetime bound using `+ '_`
// - All input lifetimes are explicitly bound to the output // - All input lifetimes are explicitly bound to the output
input_lifetimes.is_empty() input_lifetimes.is_empty()
|| (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore)) || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Infer))
|| input_lifetimes || input_lifetimes
.iter() .iter()
.all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt)) .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt))

View file

@ -351,7 +351,7 @@ impl fmt::Display for RefPrefix {
name.fmt(f)?; name.fmt(f)?;
f.write_char(' ')?; f.write_char(' ')?;
}, },
LifetimeName::Underscore => f.write_str("'_ ")?, LifetimeName::Infer => f.write_str("'_ ")?,
LifetimeName::Static => f.write_str("'static ")?, LifetimeName::Static => f.write_str("'static ")?,
_ => (), _ => (),
} }