diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index f8df0e2595973..46e2026a2a270 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -498,7 +498,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } else if !have_alt_message { // Can't show anything else useful, try to find similar impls. let impl_candidates = self.find_similar_impl_candidates(trait_ref); - self.report_similar_impl_candidates(impl_candidates, &mut err); + self.report_similar_impl_candidates(&impl_candidates, &mut err); + + self.maybe_suggest_convert_to_slice( + &mut err, + trait_ref, + &impl_candidates, + span, + ); } // Changing mutability doesn't make a difference to whether we have @@ -1091,7 +1098,7 @@ trait InferCtxtPrivExt<'tcx> { fn report_similar_impl_candidates( &self, - impl_candidates: Vec>, + impl_candidates: &[ty::TraitRef<'tcx>], err: &mut DiagnosticBuilder<'_>, ); @@ -1432,7 +1439,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { fn report_similar_impl_candidates( &self, - impl_candidates: Vec>, + impl_candidates: &[ty::TraitRef<'tcx>], err: &mut DiagnosticBuilder<'_>, ) { if impl_candidates.is_empty() { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index a90140a9b50b9..686d0cadffc11 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -176,6 +176,14 @@ pub trait InferCtxtExt<'tcx> { trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, span: Span, ); + + fn maybe_suggest_convert_to_slice( + &self, + err: &mut DiagnosticBuilder<'_>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + candidate_impls: &[ty::TraitRef<'tcx>], + span: Span, + ); } fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) { @@ -2499,6 +2507,72 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } } + + /// If the type that failed selection is an array or a reference to an array, + /// but the trait is implemented for slices, suggest that the user converts + /// the array into a slice. + fn maybe_suggest_convert_to_slice( + &self, + err: &mut DiagnosticBuilder<'_>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + candidate_impls: &[ty::TraitRef<'tcx>], + span: Span, + ) { + // Three cases where we can make a suggestion: + // 1. `[T; _]` (array of T) + // 2. `&[T; _]` (reference to array of T) + // 3. `&mut [T; _]` (mutable reference to array of T) + let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() { + ty::Array(element_ty, _) => (element_ty, None), + + ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() { + ty::Array(element_ty, _) => (element_ty, Some(mutability)), + _ => return, + }, + + _ => return, + }; + + // Go through all the candidate impls to see if any of them is for + // slices of `element_ty` with `mutability`. + let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() { + ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => { + if matches!(*t.kind(), ty::Slice(e) if e == element_ty) + && m == mutability.unwrap_or(m) + { + // Use the candidate's mutability going forward. + mutability = Some(m); + true + } else { + false + } + } + _ => false, + }; + + // Grab the first candidate that matches, if any, and make a suggestion. + if let Some(slice_ty) = candidate_impls + .iter() + .map(|trait_ref| trait_ref.self_ty()) + .filter(|t| is_slice(*t)) + .next() + { + let msg = &format!("convert the array to a `{}` slice instead", slice_ty); + + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let suggestion = if snippet.starts_with('&') { + format!("{}[..]", snippet) + } else if let Some(hir::Mutability::Mut) = mutability { + format!("&mut {}[..]", snippet) + } else { + format!("&{}[..]", snippet) + }; + err.span_suggestion(span, msg, suggestion, Applicability::MaybeIncorrect); + } else { + err.span_help(span, msg); + } + } + } } /// Collect all the returned expressions within the input expression. diff --git a/src/test/ui/dst/issue-90528-unsizing-suggestion-1.rs b/src/test/ui/dst/issue-90528-unsizing-suggestion-1.rs new file mode 100644 index 0000000000000..c04b5374cf3a9 --- /dev/null +++ b/src/test/ui/dst/issue-90528-unsizing-suggestion-1.rs @@ -0,0 +1,28 @@ +// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied +// due to a missed unsizing coercion. +// +// This test exercises array literals and a trait implemented on immutable slices. + +trait Read {} + +impl Read for &[u8] {} + +fn wants_read(_: impl Read) {} + +fn main() { + wants_read([0u8]); + //~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied + //~| HELP the following implementations were found + //~| HELP convert the array + //~| SUGGESTION &[0u8][..] + wants_read(&[0u8]); + //~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied + //~| HELP the following implementations were found + //~| HELP convert the array + //~| SUGGESTION &[0u8][..] + wants_read(&[0u8][..]); + + wants_read(&mut [0u8]); + //~^ ERROR the trait bound `&mut [u8; 1]: Read` is not satisfied + //~| HELP the following implementations were found +} diff --git a/src/test/ui/dst/issue-90528-unsizing-suggestion-1.stderr b/src/test/ui/dst/issue-90528-unsizing-suggestion-1.stderr new file mode 100644 index 0000000000000..1313e047f5e01 --- /dev/null +++ b/src/test/ui/dst/issue-90528-unsizing-suggestion-1.stderr @@ -0,0 +1,55 @@ +error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-1.rs:13:16 + | +LL | wants_read([0u8]); + | ---------- ^^^^^ + | | | + | | the trait `Read` is not implemented for `[u8; 1]` + | | help: convert the array to a `&[u8]` slice instead: `&[0u8][..]` + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&[u8] as Read> +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` + +error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-1.rs:18:16 + | +LL | wants_read(&[0u8]); + | ---------- ^^^^^^ + | | | + | | the trait `Read` is not implemented for `&[u8; 1]` + | | help: convert the array to a `&[u8]` slice instead: `&[0u8][..]` + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&[u8] as Read> +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` + +error[E0277]: the trait bound `&mut [u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-1.rs:25:16 + | +LL | wants_read(&mut [0u8]); + | ---------- ^^^^^^^^^^ the trait `Read` is not implemented for `&mut [u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&[u8] as Read> +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/dst/issue-90528-unsizing-suggestion-2.rs b/src/test/ui/dst/issue-90528-unsizing-suggestion-2.rs new file mode 100644 index 0000000000000..e962761d7a27e --- /dev/null +++ b/src/test/ui/dst/issue-90528-unsizing-suggestion-2.rs @@ -0,0 +1,42 @@ +// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied +// due to a missed unsizing coercion. +// +// This test exercises array variables and a trait implemented on immmutable slices. + +trait Read {} + +impl Read for &[u8] {} + +fn wants_read(_: impl Read) {} + +fn main() { + let x = [0u8]; + wants_read(x); + //~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied + //~| HELP the following implementations were found + //~| HELP convert the array + //~| SUGGESTION &x[..] + wants_read(&x); + //~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied + //~| HELP the following implementations were found + //~| HELP convert the array + //~| SUGGESTION &x[..] + wants_read(&x[..]); + + let x = &[0u8]; + wants_read(x); + //~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied + //~| HELP the following implementations were found + //~| HELP convert the array + //~| SUGGESTION &x[..] + wants_read(&x); + //~^ ERROR the trait bound `&&[u8; 1]: Read` is not satisfied + //~| HELP the following implementations were found + wants_read(*x); + //~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied + //~| HELP the following implementations were found + //~| HELP convert the array + //~| SUGGESTION &*x[..] + // ^^^^^^^ bad suggestion + wants_read(&x[..]); +} diff --git a/src/test/ui/dst/issue-90528-unsizing-suggestion-2.stderr b/src/test/ui/dst/issue-90528-unsizing-suggestion-2.stderr new file mode 100644 index 0000000000000..90c6f85fc3f68 --- /dev/null +++ b/src/test/ui/dst/issue-90528-unsizing-suggestion-2.stderr @@ -0,0 +1,91 @@ +error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-2.rs:14:16 + | +LL | wants_read(x); + | ---------- ^ + | | | + | | the trait `Read` is not implemented for `[u8; 1]` + | | help: convert the array to a `&[u8]` slice instead: `&x[..]` + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&[u8] as Read> +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` + +error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-2.rs:19:16 + | +LL | wants_read(&x); + | ---------- ^^ + | | | + | | the trait `Read` is not implemented for `&[u8; 1]` + | | help: convert the array to a `&[u8]` slice instead: `&x[..]` + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&[u8] as Read> +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` + +error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-2.rs:27:16 + | +LL | wants_read(x); + | ---------- ^ + | | | + | | the trait `Read` is not implemented for `&[u8; 1]` + | | help: convert the array to a `&[u8]` slice instead: `&x[..]` + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&[u8] as Read> +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` + +error[E0277]: the trait bound `&&[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-2.rs:32:16 + | +LL | wants_read(&x); + | ---------- ^^ the trait `Read` is not implemented for `&&[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&[u8] as Read> +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` + +error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-2.rs:35:16 + | +LL | wants_read(*x); + | ---------- ^^ + | | | + | | the trait `Read` is not implemented for `[u8; 1]` + | | help: convert the array to a `&[u8]` slice instead: `&*x[..]` + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&[u8] as Read> +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/dst/issue-90528-unsizing-suggestion-3.rs b/src/test/ui/dst/issue-90528-unsizing-suggestion-3.rs new file mode 100644 index 0000000000000..00b7d19db90ef --- /dev/null +++ b/src/test/ui/dst/issue-90528-unsizing-suggestion-3.rs @@ -0,0 +1,33 @@ +// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied +// due to a missed unsizing coercion. +// +// This test exercises array literals and a trait implemented on mutable slices. + +trait Write {} + +impl Write for & mut [u8] {} + +fn wants_write(_: impl Write) {} + +fn main() { + wants_write([0u8]); + //~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied + //~| HELP the following implementations were found + //~| HELP convert the array + //~| SUGGESTION &mut [0u8][..] + wants_write(&mut [0u8]); + //~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied + //~| HELP the following implementations were found + //~| HELP convert the array + //~| SUGGESTION &mut [0u8][..] + wants_write(&mut [0u8][..]); + + wants_write(&[0u8]); + //~^ ERROR the trait bound `&[u8; 1]: Write` is not satisfied + //~| HELP the following implementations were found + + wants_write(&[0u8][..]); + //~^ ERROR the trait bound `&[u8]: Write` is not satisfied + //~| HELP the following implementations were found + //~| HELP consider changing this borrow's mutability +} diff --git a/src/test/ui/dst/issue-90528-unsizing-suggestion-3.stderr b/src/test/ui/dst/issue-90528-unsizing-suggestion-3.stderr new file mode 100644 index 0000000000000..60dd7ebf2a6e0 --- /dev/null +++ b/src/test/ui/dst/issue-90528-unsizing-suggestion-3.stderr @@ -0,0 +1,75 @@ +error[E0277]: the trait bound `[u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-3.rs:13:17 + | +LL | wants_write([0u8]); + | ----------- ^^^^^ + | | | + | | the trait `Write` is not implemented for `[u8; 1]` + | | help: convert the array to a `&mut [u8]` slice instead: `&mut [0u8][..]` + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&mut [u8] as Write> +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` + +error[E0277]: the trait bound `&mut [u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-3.rs:18:17 + | +LL | wants_write(&mut [0u8]); + | ----------- ^^^^^^^^^^ + | | | + | | the trait `Write` is not implemented for `&mut [u8; 1]` + | | help: convert the array to a `&mut [u8]` slice instead: `&mut [0u8][..]` + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&mut [u8] as Write> +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` + +error[E0277]: the trait bound `&[u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-3.rs:25:17 + | +LL | wants_write(&[0u8]); + | ----------- ^^^^^^ the trait `Write` is not implemented for `&[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&mut [u8] as Write> +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` + +error[E0277]: the trait bound `&[u8]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-3.rs:29:17 + | +LL | wants_write(&[0u8][..]); + | ----------- ^^^^^^^^^^ the trait `Write` is not implemented for `&[u8]` + | | + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&mut [u8] as Write> +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` +help: consider changing this borrow's mutability + | +LL | wants_write(&mut [0u8][..]); + | ~~~~ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/dst/issue-90528-unsizing-suggestion-4.rs b/src/test/ui/dst/issue-90528-unsizing-suggestion-4.rs new file mode 100644 index 0000000000000..a1fea4b590904 --- /dev/null +++ b/src/test/ui/dst/issue-90528-unsizing-suggestion-4.rs @@ -0,0 +1,39 @@ +// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied +// due to a missed unsizing coercion. +// +// This test exercises array variables and a trait implemented on mutable slices. + +trait Write {} + +impl Write for & mut [u8] {} + +fn wants_write(_: impl Write) {} + +fn main() { + let mut x = [0u8]; + wants_write(x); + //~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied + //~| HELP the following implementations were found + //~| HELP convert the array + //~| SUGGESTION &mut x[..] + wants_write(&mut x); + //~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied + //~| HELP the following implementations were found + //~| HELP convert the array + //~| SUGGESTION &mut x[..] + wants_write(&mut x[..]); + + let x = &mut [0u8]; + wants_write(x); + //~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied + //~| HELP the following implementations were found + //~| HELP convert the array + //~| SUGGESTION &mut x[..] + wants_write(*x); + //~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied + //~| HELP the following implementations were found + //~| HELP convert the array + //~| SUGGESTION &mut *x[..] + // ^^^^^^^ bad suggestion + wants_write(&mut x[..]); +} diff --git a/src/test/ui/dst/issue-90528-unsizing-suggestion-4.stderr b/src/test/ui/dst/issue-90528-unsizing-suggestion-4.stderr new file mode 100644 index 0000000000000..bec75c6ee159d --- /dev/null +++ b/src/test/ui/dst/issue-90528-unsizing-suggestion-4.stderr @@ -0,0 +1,75 @@ +error[E0277]: the trait bound `[u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-4.rs:14:17 + | +LL | wants_write(x); + | ----------- ^ + | | | + | | the trait `Write` is not implemented for `[u8; 1]` + | | help: convert the array to a `&mut [u8]` slice instead: `&mut x[..]` + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&mut [u8] as Write> +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` + +error[E0277]: the trait bound `&mut [u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-4.rs:19:17 + | +LL | wants_write(&mut x); + | ----------- ^^^^^^ + | | | + | | the trait `Write` is not implemented for `&mut [u8; 1]` + | | help: convert the array to a `&mut [u8]` slice instead: `&mut x[..]` + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&mut [u8] as Write> +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` + +error[E0277]: the trait bound `&mut [u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-4.rs:27:17 + | +LL | wants_write(x); + | ----------- ^ + | | | + | | the trait `Write` is not implemented for `&mut [u8; 1]` + | | help: convert the array to a `&mut [u8]` slice instead: `&mut x[..]` + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&mut [u8] as Write> +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` + +error[E0277]: the trait bound `[u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-4.rs:32:17 + | +LL | wants_write(*x); + | ----------- ^^ + | | | + | | the trait `Write` is not implemented for `[u8; 1]` + | | help: convert the array to a `&mut [u8]` slice instead: `&mut *x[..]` + | required by a bound introduced by this call + | + = help: the following implementations were found: + <&mut [u8] as Write> +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`.