Skip to content

Commit b2c18d9

Browse files
Add comments
1 parent 5e6f119 commit b2c18d9

File tree

2 files changed

+35
-7
lines changed

2 files changed

+35
-7
lines changed

compiler/rustc_lint/src/redundant_lifetime_args.rs

+28-6
Original file line numberDiff line numberDiff line change
@@ -82,17 +82,28 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::
8282

8383
let infcx = &tcx.infer_ctxt().build();
8484
let ocx = ObligationCtxt::new(infcx);
85+
86+
// Compute the implied outlives bounds for the item. This ensures that we treat
87+
// a signature with an argument like `&'a &'b ()` as implicitly having `'b: 'a`.
8588
let Ok(assumed_wf_types) = ocx.assumed_wf_types(param_env, owner_id.def_id) else {
8689
return;
8790
};
88-
8991
let implied_bounds = infcx.implied_bounds_tys(param_env, owner_id.def_id, assumed_wf_types);
9092
let outlives_env = &OutlivesEnvironment::with_bounds(param_env, implied_bounds);
9193

94+
// The ordering of this lifetime map is a bit subtle.
95+
//
96+
// Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime,
97+
// where we can prove that `'candidate = 'victim`.
98+
//
99+
// `'static` must come first in this list because we can never replace `'static` with
100+
// something else, but if we find some lifetime `'a` where `'a = 'static`, we want to
101+
// suggest replacing `'a` with `'static`.
92102
let mut lifetimes = vec![tcx.lifetimes.re_static];
93103
lifetimes.extend(
94104
ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()),
95105
);
106+
// If we are in a function, add its late-bound lifetimes too.
96107
if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) {
97108
for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() {
98109
let ty::BoundVariableKind::Region(kind) = var else { continue };
@@ -101,20 +112,23 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::
101112
}
102113

103114
// Keep track of lifetimes which have already been replaced with other lifetimes.
115+
// This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by
116+
// both `'a` and `'b`.
104117
let mut shadowed = FxHashSet::default();
105118

106119
for (idx, &candidate) in lifetimes.iter().enumerate() {
120+
// Don't suggest removing a lifetime twice.
107121
if shadowed.contains(&candidate) {
108-
// Don't suggest removing a lifetime twice.
109122
continue;
110123
}
111124

125+
// Can't rename a named lifetime named `'_` without ambiguity.
112126
if !candidate.has_name() {
113-
// Can't rename a named lifetime with `'_` without ambiguity.
114127
continue;
115128
}
116129

117130
for &victim in &lifetimes[(idx + 1)..] {
131+
// We only care about lifetimes that are "real", i.e. that have a def-id.
118132
let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
119133
| ty::ReLateParam(ty::LateParamRegion {
120134
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
@@ -124,15 +138,21 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::
124138
continue;
125139
};
126140

141+
// Do not rename lifetimes not local to this item since they'll overlap
142+
// with the lint running on the parent. We still want to consider parent
143+
// lifetimes which make child lifetimes redundant, otherwise we would
144+
// have truncated the `identity_for_item` args above.
127145
if tcx.parent(def_id) != owner_id.to_def_id() {
128-
// Do not rename generics not local to this item since
129-
// they'll overlap with this lint running on the parent.
130146
continue;
131147
}
132148

133149
let infcx = infcx.fork();
150+
151+
// Require that `'candidate = 'victim`
134152
infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), candidate, victim);
135153
infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), victim, candidate);
154+
155+
// If there are no lifetime errors, then we have proven that `'candidate = 'victim`!
136156
if infcx.resolve_regions(outlives_env).is_empty() {
137157
shadowed.insert(victim);
138158
tcx.emit_spanned_lint(
@@ -150,6 +170,8 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::
150170
#[diag(lint_redundant_lifetime_args)]
151171
#[note]
152172
struct RedundantLifetimeArgsLint<'tcx> {
153-
candidate: ty::Region<'tcx>,
173+
/// The lifetime we have found to be redundant.
154174
victim: ty::Region<'tcx>,
175+
// The lifetime we can replace the victim with.
176+
candidate: ty::Region<'tcx>,
155177
}

compiler/rustc_lint_defs/src/builtin.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1532,14 +1532,20 @@ declare_lint! {
15321532

15331533
declare_lint! {
15341534
/// The `unused_lifetimes` lint detects lifetime parameters that are never
1535-
/// used.
1535+
/// used, or are redundant because they are equal to another named lifetime.
15361536
///
15371537
/// ### Example
15381538
///
15391539
/// ```rust,compile_fail
15401540
/// #[deny(unused_lifetimes)]
15411541
///
15421542
/// pub fn foo<'a>() {}
1543+
///
1544+
/// // `'a = 'static`, so all usages of `'a` can be replaced with `'static`
1545+
/// pub fn bar<'a: 'static>() {}
1546+
///
1547+
/// // `'a = 'b`, so all usages of `'b` can be replaced with `'a`
1548+
/// pub fn bar<'a: 'b, 'b: 'a>() {}
15431549
/// ```
15441550
///
15451551
/// {{produces}}

0 commit comments

Comments
 (0)