Skip to content

Commit 67c9dbf

Browse files
authored
Rollup merge of #108841 - jackh726:issue-90528, r=compiler-errors
Add suggestion to diagnostic when user has array but trait wants slice. (rebased) Rebase of #91314, except for change to multipart suggestion Resolves #90528 r? ``@compiler-errors`` since you requested the multipart suggestion
2 parents b16ed69 + 8ac7d0e commit 67c9dbf

10 files changed

+490
-7
lines changed

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

+14-6
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
10241024
// Can't show anything else useful, try to find similar impls.
10251025
let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
10261026
if !self.report_similar_impl_candidates(
1027-
impl_candidates,
1027+
&impl_candidates,
10281028
trait_ref,
10291029
body_def_id,
10301030
&mut err,
@@ -1060,14 +1060,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
10601060
let impl_candidates =
10611061
self.find_similar_impl_candidates(trait_pred);
10621062
self.report_similar_impl_candidates(
1063-
impl_candidates,
1063+
&impl_candidates,
10641064
trait_ref,
10651065
body_def_id,
10661066
&mut err,
10671067
true,
10681068
);
10691069
}
10701070
}
1071+
1072+
self.maybe_suggest_convert_to_slice(
1073+
&mut err,
1074+
trait_ref,
1075+
impl_candidates.as_slice(),
1076+
span,
1077+
);
10711078
}
10721079

10731080
// Changing mutability doesn't make a difference to whether we have
@@ -1514,7 +1521,7 @@ trait InferCtxtPrivExt<'tcx> {
15141521

