Skip to content

Commit ec91d71

Browse files
authored
Rollup merge of #123523 - estebank:issue-123414, r=BoxyUwU
Account for trait/impl difference when suggesting changing argument from ref to mut ref Do not ICE when encountering a lifetime error involving an argument with an immutable reference of a method that differs from the trait definition. Fix #123414.
2 parents 1e99af5 + 731c0e5 commit ec91d71

7 files changed

+158
-43
lines changed

compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

+54-35
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
2121

2222
use crate::diagnostics::BorrowedContentSource;
2323
use crate::util::FindAssignments;
24-
use crate::MirBorrowckCtxt;
24+
use crate::{session_diagnostics, MirBorrowckCtxt};
2525

2626
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
2727
pub(crate) enum AccessKind {
@@ -234,7 +234,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
234234
Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
235235
|_kind, var_span| {
236236
let place = self.describe_any_place(access_place.as_ref());
237-
crate::session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
237+
session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
238238
place,
239239
var_span,
240240
}
@@ -667,19 +667,26 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
667667
/// User cannot make signature of a trait mutable without changing the
668668
/// trait. So we find if this error belongs to a trait and if so we move
669669
/// suggestion to the trait or disable it if it is out of scope of this crate
670-
fn is_error_in_trait(&self, local: Local) -> (bool, Option<Span>) {
670+
///
671+
/// The returned values are:
672+
/// - is the current item an assoc `fn` of an impl that corresponds to a trait def? if so, we
673+
/// have to suggest changing both the impl `fn` arg and the trait `fn` arg
674+
/// - is the trait from the local crate? If not, we can't suggest changing signatures
675+
/// - `Span` of the argument in the trait definition
676+
fn is_error_in_trait(&self, local: Local) -> (bool, bool, Option<Span>) {
671677
if self.body.local_kind(local) != LocalKind::Arg {
672-
return (false, None);
678+
return (false, false, None);
673679
}
674680
let my_def = self.body.source.def_id();
675681
let my_hir = self.infcx.tcx.local_def_id_to_hir_id(my_def.as_local().unwrap());
676682
let Some(td) =
677683
self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
678684
else {
679-
return (false, None);
685+
return (false, false, None);
680686
};
681687
(
682688
true,
689+
td.is_local(),
683690
td.as_local().and_then(|tld| match self.infcx.tcx.hir_node_by_def_id(tld) {
684691
Node::Item(hir::Item { kind: hir::ItemKind::Trait(_, _, _, _, items), .. }) => {
685692
let mut f_in_trait_opt = None;
@@ -695,19 +702,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
695702
break;
696703
}
697704
f_in_trait_opt.and_then(|f_in_trait| {
698-
match self.infcx.tcx.hir_node(f_in_trait) {
699-
Node::TraitItem(hir::TraitItem {
700-
kind:
701-
hir::TraitItemKind::Fn(
702-
hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. },
703-
_,
704-
),
705-
..
706-
}) => {
707-
let hir::Ty { span, .. } = *inputs.get(local.index() - 1)?;
708-
Some(span)
709-
}
710-
_ => None,
705+
if let Node::TraitItem(ti) = self.infcx.tcx.hir_node(f_in_trait)
706+
&& let hir::TraitItemKind::Fn(sig, _) = ti.kind
707+
&& let Some(ty) = sig.decl.inputs.get(local.index() - 1)
708+
&& let hir::TyKind::Ref(_, mut_ty) = ty.kind
709+
&& let hir::Mutability::Not = mut_ty.mutbl
710+
&& sig.decl.implicit_self.has_implicit_self()
711+
{
712+
Some(ty.span)
713+
} else {
714+
None
711715
}
712716
})
713717
}
@@ -1061,20 +1065,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
10611065
let (pointer_sigil, pointer_desc) =
10621066
if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
10631067

1064-
let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
1065-
if is_trait_sig && local_trait.is_none() {
1068+
let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local);
1069+
1070+
if is_trait_sig && !is_local {
1071+
// Do not suggest to change the signature when the trait comes from another crate.
1072+
err.span_label(
1073+
local_decl.source_info.span,
1074+
format!("this is an immutable {pointer_desc}"),
1075+
);
10661076
return;
10671077
}
1068-
1069-
let decl_span = match local_trait {
1070-
Some(span) => span,
1071-
None => local_decl.source_info.span,
1072-
};
1078+
let decl_span = local_decl.source_info.span;
10731079

10741080
let label = match *local_decl.local_info() {
10751081
LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
10761082
let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span);
1077-
Some((true, decl_span, suggestion))
1083+
let additional =
1084+
local_trait.map(|span| (span, suggest_ampmut_self(self.infcx.tcx, span)));
1085+
Some((true, decl_span, suggestion, additional))
10781086
}
10791087

10801088
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
@@ -1113,7 +1121,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
11131121
// don't create labels for compiler-generated spans
11141122
Some(_) => None,
11151123
None => {
1116-
let label = if name != kw::SelfLower {
1124+
let (has_sugg, decl_span, sugg) = if name != kw::SelfLower {
11171125
suggest_ampmut(
11181126
self.infcx.tcx,
11191127
local_decl.ty,
@@ -1140,7 +1148,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
11401148
),
11411149
}
11421150
};
1143-
Some(label)
1151+
Some((has_sugg, decl_span, sugg, None))
11441152
}
11451153
}
11461154
}
@@ -1151,22 +1159,33 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
11511159
})) => {
11521160
let pattern_span: Span = local_decl.source_info.span;
11531161
suggest_ref_mut(self.infcx.tcx, pattern_span)
1154-
.map(|span| (true, span, "mut ".to_owned()))
1162+
.map(|span| (true, span, "mut ".to_owned(), None))
11551163
}
11561164

