Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/error_codes/E0307.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ also be the underlying implementing type, like `Foo` in the following
example:

```
# #![allow(self_lifetime_elision_not_applicable)]
# struct Foo;
# trait Trait {
# fn foo(&self);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/error_codes/E0772.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ internal data was omitted, meaning that the compiler inferred the lifetime
compiler to look like this:

```
# #![allow(self_lifetime_elision_not_applicable)]
# trait Person {}
#
# impl dyn Person {
Expand Down
33 changes: 33 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ declare_lint_pass! {
RUST_2024_INCOMPATIBLE_PAT,
RUST_2024_PRELUDE_COLLISIONS,
SELF_CONSTRUCTOR_FROM_OUTER_ITEM,
SELF_LIFETIME_ELISION_NOT_APPLICABLE,
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
SHADOWING_SUPERTRAIT_ITEMS,
SINGLE_USE_LIFETIMES,
Expand Down Expand Up @@ -2914,6 +2915,38 @@ declare_lint! {
};
}

declare_lint! {
/// The `self_lifetime_elision_not_applicable` lint detects `self` parameters
/// whose type does not syntactically contain `Self`, causing lifetime
/// elision to rely on an unreliable name-matching heuristic.
///
/// ### Example
///
/// ```rust,compile_fail
/// struct Foo<'a>(&'a ());
///
/// impl<'a> Foo<'a> {
/// fn get(self: &Foo<'a>) -> &() { self.0 }
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// When a `self` parameter uses a concrete type name instead of `Self`,
/// the compiler uses a name-matching heuristic to determine if self-type
/// lifetime elision applies. This heuristic cannot see through type
/// aliases, ignores generic arguments, and may silently choose an
/// incorrect lifetime. Use `&self` or `self: &Self` instead.
pub SELF_LIFETIME_ELISION_NOT_APPLICABLE,
Deny,
"self-type lifetime elision for non-`Self` type",
@future_incompatible = FutureIncompatibleInfo {
reason: fcw!(FutureReleaseError #140611),
};
}

declare_lint! {
/// The `semicolon_in_expressions_from_macros` lint detects trailing semicolons
/// in macro bodies when the macro is invoked in expression position.
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_resolve/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1776,3 +1776,11 @@ pub(crate) enum UnusedImportsSugg {
num_to_remove: usize,
},
}

#[derive(Diagnostic)]
#[diag("`self` parameter type does not contain `Self`")]
#[help("use `&self`, `&mut self`, or `self: &Self` for correct lifetime elision")]
pub(crate) struct SelfLifetimeElisionNotApplicable {
#[primary_span]
pub span: Span,
}
96 changes: 94 additions & 2 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use std::mem::{replace, swap, take};
use std::ops::{ControlFlow, Range};

use rustc_ast::visit::{
AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list,
AssocCtxt, BoundKind, FnCtxt, FnKind, LifetimeCtxt, Visitor, try_visit, visit_opt, walk_list,
walk_ty,
};
use rustc_ast::*;
use rustc_data_structures::either::Either;
Expand Down Expand Up @@ -2448,9 +2449,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
binder: fn_id,
report_in_path: report_elided_lifetimes_in_path,
};
let output_has_lifetime = fn_output_has_lifetime(output_ty);
self.with_lifetime_rib(rib, |this| {
// Add each argument to the rib.
let elision_lifetime = this.resolve_fn_params(has_self, inputs);
let elision_lifetime = this.resolve_fn_params(has_self, inputs, output_has_lifetime);
debug!(?elision_lifetime);

let outer_failures = take(&mut this.diag_metadata.current_elision_failures);
Expand Down Expand Up @@ -2493,6 +2495,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
&mut self,
has_self: bool,
inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone,
output_has_lifetime: bool,
) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> {
enum Elision {
/// We have not found any candidate.
Expand Down Expand Up @@ -2582,6 +2585,19 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
// Handle `self` specially.
if index == 0 && has_self {
let self_lifetime = self.find_lifetime_for_self(ty);

if output_has_lifetime
&& self.self_type_has_reference(ty)
&& !self.self_param_has_genuine_self(ty)
{
self.r.lint_buffer.buffer_lint(
lint::builtin::SELF_LIFETIME_ELISION_NOT_APPLICABLE,
ty.id,
ty.span,
crate::errors::SelfLifetimeElisionNotApplicable { span: ty.span },
);
}

elision_lifetime = match self_lifetime {
// We found `self` elision.
Set1::One(lifetime) => Elision::Self_(lifetime),
Expand Down Expand Up @@ -2610,6 +2626,58 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
Err((all_candidates, parameter_info))
}

/// Returns `true` if `ty` syntactically contains `Self`.
fn self_param_has_genuine_self(&self, ty: &'ast Ty) -> bool {
struct GenuineSelfVisitor<'a, 'ra, 'tcx> {
r: &'a Resolver<'ra, 'tcx>,
found: bool,
}
impl<'ra> Visitor<'ra> for GenuineSelfVisitor<'_, '_, '_> {
fn visit_ty(&mut self, ty: &'ra Ty) {
match ty.kind {
TyKind::ImplicitSelf => self.found = true,
TyKind::Path(None, _) => {
if matches!(
self.r.partial_res_map[&ty.id].full_res(),
Some(Res::SelfTyParam { .. } | Res::SelfTyAlias { .. })
) {
self.found = true;
}
}
_ => {}
}
if !self.found {
visit::walk_ty(self, ty);
}
}
fn visit_expr(&mut self, _: &'ra Expr) {}
}
let mut visitor = GenuineSelfVisitor { r: self.r, found: false };
visitor.visit_ty(ty);
visitor.found
}

/// Returns `true` if `ty` contains a reference type (`&` or pinned `&`).
fn self_type_has_reference(&self, ty: &'ast Ty) -> bool {
struct RefVisitor {
found: bool,
}
impl<'ra> Visitor<'ra> for RefVisitor {
fn visit_ty(&mut self, ty: &'ra Ty) {
if matches!(ty.kind, TyKind::Ref(..) | TyKind::PinnedRef(..)) {
self.found = true;
}
if !self.found {
visit::walk_ty(self, ty);
}
}
fn visit_expr(&mut self, _: &'ra Expr) {}
}
let mut visitor = RefVisitor { found: false };
visitor.visit_ty(ty);
visitor.found
}

/// List all the lifetimes that appear in the provided type.
fn find_lifetime_for_self(&self, ty: &'ast Ty) -> Set1<LifetimeRes> {
/// Visits a type to find all the &references, and determines the
Expand Down Expand Up @@ -5593,6 +5661,30 @@ impl ItemInfoCollector<'_, '_, '_> {
}
}

fn fn_output_has_lifetime(output_ty: &FnRetTy) -> bool {
struct Probe(bool);
impl<'ast> Visitor<'ast> for Probe {
fn visit_lifetime(&mut self, _: &'ast Lifetime, _: LifetimeCtxt) {
self.0 = true;
}
fn visit_ty(&mut self, ty: &'ast Ty) {
if self.0 {
return;
}
match &ty.kind {
TyKind::Ref(..) | TyKind::PinnedRef(..) | TyKind::TraitObject(..) => {
self.0 = true;
}
_ => walk_ty(self, ty),
}
}
}
let FnRetTy::Ty(ty) = output_ty else { return false };
let mut p = Probe(false);
p.visit_ty(ty);
p.0
}

fn required_generic_args_suggestion(generics: &ast::Generics) -> Option<String> {
let required = generics
.params
Expand Down
1 change: 1 addition & 0 deletions tests/ui/async-await/inference_var_self_argument.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![allow(self_lifetime_elision_not_applicable)]
//! This is a regression test for an ICE.
//@ edition: 2021

Expand Down
6 changes: 3 additions & 3 deletions tests/ui/async-await/inference_var_self_argument.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0307]: invalid `self` parameter type: `&dyn Foo`
--> $DIR/inference_var_self_argument.rs:5:24
--> $DIR/inference_var_self_argument.rs:6:24
|
LL | async fn foo(self: &dyn Foo) {
| ^^^^^^^^
Expand All @@ -8,14 +8,14 @@ LL | async fn foo(self: &dyn Foo) {
= 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`)

error[E0038]: the trait `Foo` is not dyn compatible
--> $DIR/inference_var_self_argument.rs:5:33
--> $DIR/inference_var_self_argument.rs:6:33
|
LL | async fn foo(self: &dyn Foo) {
| ^ `Foo` is not dyn compatible
|
note: for a trait to be dyn compatible it needs to allow building a vtable
for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
--> $DIR/inference_var_self_argument.rs:5:14
--> $DIR/inference_var_self_argument.rs:6:14
|
LL | trait Foo {
| --- this trait is not dyn compatible...
Expand Down
1 change: 1 addition & 0 deletions tests/ui/borrowck/borrow-in-match-with-format-string.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//@ run-pass
#![allow(self_lifetime_elision_not_applicable)]
// Test for https://github.com/rust-lang/rust/issues/21400 extracted from
// stackoverflow.com/questions/28031155/is-my-borrow-checker-drunk/28031580

Expand Down
1 change: 1 addition & 0 deletions tests/ui/closures/2229_closure_analysis/issue-88476.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//@ edition:2021

#![feature(rustc_attrs)]
#![allow(self_lifetime_elision_not_applicable)]

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

Expand Down
20 changes: 10 additions & 10 deletions tests/ui/closures/2229_closure_analysis/issue-88476.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/issue-88476.rs:20:13
--> $DIR/issue-88476.rs:21:13
|
LL | let x = #[rustc_capture_analysis] move || {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -9,7 +9,7 @@ LL | let x = #[rustc_capture_analysis] move || {
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

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

error: First Pass analysis includes:
--> $DIR/issue-88476.rs:20:39
--> $DIR/issue-88476.rs:21:39
|
LL | let x = #[rustc_capture_analysis] move || {
| _______________________________________^
Expand All @@ -28,13 +28,13 @@ LL | | };
| |_____^
|
note: Capturing f[(0, 0)] -> Immutable
--> $DIR/issue-88476.rs:26:26
--> $DIR/issue-88476.rs:27:26
|
LL | println!("{:?}", f.0);
| ^^^

error: Min Capture analysis includes:
--> $DIR/issue-88476.rs:20:39
--> $DIR/issue-88476.rs:21:39
|
LL | let x = #[rustc_capture_analysis] move || {
| _______________________________________^
Expand All @@ -43,13 +43,13 @@ LL | | };
| |_____^
|
note: Min Capture f[] -> ByValue
--> $DIR/issue-88476.rs:26:26
--> $DIR/issue-88476.rs:27:26
|
LL | println!("{:?}", f.0);
| ^^^

error: First Pass analysis includes:
--> $DIR/issue-88476.rs:48:39
--> $DIR/issue-88476.rs:49:39
|
LL | let c = #[rustc_capture_analysis] move || {
| _______________________________________^
Expand All @@ -58,13 +58,13 @@ LL | | };
| |_____^
|
note: Capturing character[(0, 0)] -> Immutable
--> $DIR/issue-88476.rs:54:24
--> $DIR/issue-88476.rs:55:24
|
LL | println!("{}", character.hp)
| ^^^^^^^^^^^^

error: Min Capture analysis includes:
--> $DIR/issue-88476.rs:48:39
--> $DIR/issue-88476.rs:49:39
|
LL | let c = #[rustc_capture_analysis] move || {
| _______________________________________^
Expand All @@ -73,7 +73,7 @@ LL | | };
| |_____^
|
note: Min Capture character[(0, 0)] -> ByValue
--> $DIR/issue-88476.rs:54:24
--> $DIR/issue-88476.rs:55:24
|
LL | println!("{}", character.hp)
| ^^^^^^^^^^^^
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//@ check-pass
//@ edition:2021

#![allow(self_lifetime_elision_not_applicable)]

use std::rc::Rc;

// Test that we restrict precision when moving not-`Copy` types, if any of the parent paths
Expand Down
1 change: 1 addition & 0 deletions tests/ui/internal-lints/rustc_pass_by_value_self.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#![feature(rustc_attrs)]
#![deny(rustc::disallowed_pass_by_ref)]
#![allow(unused)]
#![allow(self_lifetime_elision_not_applicable)]

#[rustc_pass_by_value]
struct TyCtxt<'tcx> {
Expand Down
10 changes: 5 additions & 5 deletions tests/ui/internal-lints/rustc_pass_by_value_self.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: passing `TyCtxt<'tcx>` by reference
--> $DIR/rustc_pass_by_value_self.rs:18:15
--> $DIR/rustc_pass_by_value_self.rs:19:15
|
LL | fn by_ref(&self) {}
| ^^^^^ help: try passing by value: `TyCtxt<'tcx>`
Expand All @@ -11,25 +11,25 @@ LL | #![deny(rustc::disallowed_pass_by_ref)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: passing `Ty<'tcx>` by reference
--> $DIR/rustc_pass_by_value_self.rs:30:21
--> $DIR/rustc_pass_by_value_self.rs:31:21
|
LL | fn by_ref(self: &Ty<'tcx>) {}
| ^^^^^^^^^ help: try passing by value: `Ty<'tcx>`

error: passing `Foo` by reference
--> $DIR/rustc_pass_by_value_self.rs:37:17
--> $DIR/rustc_pass_by_value_self.rs:38:17
|
LL | fn with_ref(&self) {}
| ^^^^^ help: try passing by value: `Foo`

error: passing `WithParameters<T, 1>` by reference
--> $DIR/rustc_pass_by_value_self.rs:47:17
--> $DIR/rustc_pass_by_value_self.rs:48:17
|
LL | fn with_ref(&self) {}
| ^^^^^ help: try passing by value: `WithParameters<T, 1>`

error: passing `WithParameters<T, 1, u8>` by reference
--> $DIR/rustc_pass_by_value_self.rs:51:17
--> $DIR/rustc_pass_by_value_self.rs:52:17
|
LL | fn with_ref(&self) {}
| ^^^^^ help: try passing by value: `WithParameters<T, 1, u8>`
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/issues/issue-17905-2.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(self_lifetime_elision_not_applicable)]

#[derive(Debug)]
struct Pair<T, V> (T, V);

Expand Down
Loading
Loading