15151522
fn report_similar_impl_candidates(
15161523
&self,
1517-
impl_candidates: Vec<ImplCandidate<'tcx>>,
1524+
impl_candidates: &[ImplCandidate<'tcx>],
15181525
trait_ref: ty::PolyTraitRef<'tcx>,
15191526
body_def_id: LocalDefId,
15201527
err: &mut Diagnostic,
@@ -2004,7 +2011,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
20042011

20052012
fn report_similar_impl_candidates(
20062013
&self,
2007-
impl_candidates: Vec<ImplCandidate<'tcx>>,
2014+
impl_candidates: &[ImplCandidate<'tcx>],
20082015
trait_ref: ty::PolyTraitRef<'tcx>,
20092016
body_def_id: LocalDefId,
20102017
err: &mut Diagnostic,
@@ -2113,7 +2120,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
21132120
// Prefer more similar candidates first, then sort lexicographically
21142121
// by their normalized string representation.
21152122
let mut normalized_impl_candidates_and_similarities = impl_candidates
2116-
.into_iter()
2123+
.iter()
2124+
.copied()
21172125
.map(|ImplCandidate { trait_ref, similarity }| {
21182126
// FIXME(compiler-errors): This should be using `NormalizeExt::normalize`
21192127
let normalized = self
@@ -2326,7 +2334,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
23262334
);
23272335
if impl_candidates.len() < 10 {
23282336
self.report_similar_impl_candidates(
2329-
impl_candidates,
2337+
impl_candidates.as_slice(),
23302338
trait_ref,
23312339
obligation.cause.body_id,
23322340
&mut err,

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

+76-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// ignore-tidy-filelength
22

33
use super::{
4-
DefIdOrName, FindExprBySpan, Obligation, ObligationCause, ObligationCauseCode,
4+
DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode,
55
PredicateObligation,
66
};
77

@@ -382,6 +382,14 @@ pub trait TypeErrCtxtExt<'tcx> {
382382
body_id: hir::HirId,
383383
param_env: ty::ParamEnv<'tcx>,
384384
) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>;
385+
386+
fn maybe_suggest_convert_to_slice(
387+
&self,
388+
err: &mut Diagnostic,
389+
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
390+
candidate_impls: &[ImplCandidate<'tcx>],
391+
span: Span,
392+
);
385393
}
386394

387395
fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
@@ -3826,6 +3834,73 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
38263834
}
38273835
assocs_in_this_method
38283836
}
3837+
3838+
/// If the type that failed selection is an array or a reference to an array,
3839+
/// but the trait is implemented for slices, suggest that the user converts
3840+
/// the array into a slice.
3841+
fn maybe_suggest_convert_to_slice(
3842+
&self,
3843+
err: &mut Diagnostic,
3844+
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
3845+
candidate_impls: &[ImplCandidate<'tcx>],
3846+
span: Span,
3847+
) {
3848+
// Three cases where we can make a suggestion:
3849+
// 1. `[T; _]` (array of T)
3850+
// 2. `&[T; _]` (reference to array of T)
3851+
// 3. `&mut [T; _]` (mutable reference to array of T)
3852+
let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() {
3853+
ty::Array(element_ty, _) => (element_ty, None),
3854+
3855+
ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() {
3856+
ty::Array(element_ty, _) => (element_ty, Some(mutability)),
3857+
_ => return,
3858+
},
3859+
3860+
_ => return,
3861+
};
3862+
3863+
// Go through all the candidate impls to see if any of them is for
3864+
// slices of `element_ty` with `mutability`.
3865+
let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() {
3866+
ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => {
3867+
if matches!(*t.kind(), ty::Slice(e) if e == element_ty)
3868+
&& m == mutability.unwrap_or(m)
3869+
{
3870+
// Use the candidate's mutability going forward.
3871+
mutability = Some(m);
3872+
true
3873+
} else {
3874+
false
3875+
}
3876+
}
3877+
_ => false,
3878+
};
3879+
3880+
// Grab the first candidate that matches, if any, and make a suggestion.
3881+
if let Some(slice_ty) = candidate_impls
3882+
.iter()
3883+
.map(|trait_ref| trait_ref.trait_ref.self_ty())
3884+
.filter(|t| is_slice(*t))
3885+
.next()
3886+
{
3887+
let msg = &format!("convert the array to a `{}` slice instead", slice_ty);
3888+
3889+
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
3890+
let mut suggestions = vec![];
3891+
if snippet.starts_with('&') {
3892+
} else if let Some(hir::Mutability::Mut) = mutability {
3893+
suggestions.push((span.shrink_to_lo(), "&mut ".into()));
3894+
} else {
3895+
suggestions.push((span.shrink_to_lo(), "&".into()));
3896+
}
3897+
suggestions.push((span.shrink_to_hi(), "[..]".into()));
3898+
err.multipart_suggestion_verbose(msg, suggestions, Applicability::MaybeIncorrect);
3899+
} else {
3900+
err.span_help(span, msg);
3901+
}
3902+
}
3903+
}
38293904
}
38303905

38313906
/// Add a hint to add a missing borrow or remove an unnecessary one.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
wants_read(&[0u8]);
16+
//~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied
17+
wants_read(&[0u8][..]);
18+
wants_read(&mut [0u8]);
19+
//~^ ERROR the trait bound `&mut [u8; 1]: Read` is not satisfied
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
| ---------- ^^^^^ the trait `Read` is not implemented for `[u8; 1]`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the trait `Read` is implemented for `&[u8]`
10+
note: required by a bound in `wants_read`
11+
--> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23
12+
|
13+
LL | fn wants_read(_: impl Read) {}
14+
| ^^^^ required by this bound in `wants_read`
15+
help: convert the array to a `&[u8]` slice instead
16+
|
17+
LL | wants_read(&[0u8][..]);
18+
| + ++++
19+
20+
error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied
21+
--> $DIR/issue-90528-unsizing-suggestion-1.rs:15:16
22+
|
23+
LL | wants_read(&[0u8]);
24+
| ---------- ^^^^^^ the trait `Read` is not implemented for `&[u8; 1]`
25+
| |
26+
| required by a bound introduced by this call
27+
|
28+
= help: the trait `Read` is implemented for `&[u8]`
29+
note: required by a bound in `wants_read`
30+
--> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23
31+
|
32+
LL | fn wants_read(_: impl Read) {}
33+
| ^^^^ required by this bound in `wants_read`
34+
help: convert the array to a `&[u8]` slice instead
35+
|
36+
LL | wants_read(&[0u8][..]);
37+
| ++++
38+
39+
error[E0277]: the trait bound `&mut [u8; 1]: Read` is not satisfied
40+
--> $DIR/issue-90528-unsizing-suggestion-1.rs:18:16
41+
|
42+
LL | wants_read(&mut [0u8]);
43+
| ---------- ^^^^^^^^^^ the trait `Read` is not implemented for `&mut [u8; 1]`
44+
| |
45+
| required by a bound introduced by this call
46+
|
47+
= help: the trait `Read` is implemented for `&[u8]`
48+
note: required by a bound in `wants_read`
49+
--> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23
50+
|
51+
LL | fn wants_read(_: impl Read) {}
52+
| ^^^^ required by this bound in `wants_read`
53+
54+
error: aborting due to 3 previous errors
55+
56+
For more information about this error, try `rustc --explain E0277`.
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 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+
wants_read(&x);
17+
//~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied
18+
wants_read(&x[..]);
19+
20+
let x = &[0u8];
21+
wants_read(x);
22+
//~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied
23+
wants_read(&x);
24+
//~^ ERROR the trait bound `&&[u8; 1]: Read` is not satisfied
25+
wants_read(*x);
26+
//~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied
27+
wants_read(&x[..]);
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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+
| ---------- ^ the trait `Read` is not implemented for `[u8; 1]`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the trait `Read` is implemented for `&[u8]`
10+
note: required by a bound in `wants_read`
11+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
12+
|
13+
LL | fn wants_read(_: impl Read) {}
14+
| ^^^^ required by this bound in `wants_read`
15+
help: convert the array to a `&[u8]` slice instead
16+
|
17+
LL | wants_read(&x[..]);
18+
| + ++++
19+
20+
error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied
21+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:16:16
22+
|
23+
LL | wants_read(&x);
24+
| ---------- ^^ the trait `Read` is not implemented for `&[u8; 1]`
25+
| |
26+
| required by a bound introduced by this call
27+
|
28+
= help: the trait `Read` is implemented for `&[u8]`
29+
note: required by a bound in `wants_read`
30+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
31+
|
32+
LL | fn wants_read(_: impl Read) {}
33+
| ^^^^ required by this bound in `wants_read`
34+
help: convert the array to a `&[u8]` slice instead
35+
|
36+
LL | wants_read(&x[..]);
37+
| ++++
38+
39+
error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied
40+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:21:16
41+
|
42+
LL | wants_read(x);
43+
| ---------- ^ the trait `Read` is not implemented for `&[u8; 1]`
44+
| |
45+
| required by a bound introduced by this call
46+
|
47+
= help: the trait `Read` is implemented for `&[u8]`
48+
note: required by a bound in `wants_read`
49+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
50+
|
51+
LL | fn wants_read(_: impl Read) {}
52+
| ^^^^ required by this bound in `wants_read`
53+
help: convert the array to a `&[u8]` slice instead
54+
|
55+
LL | wants_read(&x[..]);
56+
| + ++++
57+
58+
error[E0277]: the trait bound `&&[u8; 1]: Read` is not satisfied
59+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:23:16
60+
|
61+
LL | wants_read(&x);
62+
| ---------- ^^ the trait `Read` is not implemented for `&&[u8; 1]`
63+
| |
64+
| required by a bound introduced by this call
65+
|
66+
= help: the trait `Read` is implemented for `&[u8]`
67+
note: required by a bound in `wants_read`
68+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
69+
|
70+
LL | fn wants_read(_: impl Read) {}
71+
| ^^^^ required by this bound in `wants_read`
72+
73+
error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied
74+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:25:16
75+
|
76+
LL | wants_read(*x);
77+
| ---------- ^^ the trait `Read` is not implemented for `[u8; 1]`
78+
| |
79+
| required by a bound introduced by this call
80+
|
81+
= help: the trait `Read` is implemented for `&[u8]`
82+
note: required by a bound in `wants_read`
83+
--> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
84+
|
85+
LL | fn wants_read(_: impl Read) {}
86+
| ^^^^ required by this bound in `wants_read`
87+
help: convert the array to a `&[u8]` slice instead
88+
|
89+
LL | wants_read(&*x[..]);
90+
| + ++++
91+
92+
error: aborting due to 5 previous errors
93+
94+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
wants_write(&mut [0u8]);
16+
//~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied
17+
wants_write(&mut [0u8][..]);
18+
wants_write(&[0u8]);
19+
//~^ ERROR the trait bound `&[u8; 1]: Write` is not satisfied
20+
wants_write(&[0u8][..]);
21+
//~^ ERROR the trait bound `&[u8]: Write` is not satisfied
22+
}

0 commit comments

Comments
 (0)