Skip to content

Commit 76938d6

Browse files
committed
Auto merge of rust-lang#90446 - cjgillot:late-elided, r=jackh726
Lint elided lifetimes in path during lifetime resolution. The lifetime elision lint is known to be brittle and can be redundant with later lifetime resolution errors. This PR aims to remove the redundancy by performing the lint after lifetime resolution. This PR proposes to carry the information that an elision should be linted against by using a special `LifetimeName`. I am not certain this is the best solution, but it is certainly the easiest. Fixes rust-lang#60199 Fixes rust-lang#55768 Fixes rust-lang#63110 Fixes rust-lang#71957
2 parents 48a5999 + aa2450f commit 76938d6

File tree

13 files changed

+399
-239
lines changed

13 files changed

+399
-239
lines changed

compiler/rustc_ast_lowering/src/lib.rs

+10-9
Original file line numberDiff line numberDiff line change
@@ -1786,7 +1786,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
17861786
GenericArg::Lifetime(hir::Lifetime {
17871787
hir_id: self.next_id(),
17881788
span: self.lower_span(span),
1789-
name: hir::LifetimeName::Implicit,
1789+
name: hir::LifetimeName::Implicit(false),
17901790
})));
17911791
let generic_args = self.arena.alloc_from_iter(generic_args);
17921792

@@ -1927,7 +1927,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
19271927
});
19281928
let param_name = match lt.name {
19291929
hir::LifetimeName::Param(param_name) => param_name,
1930-
hir::LifetimeName::Implicit
1930+
hir::LifetimeName::Implicit(_)
19311931
| hir::LifetimeName::Underscore
19321932
| hir::LifetimeName::Static => hir::ParamName::Plain(lt.name.ident()),
19331933
hir::LifetimeName::ImplicitObjectLifetimeDefault => {
@@ -2290,7 +2290,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
22902290

22912291
AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span),
22922292

2293-
AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
2293+
AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span, false),
22942294
}
22952295
}
22962296

@@ -2322,11 +2322,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
23222322
&'s mut self,
23232323
span: Span,
23242324
count: usize,
2325+
param_mode: ParamMode,
23252326
) -> impl Iterator<Item = hir::Lifetime> + Captures<'a> + Captures<'s> + Captures<'hir> {
2326-
(0..count).map(move |_| self.elided_path_lifetime(span))
2327+
(0..count).map(move |_| self.elided_path_lifetime(span, param_mode))
23272328
}
23282329

