Skip to content

Commit 48c9d93

Browse files
committed
Auto merge of #153692 - TKanX:bugfix/140611-self-lifetime-elision-name-match, r=<try>
fix(resolve): Warn on Self-Type Lifetime Elision for Non-`Self` Types
2 parents 4b97926 + 1d914f5 commit 48c9d93

81 files changed

Lines changed: 508 additions & 161 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/rustc_error_codes/src/error_codes/E0307.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ also be the underlying implementing type, like `Foo` in the following
4141
example:
4242

4343
```
44+
# #![allow(self_lifetime_elision_not_applicable)]
4445
# struct Foo;
4546
# trait Trait {
4647
# fn foo(&self);

compiler/rustc_error_codes/src/error_codes/E0772.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ internal data was omitted, meaning that the compiler inferred the lifetime
5252
compiler to look like this:
5353

5454
```
55+
# #![allow(self_lifetime_elision_not_applicable)]
5556
# trait Person {}
5657
#
5758
# impl dyn Person {

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ declare_lint_pass! {
106106
RUST_2024_INCOMPATIBLE_PAT,
107107
RUST_2024_PRELUDE_COLLISIONS,
108108
SELF_CONSTRUCTOR_FROM_OUTER_ITEM,
109+
SELF_LIFETIME_ELISION_NOT_APPLICABLE,
109110
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
110111
SHADOWING_SUPERTRAIT_ITEMS,
111112
SINGLE_USE_LIFETIMES,
@@ -2914,6 +2915,38 @@ declare_lint! {
29142915
};
29152916
}
29162917

2918+
declare_lint! {
2919+
/// The `self_lifetime_elision_not_applicable` lint detects `self` parameters
2920+
/// whose type does not syntactically contain `Self`, causing lifetime
2921+
/// elision to rely on an unreliable name-matching heuristic.
2922+
///
2923+
/// ### Example
2924+
///
2925+
/// ```rust,compile_fail
2926+
/// struct Foo<'a>(&'a ());
2927+
///
2928+
/// impl<'a> Foo<'a> {
2929+
/// fn get(self: &Foo<'a>) -> &() { self.0 }
2930+
/// }
2931+
/// ```
2932+
///
2933+
/// {{produces}}
2934+
///
2935+
/// ### Explanation
2936+
///
2937+
/// When a `self` parameter uses a concrete type name instead of `Self`,
2938+
/// the compiler uses a name-matching heuristic to determine if self-type
2939+
/// lifetime elision applies. This heuristic cannot see through type
2940+
/// aliases, ignores generic arguments, and may silently choose an
2941+
/// incorrect lifetime. Use `&self` or `self: &Self` instead.
2942+
pub SELF_LIFETIME_ELISION_NOT_APPLICABLE,
2943+
Deny,
2944+
"self-type lifetime elision for non-`Self` type",
2945+
@future_incompatible = FutureIncompatibleInfo {
2946+
reason: fcw!(FutureReleaseError #140611),
2947+
};
2948+
}
2949+
29172950
declare_lint! {
29182951
/// The `semicolon_in_expressions_from_macros` lint detects trailing semicolons
29192952
/// in macro bodies when the macro is invoked in expression position.

compiler/rustc_resolve/src/errors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,3 +1776,11 @@ pub(crate) enum UnusedImportsSugg {
17761776
num_to_remove: usize,
17771777
},
17781778
}
1779+
1780+
#[derive(Diagnostic)]
1781+
#[diag("`self` parameter type does not contain `Self`")]
1782+
#[help("use `&self`, `&mut self`, or `self: &Self` for correct lifetime elision")]
1783+
pub(crate) struct SelfLifetimeElisionNotApplicable {
1784+
#[primary_span]
1785+
pub span: Span,
1786+
}

compiler/rustc_resolve/src/late.rs

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use std::mem::{replace, swap, take};
1313
use std::ops::{ControlFlow, Range};
1414

1515
use rustc_ast::visit::{
16-
AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list,
16+
AssocCtxt, BoundKind, FnCtxt, FnKind, LifetimeCtxt, Visitor, try_visit, visit_opt, walk_list,
17+
walk_ty,
1718
};
1819
use rustc_ast::*;
1920
use rustc_data_structures::either::Either;
@@ -2448,9 +2449,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
24482449
binder: fn_id,
24492450
report_in_path: report_elided_lifetimes_in_path,
24502451
};
2452+
let output_has_lifetime = fn_output_has_lifetime(output_ty);
24512453
self.with_lifetime_rib(rib, |this| {
24522454
// Add each argument to the rib.
2453-
let elision_lifetime = this.resolve_fn_params(has_self, inputs);
2455+
let elision_lifetime = this.resolve_fn_params(has_self, inputs, output_has_lifetime);
24542456
debug!(?elision_lifetime);
24552457

24562458
let outer_failures = take(&mut this.diag_metadata.current_elision_failures);
@@ -2493,6 +2495,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
24932495
&mut self,
24942496
has_self: bool,
24952497
inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone,
2498+
output_has_lifetime: bool,
24962499
) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> {
24972500
enum Elision {
24982501
/// We have not found any candidate.
@@ -2582,6 +2585,19 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
25822585
// Handle `self` specially.
25832586
if index == 0 && has_self {
25842587
let self_lifetime = self.find_lifetime_for_self(ty);
2588+
2589+
if output_has_lifetime
2590+
&& self.self_type_has_reference(ty)
2591+
&& !self.self_param_has_genuine_self(ty)
2592+
{
2593+
self.r.lint_buffer.buffer_lint(
2594+
lint::builtin::SELF_LIFETIME_ELISION_NOT_APPLICABLE,
2595+
ty.id,
2596+
ty.span,
2597+
crate::errors::SelfLifetimeElisionNotApplicable { span: ty.span },
2598+
);
2599+
}
2600+
25852601
elision_lifetime = match self_lifetime {
25862602
// We found `self` elision.
25872603
Set1::One(lifetime) => Elision::Self_(lifetime),
@@ -2610,6 +2626,58 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
26102626
Err((all_candidates, parameter_info))
26112627
}
26122628

2629+
/// Returns `true` if `ty` syntactically contains `Self`.
2630+
fn self_param_has_genuine_self(&self, ty: &'ast Ty) -> bool {
2631+
struct GenuineSelfVisitor<'a, 'ra, 'tcx> {
2632+
r: &'a Resolver<'ra, 'tcx>,
2633+
found: bool,
2634+
}
2635+
impl<'ra> Visitor<'ra> for GenuineSelfVisitor<'_, '_, '_> {
2636+
fn visit_ty(&mut self, ty: &'ra Ty) {
2637+
match ty.kind {
2638+
TyKind::ImplicitSelf => self.found = true,
2639+
TyKind::Path(None, _) => {
2640+
if matches!(
2641+
self.r.partial_res_map[&ty.id].full_res(),
2642+
Some(Res::SelfTyParam { .. } | Res::SelfTyAlias { .. })
2643+
) {
2644+
self.found = true;
2645+
}
2646+
}
2647+
_ => {}
2648+
}
2649+
if !self.found {
2650+
visit::walk_ty(self, ty);
2651+
}
2652+
}
2653+
fn visit_expr(&mut self, _: &'ra Expr) {}
2654+
}
2655+
let mut visitor = GenuineSelfVisitor { r: self.r, found: false };
2656+
visitor.visit_ty(ty);
2657+
visitor.found
2658+
}
2659+
2660+
/// Returns `true` if `ty` contains a reference type (`&` or pinned `&`).
2661+
fn self_type_has_reference(&self, ty: &'ast Ty) -> bool {
2662+
struct RefVisitor {
2663+
found: bool,
2664+
}
2665+
impl<'ra> Visitor<'ra> for RefVisitor {
2666+
fn visit_ty(&mut self, ty: &'ra Ty) {
2667+
if matches!(ty.kind, TyKind::Ref(..) | TyKind::PinnedRef(..)) {
2668+
self.found = true;
2669+
}
2670+
if !self.found {
2671+
visit::walk_ty(self, ty);
2672+
}
2673+
}
2674+
fn visit_expr(&mut self, _: &'ra Expr) {}
2675+
}
2676+
let mut visitor = RefVisitor { found: false };
2677+
visitor.visit_ty(ty);
2678+
visitor.found
2679+
}
2680+
26132681
/// List all the lifetimes that appear in the provided type.
26142682
fn find_lifetime_for_self(&self, ty: &'ast Ty) -> Set1<LifetimeRes> {
26152683
/// Visits a type to find all the &references, and determines the
@@ -5593,6 +5661,30 @@ impl ItemInfoCollector<'_, '_, '_> {
55935661
}
55945662
}
55955663

5664+
fn fn_output_has_lifetime(output_ty: &FnRetTy) -> bool {
5665+
struct Probe(bool);
5666+
impl<'ast> Visitor<'ast> for Probe {
5667+
fn visit_lifetime(&mut self, _: &'ast Lifetime, _: LifetimeCtxt) {
5668+
self.0 = true;
5669+
}
5670+
fn visit_ty(&mut self, ty: &'ast Ty) {
5671+
if self.0 {
5672+
return;
5673+
}
5674+
match &ty.kind {
5675+
TyKind::Ref(..) | TyKind::PinnedRef(..) | TyKind::TraitObject(..) => {
5676+
self.0 = true;
5677+
}
5678+
_ => walk_ty(self, ty),
5679+
}
5680+
}
5681+
}
5682+
let FnRetTy::Ty(ty) = output_ty else { return false };
5683+
let mut p = Probe(false);
5684+
p.visit_ty(ty);
5685+
p.0
5686+
}
5687+
55965688
fn required_generic_args_suggestion(generics: &ast::Generics) -> Option<String> {
55975689
let required = generics
55985690
.params

tests/ui/async-await/inference_var_self_argument.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![allow(self_lifetime_elision_not_applicable)]
12
//! This is a regression test for an ICE.
23
//@ edition: 2021
34

tests/ui/async-await/inference_var_self_argument.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0307]: invalid `self` parameter type: `&dyn Foo`
2-
--> $DIR/inference_var_self_argument.rs:5:24
2+
--> $DIR/inference_var_self_argument.rs:6:24
33
|
44
LL | async fn foo(self: &dyn Foo) {
55
| ^^^^^^^^
@@ -8,14 +8,14 @@ LL | async fn foo(self: &dyn Foo) {
88
= help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
99

1010
error[E0038]: the trait `Foo` is not dyn compatible
11-
--> $DIR/inference_var_self_argument.rs:5:33
11+
--> $DIR/inference_var_self_argument.rs:6:33
1212
|
1313
LL | async fn foo(self: &dyn Foo) {
1414
| ^ `Foo` is not dyn compatible
1515
|
1616
note: for a trait to be dyn compatible it needs to allow building a vtable
1717
for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
18-
--> $DIR/inference_var_self_argument.rs:5:14
18+
--> $DIR/inference_var_self_argument.rs:6:14
1919
|
2020
LL | trait Foo {
2121
| --- this trait is not dyn compatible...

tests/ui/borrowck/borrow-in-match-with-format-string.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@ run-pass
2+
#![allow(self_lifetime_elision_not_applicable)]
23
// Test for https://github.com/rust-lang/rust/issues/21400 extracted from
34
// stackoverflow.com/questions/28031155/is-my-borrow-checker-drunk/28031580
45

tests/ui/closures/2229_closure_analysis/issue-88476.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//@ edition:2021
22

33
#![feature(rustc_attrs)]
4+
#![allow(self_lifetime_elision_not_applicable)]
45

56
// Test that we can't move out of struct that impls `Drop`.
67

tests/ui/closures/2229_closure_analysis/issue-88476.stderr

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0658]: attributes on expressions are experimental
2-
--> $DIR/issue-88476.rs:20:13
2+
--> $DIR/issue-88476.rs:21:13
33
|
44
LL | let x = #[rustc_capture_analysis] move || {
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | let x = #[rustc_capture_analysis] move || {
99
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
1010

1111
error[E0658]: attributes on expressions are experimental
12-
--> $DIR/issue-88476.rs:48:13
12+
--> $DIR/issue-88476.rs:49:13
1313
|
1414
LL | let c = #[rustc_capture_analysis] move || {
1515
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -19,7 +19,7 @@ LL | let c = #[rustc_capture_analysis] move || {
1919
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
2020

2121
error: First Pass analysis includes:
22-
--> $DIR/issue-88476.rs:20:39
22+
--> $DIR/issue-88476.rs:21:39
2323
|
2424
LL | let x = #[rustc_capture_analysis] move || {
2525
| _______________________________________^
@@ -28,13 +28,13 @@ LL | | };
2828
| |_____^
2929
|
3030
note: Capturing f[(0, 0)] -> Immutable
31-
--> $DIR/issue-88476.rs:26:26
31+
--> $DIR/issue-88476.rs:27:26
3232
|
3333
LL | println!("{:?}", f.0);
3434
| ^^^
3535

3636
error: Min Capture analysis includes:
37-
--> $DIR/issue-88476.rs:20:39
37+
--> $DIR/issue-88476.rs:21:39
3838
|
3939
LL | let x = #[rustc_capture_analysis] move || {
4040
| _______________________________________^
@@ -43,13 +43,13 @@ LL | | };
4343
| |_____^
4444
|
4545
note: Min Capture f[] -> ByValue
46-
--> $DIR/issue-88476.rs:26:26
46+
--> $DIR/issue-88476.rs:27:26
4747
|
4848
LL | println!("{:?}", f.0);
4949
| ^^^
5050

5151
error: First Pass analysis includes:
52-
--> $DIR/issue-88476.rs:48:39
52+
--> $DIR/issue-88476.rs:49:39
5353
|
5454
LL | let c = #[rustc_capture_analysis] move || {
5555
| _______________________________________^
@@ -58,13 +58,13 @@ LL | | };
5858
| |_____^
5959
|
6060
note: Capturing character[(0, 0)] -> Immutable
61-
--> $DIR/issue-88476.rs:54:24
61+
--> $DIR/issue-88476.rs:55:24
6262
|
6363
LL | println!("{}", character.hp)
6464
| ^^^^^^^^^^^^
6565

6666
error: Min Capture analysis includes:
67-
--> $DIR/issue-88476.rs:48:39
67+
--> $DIR/issue-88476.rs:49:39
6868
|
6969
LL | let c = #[rustc_capture_analysis] move || {
7070
| _______________________________________^
@@ -73,7 +73,7 @@ LL | | };
7373
| |_____^
7474
|
7575
note: Min Capture character[(0, 0)] -> ByValue
76-
--> $DIR/issue-88476.rs:54:24
76+
--> $DIR/issue-88476.rs:55:24
7777
|
7878
LL | println!("{}", character.hp)
7979
| ^^^^^^^^^^^^

0 commit comments

Comments
 (0)