use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot};
use rustc_ast::walk_list;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::{struct_span_err, Applicability, Diagnostic};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
map: &'a mut NamedRegionMap,
scope: ScopeRef<'a>,
- /// Used to disallow the use of in-band lifetimes in `fn` or `Fn` syntax.
- is_in_fn_syntax: bool,
-
- is_in_const_generic: bool,
-
/// Indicates that we only care about the definition of a trait. This should
/// be false if the `Item` we are resolving lifetimes for is not a trait or
/// we eventually need lifetimes resolve for trait items.
/// 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.
+ /// This is used to diagnose inaccessible lifetimes in APIT:
+ /// ```ignore (illustrative)
+ /// fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+ /// ```
+ where_bound_origin: Option<hir::PredicateOrigin>,
},
/// Lifetimes introduced by a fn are scoped to the call-site for that fn,
opaque_type_parent,
scope_type,
hir_id,
- s: _,
allow_late_bound,
+ where_bound_origin,
+ s: _,
} => f
.debug_struct("Binder")
.field("lifetimes", lifetimes)
.field("opaque_type_parent", opaque_type_parent)
.field("scope_type", scope_type)
.field("hir_id", hir_id)
- .field("s", &"..")
.field("allow_late_bound", allow_late_bound)
+ .field("where_bound_origin", where_bound_origin)
+ .field("s", &"..")
.finish(),
Scope::Body { id, s: _ } => {
f.debug_struct("Body").field("id", id).field("s", &"..").finish()
/// The reason for this separate call is to resolve what would otherwise
/// be a cycle. Consider this example:
///
-/// ```rust
+/// ```ignore UNSOLVED (maybe @jackh726 knows what lifetime parameter to give Sub)
/// trait Base<'a> {
/// type BaseItem;
/// }
tcx: TyCtxt<'_>,
local_def_id: LocalDefId,
) -> ResolveLifetimes {
- convert_named_region_map(do_resolve(tcx, local_def_id, true, false))
+ convert_named_region_map(tcx, do_resolve(tcx, local_def_id, true, false))
}
/// Computes the `ResolveLifetimes` map that contains data for an entire `Item`.
/// `named_region_map`, `is_late_bound_map`, etc.
#[tracing::instrument(level = "debug", skip(tcx))]
fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> ResolveLifetimes {
- convert_named_region_map(do_resolve(tcx, local_def_id, false, false))
+ convert_named_region_map(tcx, do_resolve(tcx, local_def_id, false, false))
}
fn do_resolve(
tcx,
map: &mut named_region_map,
scope: ROOT_SCOPE,
- is_in_fn_syntax: false,
- is_in_const_generic: false,
trait_definition_only,
labels_in_fn: vec![],
xcrate_object_lifetime_defaults: Default::default(),
named_region_map
}
-fn convert_named_region_map(named_region_map: NamedRegionMap) -> ResolveLifetimes {
+fn convert_named_region_map(tcx: TyCtxt<'_>, named_region_map: NamedRegionMap) -> ResolveLifetimes {
let mut rl = ResolveLifetimes::default();
for (hir_id, v) in named_region_map.defs {
}
for hir_id in named_region_map.late_bound {
let map = rl.late_bound.entry(hir_id.owner).or_default();
- map.insert(hir_id.local_id);
+ let def_id = tcx.hir().local_def_id(hir_id);
+ map.insert(def_id);
+ }
+ for (_, late_bound) in &mut rl.late_bound {
+ late_bound.sort_by(|&a, &b| {
+ tcx.def_path_hash(a.to_def_id()).cmp(&tcx.def_path_hash(b.to_def_id()))
+ });
}
for (hir_id, v) in named_region_map.late_bound_vars {
let map = rl.late_bound_vars.entry(hir_id.owner).or_default();
fn is_late_bound_map<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
-) -> Option<(LocalDefId, &'tcx FxHashSet<ItemLocalId>)> {
+) -> Option<(LocalDefId, &'tcx FxIndexSet<LocalDefId>)> {
match tcx.def_kind(def_id) {
DefKind::AnonConst | DefKind::InlineConst => {
- let mut def_id = tcx
- .parent(def_id.to_def_id())
- .unwrap_or_else(|| bug!("anon const or closure without a parent"));
+ let mut def_id = tcx.local_parent(def_id);
// We search for the next outer anon const or fn here
// while skipping closures.
//
// Note that for `AnonConst` we still just recurse until we
// find a function body, but who cares :shrug:
- while tcx.is_closure(def_id) {
- def_id = tcx
- .parent(def_id)
- .unwrap_or_else(|| bug!("anon const or closure without a parent"));
+ while tcx.is_closure(def_id.to_def_id()) {
+ def_id = tcx.local_parent(def_id);
}
- tcx.is_late_bound_map(def_id.expect_local())
+ tcx.is_late_bound_map(def_id)
}
_ => resolve_lifetimes_for(tcx, def_id).late_bound.get(&def_id).map(|lt| (def_id, lt)),
}
match scope {
Scope::Binder { lifetimes, s, .. } => {
available_lifetimes.extend(lifetimes.keys().filter_map(|p| match p {
- hir::ParamName::Plain(ident) => Some(ident.name.to_string()),
+ hir::ParamName::Plain(ident) => Some(ident.name),
_ => None,
}));
scope = s;
hir_id: hir::HirId,
) {
let name = match fk {
- intravisit::FnKind::ItemFn(id, _, _, _) => id.name,
- intravisit::FnKind::Method(id, _, _) => id.name,
+ intravisit::FnKind::ItemFn(id, _, _) => id.name,
+ intravisit::FnKind::Method(id, _) => id.name,
intravisit::FnKind::Closure => sym::closure,
};
let name = name.as_str();
opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: true,
+ where_bound_origin: None,
};
self.with(scope, move |_old_scope, this| {
intravisit::walk_fn(this, fk, fd, b, s, hir_id)
});
}
for (&owner, late_bound) in resolved_lifetimes.late_bound.iter() {
- late_bound.iter().for_each(|&local_id| {
- self.map.late_bound.insert(hir::HirId { owner, local_id });
+ late_bound.iter().for_each(|&id| {
+ let hir_id = self.tcx.local_def_id_to_hir_id(id);
+ debug_assert_eq!(owner, hir_id.owner);
+ self.map.late_bound.insert(hir_id);
});
}
for (&owner, late_bound_vars) in
scope_type: BinderScopeType::Normal,
s: ROOT_SCOPE,
allow_late_bound: false,
+ where_bound_origin: None,
};
self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
match ty.kind {
hir::TyKind::BareFn(ref c) => {
let next_early_index = self.next_early_index();
- let was_in_fn_syntax = self.is_in_fn_syntax;
- self.is_in_fn_syntax = true;
let lifetime_span: Option<Span> =
c.generic_params.iter().rev().find_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => Some(param.span),
opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: true,
+ where_bound_origin: None,
};
self.with(scope, |old_scope, this| {
// a bare fn has no bounds, so everything
intravisit::walk_ty(this, ty);
});
self.missing_named_lifetime_spots.pop();
- self.is_in_fn_syntax = was_in_fn_syntax;
}
hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
debug!(?bounds, ?lifetime, "TraitObject");
}
});
match lifetime.name {
- LifetimeName::Implicit(_) => {
+ LifetimeName::Implicit => {
// For types like `dyn Foo`, we should
// generate a special form of elided.
span_bug!(ty.span, "object-lifetime-default expected, not implicit",);
opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: false,
+ where_bound_origin: None,
};
this.with(scope, |_old_scope, this| {
this.visit_generics(generics);
opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: false,
+ where_bound_origin: None,
};
self.with(scope, |_old_scope, this| {
let scope = Scope::TraitRefBoundary { s: this.scope };
opaque_type_parent: true,
scope_type: BinderScopeType::Normal,
allow_late_bound: false,
+ where_bound_origin: None,
};
self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
opaque_type_parent: true,
scope_type: BinderScopeType::Normal,
allow_late_bound: true,
+ where_bound_origin: None,
};
self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
self.insert_lifetime(lifetime_ref, Region::Static);
return;
}
- if self.is_in_const_generic && lifetime_ref.name != LifetimeName::Error {
- self.emit_non_static_lt_in_const_generic_error(lifetime_ref);
- return;
- }
self.resolve_lifetime_ref(lifetime_ref);
}
match param.kind {
GenericParamKind::Lifetime { .. } => {}
GenericParamKind::Type { ref default, .. } => {
- walk_list!(this, visit_param_bound, param.bounds);
if let Some(ref ty) = default {
this.visit_ty(&ty);
}
}
GenericParamKind::Const { ref ty, default } => {
- let was_in_const_generic = this.is_in_const_generic;
- this.is_in_const_generic = true;
- walk_list!(this, visit_param_bound, param.bounds);
this.visit_ty(&ty);
if let Some(default) = default {
this.visit_body(this.tcx.hir().body(default.body));
}
- this.is_in_const_generic = was_in_const_generic;
}
}
}
- for predicate in generics.where_clause.predicates {
+ for predicate in generics.predicates {
match predicate {
&hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
ref bounded_ty,
bounds,
ref bound_generic_params,
+ origin,
..
}) => {
let (lifetimes, binders): (FxIndexMap<hir::ParamName, Region>, Vec<_>) =
opaque_type_parent: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: true,
+ where_bound_origin: Some(origin),
};
this.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &bound_generic_params);
}) => {
this.visit_lifetime(lifetime);
walk_list!(this, visit_param_bound, bounds);
+
+ if lifetime.name != hir::LifetimeName::Static {
+ for bound in bounds {
+ let hir::GenericBound::Outlives(ref lt) = bound else {
+ continue;
+ };
+ if lt.name != hir::LifetimeName::Static {
+ continue;
+ }
+ this.insert_lifetime(lt, Region::Static);
+ this.tcx
+ .sess
+ .struct_span_warn(
+ lifetime.span,
+ &format!(
+ "unnecessary lifetime parameter `{}`",
+ lifetime.name.ident(),
+ ),
+ )
+ .help(&format!(
+ "you can use the `'static` lifetime directly, in place of `{}`",
+ lifetime.name.ident(),
+ ))
+ .emit();
+ }
+ }
}
&hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
ref lhs_ty,
opaque_type_parent: false,
scope_type,
allow_late_bound: true,
+ where_bound_origin: None,
};
self.with(scope, |_, this| {
intravisit::walk_param_bound(this, bound);
opaque_type_parent: false,
scope_type,
allow_late_bound: true,
+ where_bound_origin: None,
};
self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
GenericParamKind::Type { .. } => {
let mut set = Set1::Empty;
- add_bounds(&mut set, ¶m.bounds);
-
let param_def_id = tcx.hir().local_def_id(param.hir_id);
- for predicate in generics.where_clause.predicates {
+ for predicate in generics.predicates {
// Look for `type: ...` where clauses.
let hir::WherePredicate::BoundPredicate(ref data) = *predicate else { continue };
tcx: *tcx,
map,
scope: &wrap_scope,
- is_in_fn_syntax: self.is_in_fn_syntax,
- is_in_const_generic: self.is_in_const_generic,
trait_definition_only: self.trait_definition_only,
labels_in_fn,
xcrate_object_lifetime_defaults,
let remove_decl = self
.tcx
.parent(def_id)
- .and_then(|parent_def_id| parent_def_id.as_local())
+ .as_local()
.and_then(|parent_def_id| self.tcx.hir().get_generics(parent_def_id))
.and_then(|generics| self.lifetime_deletion_span(name, generics));
continue;
}
- if let Some(parent_def_id) = self.tcx.parent(def_id) {
- if let Some(def_id) = parent_def_id.as_local() {
- // lifetimes in `derive` expansions don't count (Issue #53738)
- if self
- .tcx
- .get_attrs(def_id.to_def_id())
- .iter()
- .any(|attr| attr.has_name(sym::automatically_derived))
- {
- continue;
- }
+ let parent_def_id = self.tcx.parent(def_id);
+ if let Some(def_id) = parent_def_id.as_local() {
+ // lifetimes in `derive` expansions don't count (Issue #53738)
+ if self.tcx.has_attr(def_id.to_def_id(), sym::automatically_derived) {
+ continue;
+ }
- // opaque types generated when desugaring an async function can have a single
- // use lifetime even if it is explicitly denied (Issue #77175)
- if let hir::Node::Item(hir::Item {
- kind: hir::ItemKind::OpaqueTy(ref opaque),
- ..
- }) = self.tcx.hir().get_by_def_id(def_id)
- {
- if !matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(..)) {
+ // opaque types generated when desugaring an async function can have a single
+ // use lifetime even if it is explicitly denied (Issue #77175)
+ if let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::OpaqueTy(ref opaque),
+ ..
+ }) = self.tcx.hir().get_by_def_id(def_id)
+ {
+ if !matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(..)) {
+ continue 'lifetimes;
+ }
+ // We want to do this only if the lifetime identifier is already defined
+ // in the async function that generated this. Otherwise it could be
+ // an opaque type defined by the developer and we still want this
+ // lint to fail compilation
+ for p in opaque.generics.params {
+ if defined_by.contains_key(&p.name) {
continue 'lifetimes;
}
- // We want to do this only if the lifetime identifier is already defined
- // in the async function that generated this. Otherwise it could be
- // an opaque type defined by the developer and we still want this
- // lint to fail compilation
- for p in opaque.generics.params {
- if defined_by.contains_key(&p.name) {
- continue 'lifetimes;
- }
- }
}
}
}
|lint| {
let mut err = lint
.build(&format!("lifetime parameter `{}` never used", name));
- if let Some(parent_def_id) = self.tcx.parent(def_id) {
- if let Some(generics) =
- self.tcx.hir().get_generics(parent_def_id.expect_local())
- {
- let unused_lt_span =
- self.lifetime_deletion_span(name, generics);
- if let Some(span) = unused_lt_span {
- err.span_suggestion(
- span,
- "elide the unused lifetime",
- String::new(),
- Applicability::MachineApplicable,
- );
- }
+ let parent_def_id = self.tcx.parent(def_id);
+ if let Some(generics) =
+ self.tcx.hir().get_generics(parent_def_id.expect_local())
+ {
+ let unused_lt_span =
+ self.lifetime_deletion_span(name, generics);
+ if let Some(span) = unused_lt_span {
+ err.span_suggestion(
+ span,
+ "elide the unused lifetime",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
}
}
err.emit();
track_lifetime_uses: false,
scope_type: BinderScopeType::Normal,
allow_late_bound: true,
+ where_bound_origin: None,
};
self.with(scope, move |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
let result = loop {
match *scope {
Scope::Body { id, s } => {
- // Non-static lifetimes are prohibited in anonymous constants without
- // `generic_const_exprs`.
- self.maybe_emit_forbidden_non_static_lifetime_error(id, lifetime_ref);
-
outermost_body = Some(id);
scope = s;
}
}
self.insert_lifetime(lifetime_ref, def);
- } else {
- self.emit_undeclared_lifetime_error(lifetime_ref);
+ return;
+ }
+
+ // We may fail to resolve higher-ranked lifetimes that are mentionned by APIT.
+ // AST-based resolution does not care for impl-trait desugaring, which are the
+ // responibility of lowering. This may create a mismatch between the resolution
+ // AST found (`region_def_id`) which points to HRTB, and what HIR allows.
+ // ```
+ // fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+ // ```
+ //
+ // In such case, walk back the binders to diagnose it properly.
+ let mut scope = self.scope;
+ loop {
+ match *scope {
+ Scope::Binder {
+ where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), ..
+ } => {
+ self.tcx
+ .sess
+ .struct_span_err(
+ lifetime_ref.span,
+ "`impl Trait` can only mention lifetimes bound at the fn or impl level",
+ )
+ .emit();
+ return;
+ }
+ Scope::Root => break,
+ Scope::Binder { s, .. }
+ | Scope::Body { s, .. }
+ | Scope::Elision { s, .. }
+ | Scope::ObjectLifetimeDefault { s, .. }
+ | Scope::Supertrait { s, .. }
+ | Scope::TraitRefBoundary { s, .. } => {
+ scope = s;
+ }
+ }
}
+
+ self.tcx.sess.delay_span_bug(
+ lifetime_ref.span,
+ &format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
+ );
}
fn visit_segment_args(
);
if generic_args.parenthesized {
- let was_in_fn_syntax = self.is_in_fn_syntax;
- self.is_in_fn_syntax = true;
self.visit_fn_like_elision(generic_args.inputs(), Some(generic_args.bindings[0].ty()));
- self.is_in_fn_syntax = was_in_fn_syntax;
return;
}
/// Returns all the late-bound vars that come into scope from supertrait HRTBs, based on the
/// associated type name and starting trait.
/// For example, imagine we have
- /// ```rust
+ /// ```ignore (illustrative)
/// trait Foo<'a, 'b> {
/// type As;
/// }
let error = loop {
match *scope {
// Do not assign any resolution, it will be inferred.
- Scope::Body { .. } => break Ok(()),
+ Scope::Body { .. } => return,
- Scope::Root => break Err(None),
+ Scope::Root => break None,
Scope::Binder { s, ref lifetimes, scope_type, .. } => {
// collect named lifetimes for suggestions
self.insert_lifetime(lifetime_ref, lifetime);
}
- break Ok(());
+ return;
}
Scope::Elision { elide: Elide::Exact(l), .. } => {
for lifetime_ref in lifetime_refs {
self.insert_lifetime(lifetime_ref, lifetime);
}
- break Ok(());
+ return;
}
Scope::Elision { elide: Elide::Error(ref e), ref s, .. } => {
_ => break,
}
}
- break Err(Some(&e[..]));
+ break Some(&e[..]);
}
- Scope::Elision { elide: Elide::Forbid, .. } => break Err(None),
+ Scope::Elision { elide: Elide::Forbid, .. } => break None,
Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
}
};
- let error = match error {
- Ok(()) => {
- self.report_elided_lifetime_in_ty(lifetime_refs);
- return;
- }
- Err(error) => error,
- };
-
// If we specifically need the `scope_for_path` map, then we're in the
// diagnostic pass and we don't want to emit more errors.
if self.map.scope_for_path.is_some() {
if let hir::ParamName::Plain(_) = lifetime_i_name {
let name = lifetime_i_name.ident().name;
if name == kw::UnderscoreLifetime || name == kw::StaticLifetime {
- let mut err = struct_span_err!(
- self.tcx.sess,
- lifetime_i.span,
- E0262,
- "invalid lifetime parameter name: `{}`",
- lifetime_i.name.ident(),
- );
- err.span_label(
+ self.tcx.sess.delay_span_bug(
lifetime_i.span,
- format!("{} is a reserved lifetime name", name),
+ &format!("invalid lifetime parameter name: `{}`", lifetime_i.name.ident()),
);
- err.emit();
}
}
// It is a soft error to shadow a lifetime within a parent scope.
self.check_lifetime_param_for_shadowing(old_scope, &lifetime_i);
-
- for bound in lifetime_i.bounds {
- match bound {
- hir::GenericBound::Outlives(ref lt) => match lt.name {
- hir::LifetimeName::Underscore => {
- self.tcx.sess.delay_span_bug(
- lt.span,
- "use of `'_` in illegal place, but not caught by lowering",
- );
- }
- hir::LifetimeName::Static => {
- self.insert_lifetime(lt, Region::Static);
- self.tcx
- .sess
- .struct_span_warn(
- lifetime_i.span.to(lt.span),
- &format!(
- "unnecessary lifetime parameter `{}`",
- lifetime_i.name.ident(),
- ),
- )
- .help(&format!(
- "you can use the `'static` lifetime directly, in place of `{}`",
- lifetime_i.name.ident(),
- ))
- .emit();
- }
- hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit(_) => {
- self.resolve_lifetime_ref(lt);
- }
- hir::LifetimeName::ImplicitObjectLifetimeDefault => {
- self.tcx.sess.delay_span_bug(
- lt.span,
- "lowering generated `ImplicitObjectLifetimeDefault` \
- outside of an object type",
- );
- }
- hir::LifetimeName::Error => {
- // No need to do anything, error already reported.
- }
- },
- _ => bug!(),
- }
- }
}
}
// ignore binders here and scrape up all names we see.
let mut appears_in_where_clause = AllCollector::default();
appears_in_where_clause.visit_generics(generics);
-
- for param in generics.params {
- if let hir::GenericParamKind::Lifetime { .. } = param.kind {
- if !param.bounds.is_empty() {
- // `'a: 'b` means both `'a` and `'b` are referenced
- appears_in_where_clause
- .regions
- .insert(hir::LifetimeName::Param(param.name.normalize_to_macros_2_0()));
- }
- }
- }
-
debug!(?appears_in_where_clause.regions);
// Late bound regions are those that: