Skip to content

Commit 54c4506

Browse files
committed
Add suggestion to diagnostic when user has array but trait wants slice.
For rust-lang#90528.
1 parent 4919988 commit 54c4506

10 files changed

+522
-3
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
498498
} else if !have_alt_message {
499499
// Can't show anything else useful, try to find similar impls.
500500
let impl_candidates = self.find_similar_impl_candidates(trait_ref);
501-
self.report_similar_impl_candidates(impl_candidates, &mut err);
501+
self.report_similar_impl_candidates(&impl_candidates, &mut err);
502+
503+
self.maybe_suggest_convert_to_slice(
504+
&mut err,
505+
trait_ref,
506+
&impl_candidates,
507+
span,
508+
);
502509
}
503510

504511
// Changing mutability doesn't make a difference to whether we have
@@ -1091,7 +1098,7 @@ trait InferCtxtPrivExt<'tcx> {
10911098

10921099
fn report_similar_impl_candidates(
10931100
&self,
1094-
impl_candidates: Vec<ty::TraitRef<'tcx>>,
1101+
impl_candidates: &[ty::TraitRef<'tcx>],
10951102
err: &mut DiagnosticBuilder<'_>,
10961103
);
10971104

@@ -1432,7 +1439,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
14321439

14331440
fn report_similar_impl_candidates(
14341441
&self,
1435-
impl_candidates: Vec<ty::TraitRef<'tcx>>,
1442+
impl_candidates: &[ty::TraitRef<'tcx>],
14361443
err: &mut DiagnosticBuilder<'_>,
14371444
) {
14381445
if impl_candidates.is_empty() {

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,14 @@ pub trait InferCtxtExt<'tcx> {
176176
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
177177
span: Span,
178178
);
179+
180+
fn maybe_suggest_convert_to_slice(
181+
&self,
182+
err: &mut DiagnosticBuilder<'_>,
183+
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
184+
candidate_impls: &[ty::TraitRef<'tcx>],
185+
span: Span,
186+
);
179187
}
180188

181189
fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
@@ -2499,6 +2507,72 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
24992507
}
25002508
}
25012509
}
2510+
2511+
/// If the type that failed selection is an array or a reference to an array,
2512+
/// but the trait is implemented for slices, suggest that the user converts
2513+
/// the array into a slice.
2514+
fn maybe_suggest_convert_to_slice(
2515+
&self,
2516+
err: &mut DiagnosticBuilder<'_>,
2517+
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
2518+
candidate_impls: &[ty::TraitRef<'tcx>],
2519+
span: Span,
2520+
) {
2521+
// Three cases where we can make a suggestion:
2522+
// 1. `[T; _]` (array of T)
2523+
// 2. `&[T; _]` (reference to array of T)
2524+
// 3. `&mut [T; _]` (mutable reference to array of T)
2525+
let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() {
2526+
ty::Array(element_ty, _) => (element_ty, None),
2527+
2528+
ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() {
2529+
ty::Array(element_ty, _) => (element_ty, Some(mutability)),
2530+
_ => return,
2531+
},
2532+
2533+
_ => return,
2534+
};
2535+
2536+
// Go through all the candidate impls to see if any of them is for
2537+
// slices of `element_ty` with `mutability`.
2538+
let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() {
2539+
ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => {
2540+
if matches!(*t.kind(), ty::Slice(e) if e == element_ty)
2541+
&& m == mutability.unwrap_or(m)
2542+
{
2543+
// Use the candidate's mutability going forward.
2544+
mutability = Some(m);
2545+
true
2546+
} else {
2547+
false
2548+
}
2549+
}
2550+
_ => false,
2551+
};
2552+
2553+
// Grab the first candidate that matches, if any, and make a suggestion.
2554+
if let Some(slice_ty) = candidate_impls
2555+
.iter()
2556+
.map(|trait_ref| trait_ref.self_ty())
2557+
.filter(|t| is_slice(*t))
2558+
.next()
2559+
{
2560+
let msg = &format!("convert the array to a `{}` slice instead", slice_ty);
2561+
2562+
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
2563+
let suggestion = if snippet.starts_with('&') {
2564+
format!("{}[..]", snippet)
2565+
} else if let Some(hir::Mutability::Mut) = mutability {
2566+
format!("&mut {}[..]", snippet)
2567+
} else {
2568+
format!("&{}[..]", snippet)
2569+
};
2570+
err.span_suggestion(span, msg, suggestion, Applicability::MaybeIncorrect);
2571+
} else {
2572+
err.span_help(span, msg);
2573+
}
2574+
}
2575+
}
25022576
}
25032577