11571165
_ => unreachable!(),
11581166
};
11591167

11601168
match label {
1161-
Some((true, err_help_span, suggested_code)) => {
1162-
err.span_suggestion_verbose(
1163-
err_help_span,
1164-
format!("consider changing this to be a mutable {pointer_desc}"),
1165-
suggested_code,
1169+
Some((true, err_help_span, suggested_code, additional)) => {
1170+
let mut sugg = vec![(err_help_span, suggested_code)];
1171+
if let Some(s) = additional {
1172+
sugg.push(s);
1173+
}
1174+
1175+
err.multipart_suggestion_verbose(
1176+
format!(
1177+
"consider changing this to be a mutable {pointer_desc}{}",
1178+
if is_trait_sig {
1179+
" in the `impl` method and the `trait` definition"
1180+
} else {
1181+
""
1182+
}
1183+
),
1184+
sugg,
11661185
Applicability::MachineApplicable,
11671186
);
11681187
}
1169-
Some((false, err_label_span, message)) => {
1188+
Some((false, err_label_span, message, _)) => {
11701189
let def_id = self.body.source.def_id();
11711190
let hir_id = if let Some(local_def_id) = def_id.as_local()
11721191
&& let Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id)

tests/ui/borrowck/argument_number_mismatch_ice.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ error[E0594]: cannot assign to `*input`, which is behind a `&` reference
1212
|
1313
LL | *input = self.0;
1414
| ^^^^^^^^^^^^^^^ `input` is a `&` reference, so the data it refers to cannot be written
15+
|
16+
help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition
17+
|
18+
LL | fn example(&self, input: &mut i32) {
19+
| +++
1520

1621
error: aborting due to 2 previous errors
1722

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Issue https://github.com/rust-lang/rust/issues/123414
2+
trait MemoryUnit {
3+
extern "C" fn read_word(&mut self) -> u8;
4+
extern "C" fn read_dword(Self::Assoc<'_>) -> u16;
5+
//~^ WARNING anonymous parameters are deprecated and will be removed in the next edition
6+
//~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
7+
//~| ERROR associated type `Assoc` not found for `Self`
8+
}
9+
10+
struct ROM {}
11+
12+
impl MemoryUnit for ROM {
13+
//~^ ERROR not all trait items implemented, missing: `read_word`
14+
extern "C" fn read_dword(&'_ self) -> u16 {
15+
//~^ ERROR method `read_dword` has a `&self` declaration in the impl, but not in the trait
16+
let a16 = self.read_word() as u16;
17+
//~^ ERROR cannot borrow `*self` as mutable, as it is behind a `&` reference
18+
let b16 = self.read_word() as u16;
19+
//~^ ERROR cannot borrow `*self` as mutable, as it is behind a `&` reference
20+
21+
(b16 << 8) | a16
22+
}
23+
}
24+
25+
pub fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
warning: anonymous parameters are deprecated and will be removed in the next edition
2+
--> $DIR/trait-impl-argument-difference-ice.rs:4:30
3+
|
4+
LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16;
5+
| ^^^^^^^^^^^^^^^ help: try naming the parameter or explicitly ignoring it: `_: Self::Assoc<'_>`
6+
|
7+
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
8+
= note: for more information, see issue #41686 <https://github.com/rust-lang/rust/issues/41686>
9+
= note: `#[warn(anonymous_parameters)]` on by default
10+
11+
error[E0220]: associated type `Assoc` not found for `Self`
12+
--> $DIR/trait-impl-argument-difference-ice.rs:4:36
13+
|
14+
LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16;
15+
| ^^^^^ associated type `Assoc` not found
16+
17+
error[E0185]: method `read_dword` has a `&self` declaration in the impl, but not in the trait
18+
--> $DIR/trait-impl-argument-difference-ice.rs:14:5
19+
|
20+
LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16;
21+
| ------------------------------------------------- trait method declared without `&self`
22+
...
23+
LL | extern "C" fn read_dword(&'_ self) -> u16 {
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&self` used in impl
25+
26+
error[E0046]: not all trait items implemented, missing: `read_word`
27+
--> $DIR/trait-impl-argument-difference-ice.rs:12:1
28+
|
29+
LL | extern "C" fn read_word(&mut self) -> u8;
30+
| ----------------------------------------- `read_word` from trait
31+
...
32+
LL | impl MemoryUnit for ROM {
33+
| ^^^^^^^^^^^^^^^^^^^^^^^ missing `read_word` in implementation
34+
35+
error[E0596]: cannot borrow `*self` as mutable, as it is behind a `&` reference
36+
--> $DIR/trait-impl-argument-difference-ice.rs:16:19
37+
|
38+
LL | let a16 = self.read_word() as u16;
39+
| ^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
40+
|
41+
help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition
42+
|
43+
LL | extern "C" fn read_dword(&'_ mut self) -> u16 {
44+
| ~~~~~~~~~~~~
45+
46+
error[E0596]: cannot borrow `*self` as mutable, as it is behind a `&` reference
47+
--> $DIR/trait-impl-argument-difference-ice.rs:18:19
48+
|
49+
LL | let b16 = self.read_word() as u16;
50+
| ^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
51+
|
52+
help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition
53+
|
54+
LL | extern "C" fn read_dword(&'_ mut self) -> u16 {
55+
| ~~~~~~~~~~~~
56+
57+
error: aborting due to 5 previous errors; 1 warning emitted
58+
59+
Some errors have detailed explanations: E0046, E0185, E0220, E0596.
60+
For more information about an error, try `rustc --explain E0046`.

tests/ui/suggestions/issue-68049-1.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
error[E0594]: cannot assign to `self.0`, which is behind a `&` reference
22
--> $DIR/issue-68049-1.rs:7:9
33
|
4+
LL | unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
5+
| ----- this is an immutable reference
46
LL | self.0 += 1;
57
| ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written
68

tests/ui/suggestions/issue-68049-2.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
trait Hello {
2-
fn example(&self, input: &i32); // should suggest here
2+
fn example(&self, input: &i32);
33
}
44

55
struct Test1(i32);
66

77
impl Hello for Test1 {
8-
fn example(&self, input: &i32) { // should not suggest here
8+
fn example(&self, input: &i32) {
99
*input = self.0; //~ ERROR cannot assign
1010
}
1111
}
1212

1313
struct Test2(i32);
1414

1515
impl Hello for Test2 {
16-
fn example(&self, input: &i32) { // should not suggest here
16+
fn example(&self, input: &i32) {
1717
self.0 += *input; //~ ERROR cannot assign
1818
}
1919
}

tests/ui/suggestions/issue-68049-2.stderr

+9-5
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ error[E0594]: cannot assign to `*input`, which is behind a `&` reference
44
LL | *input = self.0;
55
| ^^^^^^^^^^^^^^^ `input` is a `&` reference, so the data it refers to cannot be written
66
|
7-
help: consider changing this to be a mutable reference
7+
help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition
88
|
9-
LL | fn example(&self, input: &mut i32) { // should not suggest here
9+
LL | fn example(&self, input: &mut i32) {
1010
| +++
1111

1212
error[E0594]: cannot assign to `self.0`, which is behind a `&` reference
@@ -15,10 +15,14 @@ error[E0594]: cannot assign to `self.0`, which is behind a `&` reference
1515
LL | self.0 += *input;
1616
| ^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written
1717
|
18-
help: consider changing this to be a mutable reference
18+
help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition
19+
|
20+
LL ~ fn example(&mut self, input: &i32);
21+
LL | }
22+
...
23+
LL | impl Hello for Test2 {
24+
LL ~ fn example(&mut self, input: &i32) {
1925
|
20-
LL | fn example(&mut self, input: &i32); // should suggest here
21-
| ~~~~~~~~~
2226

2327
error: aborting due to 2 previous errors
2428

0 commit comments

Comments
 (0)