Skip to content

Commit 20f17bc

Browse files
committed
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.
1 parent 76cf07d commit 20f17bc

File tree

6 files changed

+141
-37
lines changed

6 files changed

+141
-37
lines changed

compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

+42-33
Original file line numberDiff line numberDiff line change
@@ -667,19 +667,20 @@ 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+
fn is_error_in_trait(&self, local: Local) -> (bool, bool, Option<Span>) {
671671
if self.body.local_kind(local) != LocalKind::Arg {
672-
return (false, None);
672+
return (false, false, None);
673673
}
674674
let my_def = self.body.source.def_id();
675675
let my_hir = self.infcx.tcx.local_def_id_to_hir_id(my_def.as_local().unwrap());
676676
let Some(td) =
677677
self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
678678
else {
679-
return (false, None);
679+
return (false, false, None);
680680
};
681681
(
682682
true,
683+
td.is_local(),
683684
td.as_local().and_then(|tld| match self.infcx.tcx.hir_node_by_def_id(tld) {
684685
Node::Item(hir::Item { kind: hir::ItemKind::Trait(_, _, _, _, items), .. }) => {
685686
let mut f_in_trait_opt = None;
@@ -695,19 +696,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
695696
break;
696697
}
697698
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,
699+
if let Node::TraitItem(ti) = self.infcx.tcx.hir_node(f_in_trait)
700+
&& let hir::TraitItemKind::Fn(sig, _) = ti.kind
701+
&& let Some(ty) = sig.decl.inputs.get(local.index() - 1)
702+
&& let hir::TyKind::Ref(_, mut_ty) = ty.kind
703+
&& let hir::Mutability::Not = mut_ty.mutbl
704+
&& sig.decl.implicit_self.has_implicit_self()
705+
{
706+
Some(ty.span)
707+
} else {
708+
None
711709
}
712710
})
713711
}
@@ -1061,20 +1059,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
10611059
let (pointer_sigil, pointer_desc) =
10621060
if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
10631061

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

10741074
let label = match *local_decl.local_info() {
10751075
LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
10761076
let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span);
1077-
Some((true, decl_span, suggestion))
1077+
let additional =
1078+
local_trait.map(|span| (span, suggest_ampmut_self(self.infcx.tcx, span)));
1079+
Some((true, decl_span, suggestion, additional))
10781080
}
10791081

10801082
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
@@ -1113,7 +1115,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
11131115
// don't create labels for compiler-generated spans
11141116
Some(_) => None,
11151117
None => {
1116-
let label = if name != kw::SelfLower {
1118+
let (has_sugg, decl_span, sugg) = if name != kw::SelfLower {
11171119
suggest_ampmut(
11181120
self.infcx.tcx,
11191121
local_decl.ty,
@@ -1140,7 +1142,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
11401142
),
11411143
}
11421144
};
1143-
Some(label)
1145+
Some((has_sugg, decl_span, sugg, None))
11441146
}
11451147
}
11461148
}
@@ -1151,22 +1153,29 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
11511153
})) => {
11521154
let pattern_span: Span = local_decl.source_info.span;
11531155
suggest_ref_mut(self.infcx.tcx, pattern_span)
1154-
.map(|span| (true, span, "mut ".to_owned()))
1156+
.map(|span| (true, span, "mut ".to_owned(), None))
11551157
}
11561158

11571159
_ => unreachable!(),
11581160
};
11591161

11601162
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,
1163+
Some((true, err_help_span, suggested_code, additional)) => {
1164+
let mut sugg = vec![(err_help_span, suggested_code)];
1165+
if let Some(s) = additional {
1166+
sugg.push(s);
1167+
}
1168+
1169+
err.multipart_suggestion_verbose(
1170+
format!(
1171+
"consider changing this to be a mutable {pointer_desc}{}",
1172+
if is_trait_sig { "in the `impl` and the `trait`" } else { "" }
1173+
),
1174+
sugg,
11661175
Applicability::MachineApplicable,
11671176
);
11681177
}
1169-
Some((false, err_label_span, message)) => {
1178+
Some((false, err_label_span, message, _)) => {
11701179
let def_id = self.body.source.def_id();
11711180
let hir_id = if let Some(local_def_id) = def_id.as_local()
11721181
&& 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 referencein the `impl` and the `trait`
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,24 @@
1+
trait MemoryUnit {
2+
extern "C" fn read_word(&mut self) -> u8;
3+
extern "C" fn read_dword(Self::Assoc<'_>) -> u16;
4+
//~^ WARNING anonymous parameters are deprecated and will be removed in the next edition
5+
//~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
6+
//~| ERROR associated type `Assoc` not found for `Self`
7+
}
8+
9+
struct ROM {}
10+
11+
impl MemoryUnit for ROM {
12+
//~^ ERROR not all trait items implemented, missing: `read_word`
13+
extern "C" fn read_dword(&'_ self) -> u16 {
14+
//~^ ERROR method `read_dword` has a `&self` declaration in the impl, but not in the trait
15+
let a16 = self.read_word() as u16;
16+
//~^ ERROR cannot borrow `*self` as mutable, as it is behind a `&` reference
17+
let b16 = self.read_word() as u16;
18+
//~^ ERROR cannot borrow `*self` as mutable, as it is behind a `&` reference
19+
20+
(b16 << 8) | a16
21+
}
22+
}
23+
24+
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:3: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:3: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:13: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:11: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:15: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 referencein the `impl` and the `trait`
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:17: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 referencein the `impl` and the `trait`
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.stderr

+8-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ 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 referencein the `impl` and the `trait`
88
|
99
LL | fn example(&self, input: &mut i32) { // should not suggest here
1010
| +++
@@ -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 referencein the `impl` and the `trait`
19+
|
20+
LL ~ fn example(&mut self, input: &i32); // should suggest here
21+
LL | }
22+
...
23+
LL | impl Hello for Test2 {
24+
LL ~ fn example(&mut self, input: &i32) { // should not suggest here
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)