2329-
fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime {
2330+
fn elided_path_lifetime(&mut self, span: Span, param_mode: ParamMode) -> hir::Lifetime {
23302331
match self.anonymous_lifetime_mode {
23312332
AnonymousLifetimeMode::CreateParameter => {
23322333
// We should have emitted E0726 when processing this path above
@@ -2342,7 +2343,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
23422343
// lifetime. Instead, we simply create an implicit lifetime, which will be checked
23432344
// later, at which point a suitable error will be emitted.
23442345
AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => {
2345-
self.new_implicit_lifetime(span)
2346+
self.new_implicit_lifetime(span, param_mode == ParamMode::Explicit)
23462347
}
23472348
}
23482349
}
@@ -2385,11 +2386,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
23852386
r
23862387
}
23872388

2388-
fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime {
2389+
fn new_implicit_lifetime(&mut self, span: Span, missing: bool) -> hir::Lifetime {
23892390
hir::Lifetime {
23902391
hir_id: self.next_id(),
23912392
span: self.lower_span(span),
2392-
name: hir::LifetimeName::Implicit,
2393+
name: hir::LifetimeName::Implicit(missing),
23932394
}
23942395
}
23952396

@@ -2536,7 +2537,7 @@ fn lifetimes_from_impl_trait_bounds(
25362537

25372538
fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
25382539
let name = match lifetime.name {
2539-
hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => {
2540+
hir::LifetimeName::Implicit(_) | hir::LifetimeName::Underscore => {
25402541
if self.collect_elided_lifetimes {
25412542
// Use `'_` for both implicit and underscore lifetimes in
25422543
// `type Foo<'_> = impl SomeTrait<'_>;`.

compiler/rustc_ast_lowering/src/path.rs

+33-55
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ use rustc_hir as hir;
77
use rustc_hir::def::{DefKind, PartialRes, Res};
88
use rustc_hir::def_id::DefId;
99
use rustc_hir::GenericArg;
10-
use rustc_session::lint::builtin::ELIDED_LIFETIMES_IN_PATHS;
11-
use rustc_session::lint::BuiltinLintDiagnostics;
1210
use rustc_span::symbol::Ident;
1311
use rustc_span::{BytePos, Span, DUMMY_SP};
1412

@@ -270,12 +268,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
270268

271269
let has_lifetimes =
272270
generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
273-
if !generic_args.parenthesized && !has_lifetimes {
271+
if !generic_args.parenthesized && !has_lifetimes && expected_lifetimes > 0 {
274272
// Note: these spans are used for diagnostics when they can't be inferred.
275273
// See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
276274
let elided_lifetime_span = if generic_args.span.is_empty() {
277275
// If there are no brackets, use the identifier span.
278-
segment.ident.span
276+
path_span
279277
} else if generic_args.is_empty() {
280278
// If there are brackets, but not generic arguments, then use the opening bracket
281279
generic_args.span.with_hi(generic_args.span.lo() + BytePos(1))
@@ -284,67 +282,47 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
284282
generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo()
285283
};
286284
generic_args.args = self
287-
.elided_path_lifetimes(elided_lifetime_span, expected_lifetimes)
285+
.elided_path_lifetimes(elided_lifetime_span, expected_lifetimes, param_mode)
288286
.map(GenericArg::Lifetime)
289287
.chain(generic_args.args.into_iter())
290288
.collect();
291-
if expected_lifetimes > 0 && param_mode == ParamMode::Explicit {
289+
// In create-parameter mode we error here because we don't want to support
290+
// deprecated impl elision in new features like impl elision and `async fn`,
291+
// both of which work using the `CreateParameter` mode:
292+
//
293+
// impl Foo for std::cell::Ref<u32> // note lack of '_
294+
// async fn foo(_: std::cell::Ref<u32>) { ... }
295+
if let (ParamMode::Explicit, AnonymousLifetimeMode::CreateParameter) =
296+
(param_mode, self.anonymous_lifetime_mode)
297+
{
292298
let anon_lt_suggestion = vec!["'_"; expected_lifetimes].join(", ");
293299
let no_non_lt_args = generic_args.args.len() == expected_lifetimes;
294300
let no_bindings = generic_args.bindings.is_empty();
295-
let (incl_angl_brckt, insertion_sp, suggestion) = if no_non_lt_args && no_bindings {
301+
let (incl_angl_brckt, suggestion) = if no_non_lt_args && no_bindings {
296302
// If there are no generic args, our suggestion can include the angle brackets.
297-
(true, path_span.shrink_to_hi(), format!("<{}>", anon_lt_suggestion))
303+
(true, format!("<{}>", anon_lt_suggestion))
298304
} else {
299305
// Otherwise we'll insert a `'_, ` right after the opening bracket.
300-
let span = generic_args
301-
.span
302-
.with_lo(generic_args.span.lo() + BytePos(1))
303-
.shrink_to_lo();
304-
(false, span, format!("{}, ", anon_lt_suggestion))
306+
(false, format!("{}, ", anon_lt_suggestion))
305307
};
306-
match self.anonymous_lifetime_mode {
307-
// In create-parameter mode we error here because we don't want to support
308-
// deprecated impl elision in new features like impl elision and `async fn`,
309-
// both of which work using the `CreateParameter` mode:
310-
//
311-
// impl Foo for std::cell::Ref<u32> // note lack of '_
312-
// async fn foo(_: std::cell::Ref<u32>) { ... }
313-
AnonymousLifetimeMode::CreateParameter => {
314-
let mut err = struct_span_err!(
315-
self.sess,
316-
path_span,
317-
E0726,
318-
"implicit elided lifetime not allowed here"
319-
);
320-
rustc_errors::add_elided_lifetime_in_path_suggestion(
321-
&self.sess.source_map(),
322-
&mut err,
323-
expected_lifetimes,
324-
path_span,
325-
incl_angl_brckt,
326-
insertion_sp,
327-
suggestion,
328-
);
329-
err.note("assuming a `'static` lifetime...");
330-
err.emit();
331-
}
332-
AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => {
333-
self.resolver.lint_buffer().buffer_lint_with_diagnostic(
334-
ELIDED_LIFETIMES_IN_PATHS,
335-
CRATE_NODE_ID,
336-
path_span,
337-
"hidden lifetime parameters in types are deprecated",
338-
BuiltinLintDiagnostics::ElidedLifetimesInPaths(
339-
expected_lifetimes,
340-
path_span,
341-
incl_angl_brckt,
342-
insertion_sp,
343-
suggestion,
344-
),
345-
);
346-
}
347-
}
308+
let insertion_sp = elided_lifetime_span.shrink_to_hi();
309+
let mut err = struct_span_err!(
310+
self.sess,
311+
path_span,
312+
E0726,
313+
"implicit elided lifetime not allowed here"
314+
);
315+
rustc_errors::add_elided_lifetime_in_path_suggestion(
316+
&self.sess.source_map(),
317+
&mut err,
318+
expected_lifetimes,
319+
path_span,
320+
incl_angl_brckt,
321+
insertion_sp,
322+
suggestion,
323+
);
324+
err.note("assuming a `'static` lifetime...");
325+
err.emit();
348326
}
349327
}
350328

compiler/rustc_borrowck/src/diagnostics/region_name.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
584584
Some(RegionNameHighlight::MatchedAdtAndSegment(lifetime_span))
585585
}
586586

587-
hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Implicit => {
587+
hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Implicit(_) => {
588588
// In this case, the user left off the lifetime; so
589589
// they wrote something like:
590590
//

compiler/rustc_hir/src/hir.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ pub enum LifetimeName {
9292
Param(ParamName),
9393

9494
/// User wrote nothing (e.g., the lifetime in `&u32`).
95-
Implicit,
95+
///
96+
/// The bool indicates whether the user should have written something.
97+
Implicit(bool),
9698

9799
/// Implicit lifetime in a context like `dyn Foo`. This is
98100
/// distinguished from implicit lifetimes elsewhere because the
@@ -122,7 +124,7 @@ impl LifetimeName {
122124
pub fn ident(&self) -> Ident {
123125
match *self {
124126
LifetimeName::ImplicitObjectLifetimeDefault
125-
| LifetimeName::Implicit
127+
| LifetimeName::Implicit(_)
126128
| LifetimeName::Error => Ident::empty(),
127129
LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime),
128130
LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime),
@@ -133,7 +135,7 @@ impl LifetimeName {
133135
pub fn is_elided(&self) -> bool {
134136
match self {
135137
LifetimeName::ImplicitObjectLifetimeDefault
136-
| LifetimeName::Implicit
138+
| LifetimeName::Implicit(_)
137139
| LifetimeName::Underscore => true,
138140

139141
// It might seem surprising that `Fresh(_)` counts as
@@ -3298,7 +3300,7 @@ mod size_asserts {
32983300
rustc_data_structures::static_assert_size!(super::Expr<'static>, 64);
32993301
rustc_data_structures::static_assert_size!(super::Pat<'static>, 88);
33003302
rustc_data_structures::static_assert_size!(super::QPath<'static>, 24);
3301-
rustc_data_structures::static_assert_size!(super::Ty<'static>, 72);
3303+
rustc_data_structures::static_assert_size!(super::Ty<'static>, 80);
33023304

33033305
rustc_data_structures::static_assert_size!(super::Item<'static>, 184);
33043306
rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 128);

compiler/rustc_hir/src/intravisit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime
545545
| LifetimeName::Param(ParamName::Error)
546546
| LifetimeName::Static
547547
| LifetimeName::Error
548-
| LifetimeName::Implicit
548+
| LifetimeName::Implicit(_)
549549
| LifetimeName::ImplicitObjectLifetimeDefault
550550
| LifetimeName::Underscore => {}
551551
}

compiler/rustc_resolve/src/late/diagnostics.rs

+114-1
Original file line numberDiff line numberDiff line change
@@ -1871,6 +1871,117 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
18711871
err.emit();
18721872
}
18731873

1874+
/// Returns whether to add `'static` lifetime to the suggested lifetime list.
1875+
crate fn report_elision_failure(
1876+
&mut self,
1877+
db: &mut DiagnosticBuilder<'_>,
1878+
params: &[ElisionFailureInfo],
1879+
) -> bool {
1880+
let mut m = String::new();
1881+
let len = params.len();
1882+
1883+
let elided_params: Vec<_> =
1884+
params.iter().cloned().filter(|info| info.lifetime_count > 0).collect();
1885+
1886+
let elided_len = elided_params.len();
1887+
1888+
for (i, info) in elided_params.into_iter().enumerate() {
1889+
let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } =
1890+
info;
1891+
1892+
db.span_label(span, "");
1893+
let help_name = if let Some(ident) =
1894+
parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident())
1895+
{
1896+
format!("`{}`", ident)
1897+
} else {
1898+
format!("argument {}", index + 1)
1899+
};
1900+
1901+
m.push_str(
1902+
&(if n == 1 {
1903+
help_name
1904+
} else {
1905+
format!(
1906+
"one of {}'s {} {}lifetimes",
1907+
help_name,
1908+
n,
1909+
if have_bound_regions { "free " } else { "" }
1910+
)
1911+
})[..],
1912+
);
1913+
1914+
if elided_len == 2 && i == 0 {
1915+
m.push_str(" or ");
1916+
} else if i + 2 == elided_len {
1917+
m.push_str(", or ");
1918+
} else if i != elided_len - 1 {
1919+
m.push_str(", ");
1920+
}
1921+
}
1922+
1923+
if len == 0 {
1924+
db.help(
1925+
"this function's return type contains a borrowed value, \
1926+
but there is no value for it to be borrowed from",
1927+
);
1928+
true
1929+
} else if elided_len == 0 {
1930+
db.help(
1931+
"this function's return type contains a borrowed value with \
1932+
an elided lifetime, but the lifetime cannot be derived from \
1933+
the arguments",
1934+
);
1935+
true
1936+
} else if elided_len == 1 {
1937+
db.help(&format!(
1938+
"this function's return type contains a borrowed value, \
1939+
but the signature does not say which {} it is borrowed from",
1940+
m
1941+
));
1942+
false
1943+
} else {
1944+
db.help(&format!(
1945+
"this function's return type contains a borrowed value, \
1946+
but the signature does not say whether it is borrowed from {}",
1947+
m
1948+
));
1949+
false
1950+
}
1951+
}
1952+
1953+
crate fn report_elided_lifetime_in_ty(&self, lifetime_refs: &[&hir::Lifetime]) {
1954+
let Some(missing_lifetime) = lifetime_refs.iter().find(|lt| {
1955+
lt.name == hir::LifetimeName::Implicit(true)
1956+
}) else { return };
1957+
1958+
let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
1959+
spans.sort();
1960+
let mut spans_dedup = spans.clone();
1961+
spans_dedup.dedup();
1962+
let spans_with_counts: Vec<_> = spans_dedup
1963+
.into_iter()
1964+
.map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count()))
1965+
.collect();
1966+
1967+
self.tcx.struct_span_lint_hir(
1968+
rustc_session::lint::builtin::ELIDED_LIFETIMES_IN_PATHS,
1969+
missing_lifetime.hir_id,
1970+
spans,
1971+
|lint| {
1972+
let mut db = lint.build("hidden lifetime parameters in types are deprecated");
1973+
self.add_missing_lifetime_specifiers_label(
1974+
&mut db,
1975+
spans_with_counts,
1976+
&FxHashSet::from_iter([kw::UnderscoreLifetime]),
1977+
Vec::new(),
1978+
&[],
1979+
);
1980+
db.emit()
1981+
},
1982+
);
1983+
}
1984+
18741985
// FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const
18751986
// generics. We are disallowing this until we can decide on how we want to handle non-'static
18761987
// lifetimes in const generics. See issue #74052 for discussion.
@@ -2297,7 +2408,9 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
22972408
);
22982409
let is_allowed_lifetime = matches!(
22992410
lifetime_ref.name,
2300-
hir::LifetimeName::Implicit | hir::LifetimeName::Static | hir::LifetimeName::Underscore
2411+
hir::LifetimeName::Implicit(_)
2412+
| hir::LifetimeName::Static
2413+
| hir::LifetimeName::Underscore
23012414
);
23022415

23032416
if !self.tcx.lazy_normalization() && is_anon_const && !is_allowed_lifetime {

0 commit comments

Comments
 (0)