25042578
/// Collect all the returned expressions within the input expression.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied
2+
// due to a missed unsizing coercion.
3+
//
4+
// This test exercises array literals and a trait implemented on immutable slices.
5+
6+
trait Read {}
7+
8+
impl Read for &[u8] {}
9+
10+
fn wants_read(_: impl Read) {}
11+
12+
fn main() {
13+
wants_read([0u8]);
14+
//~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied
15+
//~| HELP the following implementations were found
16+
//~| HELP convert the array
17+
//~| SUGGESTION &[0u8][..]
18+
wants_read(&[0u8]);
19+
//~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied
20+
//~| HELP the following implementations were found
21+
//~| HELP convert the array
22+
//~| SUGGESTION &[0u8][..]
23+
wants_read(&[0u8][..]);
24+
25+
wants_read(&mut [0u8]);
26+
//~^ ERROR the trait bound `&mut [u8; 1]: Read` is not satisfied
27+
//~| HELP the following implementations were found
28+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied
2+
--> $DIR/issue-90528-unsizing-suggestion-1.rs:13:16
3+
|
4+
LL | wants_read([0u8]);
5+
| ---------- ^^^^^
6+
| | |
7+
| | the trait `Read` is not implemented for `[u8; 1]`
8+
| | help: convert the array to a `&[u8]` slice instead: `&[0u8][..]`
9+
| required by a bound introduced by this call
10+
|
11+
= help: the following implementations were found:
12+
<&[u8] as Read>
13+
note: required by a bound in `wants_read`
14+
--> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23
15+
|
16+
LL | fn wants_read(_: impl Read) {}
17+
| ^^^^ required by this bound in `wants_read`
18+
19+
error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied
20+
--> $DIR/issue-90528-unsizing-suggestion-1.rs:18:16
21+
|
22+
LL | wants_read(&[0u8]);
23+
| ---------- ^^^^^^
24+
| | |
25+
| | the trait `Read` is not implemented for `&[u8; 1]`
26+
| | help: convert the array to a `&[u8]` slice instead: `&[0u8][..]`
27+
| required by a bound introduced by this call
28+
|
29+
= help: the following implementations were found:
30+
<&[u8] as Read>
31+
note: required by a bound in `wants_read`
32+
--> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23
33+
|
34+
LL | fn wants_read(_: impl Read) {}
35+
| ^^^^ required by this bound in `wants_read`
36+
37+
error[E0277]: the trait bound `&mut [u8; 1]: Read` is not satisfied
38+
--> $DIR/issue-90528-unsizing-suggestion-1.rs:25:16
39+
|
40+
LL | wants_read(&mut [0u8]);
41+
| ---------- ^^^^^^^^^^ the trait `Read` is not implemented for `&mut [u8; 1]`
42+
| |
43+
| required by a bound introduced by this call
44+
|
45+
= help: the following implementations were found:
46+
<&[u8] as Read>
47+
note: required by a bound in `wants_read`
48+
--> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23
49+
|
50+
LL | fn wants_read(_: impl Read) {}
51+
| ^^^^ required by this bound in `wants_read`
52+
53+
error: aborting due to 3 previous errors
54+
55+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied
2+
// due to a missed unsizing coercion.
3+
//
4+
// This test exercises array variables and a trait implemented on immmutable slices.
5+
6+
trait Read {}
7+
8+
impl Read for &[u8] {}
9+
10+
fn wants_read(_: impl Read) {}
11+
12+
fn main() {
13+
let x = [0u8];
14+
wants_read(x);
15+
//~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied
16+
//~| HELP the following implementations were found
17+
//~| HELP convert the array
18+
//~| SUGGESTION &x[..]
19+
wants_read(&x);
20+
//~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied
21+
//~| HELP the following implementations were found
22+
//~| HELP convert the array
23+
//~| SUGGESTION &x[..]
24+
wants_read(&x[..]);
25+
26+
let x = &[0u8];
27+
wants_read(x);
28+
//~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied
29+
//~| HELP the following implementations were found
30+
//~| HELP convert the array
31+
//~| SUGGESTION &x[..]
32+
wants_read(&x);
33+
//~^ ERROR the trait bound `&&[u8; 1]: Read` is not satisfied
34+
//~| HELP the following implementations were found
35+
wants_read(*x);
36+
//~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied
37+
//~| HELP the following implementations were found
38+
//~| HELP convert the array
39+
//~| SUGGESTION &*x[..]
40+
// ^^^^^^^ bad suggestion
41+
wants_read(&x[..]);
42+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied
2+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:14:16
3+
|
4+
LL | wants_read(x);
5+
| ---------- ^
6+
| | |
7+
| | the trait `Read` is not implemented for `[u8; 1]`
8+
| | help: convert the array to a `&[u8]` slice instead: `&x[..]`
9+
| required by a bound introduced by this call
10+
|
11+
= help: the following implementations were found:
12+
<&[u8] as Read>
13+
note: required by a bound in `wants_read`
14+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
15+
|
16+
LL | fn wants_read(_: impl Read) {}
17+
| ^^^^ required by this bound in `wants_read`
18+
19+
error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied
20+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:19:16
21+
|
22+
LL | wants_read(&x);
23+
| ---------- ^^
24+
| | |
25+
| | the trait `Read` is not implemented for `&[u8; 1]`
26+
| | help: convert the array to a `&[u8]` slice instead: `&x[..]`
27+
| required by a bound introduced by this call
28+
|
29+
= help: the following implementations were found:
30+
<&[u8] as Read>
31+
note: required by a bound in `wants_read`
32+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
33+
|
34+
LL | fn wants_read(_: impl Read) {}
35+
| ^^^^ required by this bound in `wants_read`
36+
37+
error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied
38+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:27:16
39+
|
40+
LL | wants_read(x);
41+
| ---------- ^
42+
| | |
43+
| | the trait `Read` is not implemented for `&[u8; 1]`
44+
| | help: convert the array to a `&[u8]` slice instead: `&x[..]`
45+
| required by a bound introduced by this call
46+
|
47+
= help: the following implementations were found:
48+
<&[u8] as Read>
49+
note: required by a bound in `wants_read`
50+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
51+
|
52+
LL | fn wants_read(_: impl Read) {}
53+
| ^^^^ required by this bound in `wants_read`
54+
55+
error[E0277]: the trait bound `&&[u8; 1]: Read` is not satisfied
56+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:32:16
57+
|
58+
LL | wants_read(&x);
59+
| ---------- ^^ the trait `Read` is not implemented for `&&[u8; 1]`
60+
| |
61+
| required by a bound introduced by this call
62+
|
63+
= help: the following implementations were found:
64+
<&[u8] as Read>
65+
note: required by a bound in `wants_read`
66+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
67+
|
68+
LL | fn wants_read(_: impl Read) {}
69+
| ^^^^ required by this bound in `wants_read`
70+
71+
error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied
72+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:35:16
73+
|
74+
LL | wants_read(*x);
75+
| ---------- ^^
76+
| | |
77+
| | the trait `Read` is not implemented for `[u8; 1]`
78+
| | help: convert the array to a `&[u8]` slice instead: `&*x[..]`
79+
| required by a bound introduced by this call
80+
|
81+
= help: the following implementations were found:
82+
<&[u8] as Read>
83+
note: required by a bound in `wants_read`
84+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
85+
|
86+
LL | fn wants_read(_: impl Read) {}
87+
| ^^^^ required by this bound in `wants_read`
88+
89+
error: aborting due to 5 previous errors
90+
91+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied
2+
// due to a missed unsizing coercion.
3+
//
4+
// This test exercises array literals and a trait implemented on mutable slices.
5+
6+
trait Write {}
7+
8+
impl Write for & mut [u8] {}
9+
10+
fn wants_write(_: impl Write) {}
11+
12+
fn main() {
13+
wants_write([0u8]);
14+
//~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied
15+
//~| HELP the following implementations were found
16+
//~| HELP convert the array
17+
//~| SUGGESTION &mut [0u8][..]
18+
wants_write(&mut [0u8]);
19+
//~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied
20+
//~| HELP the following implementations were found
21+
//~| HELP convert the array
22+
//~| SUGGESTION &mut [0u8][..]
23+
wants_write(&mut [0u8][..]);
24+
25+
wants_write(&[0u8]);
26+
//~^ ERROR the trait bound `&[u8; 1]: Write` is not satisfied
27+
//~| HELP the following implementations were found
28+
29+
wants_write(&[0u8][..]);
30+
//~^ ERROR the trait bound `&[u8]: Write` is not satisfied
31+
//~| HELP the following implementations were found
32+
//~| HELP consider changing this borrow's mutability
33+
}

0 commit comments

Comments
 (0)