diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index bed2044c70855..e7d63c138f397 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -2741,14 +2741,8 @@ impl Node<'_> { pub fn generics(&self) -> Option<&Generics<'_>> { match self { Node::TraitItem(TraitItem { generics, .. }) - | Node::ImplItem(ImplItem { generics, .. }) - | Node::Item(Item { - kind: - ItemKind::Trait(_, _, generics, ..) - | ItemKind::Impl { generics, .. } - | ItemKind::Fn(_, generics, _), - .. - }) => Some(generics), + | Node::ImplItem(ImplItem { generics, .. }) => Some(generics), + Node::Item(item) => item.kind.generics(), _ => None, } } diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs index e5a6c9a2e391a..060877f80adef 100644 --- a/src/librustc_trait_selection/traits/error_reporting/mod.rs +++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs @@ -15,6 +15,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::intravisit::Visitor; use rustc_hir::Node; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::error::ExpectedFound; @@ -25,7 +26,7 @@ use rustc_middle::ty::{ TypeFoldable, WithConstness, }; use rustc_session::DiagnosticMessageId; -use rustc_span::{ExpnKind, Span, DUMMY_SP}; +use rustc_span::{ExpnKind, MultiSpan, Span, DUMMY_SP}; use std::fmt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -1695,36 +1696,95 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'tcx>, obligation: &PredicateObligation<'tcx>, ) { - if let ( - ty::PredicateKind::Trait(pred, _), - ObligationCauseCode::BindingObligation(item_def_id, span), - ) = (obligation.predicate.kind(), &obligation.cause.code) - { - if let (Some(generics), true) = ( - self.tcx.hir().get_if_local(*item_def_id).as_ref().and_then(|n| n.generics()), - Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), - ) { - for param in generics.params { - if param.span == *span - && !param.bounds.iter().any(|bound| { - bound.trait_ref().and_then(|trait_ref| trait_ref.trait_def_id()) - == self.tcx.lang_items().sized_trait() - }) - { - let (span, separator) = match param.bounds { - [] => (span.shrink_to_hi(), ":"), - [.., bound] => (bound.span().shrink_to_hi(), " +"), - }; - err.span_suggestion_verbose( - span, - "consider relaxing the implicit `Sized` restriction", - format!("{} ?Sized", separator), - Applicability::MachineApplicable, + let (pred, item_def_id, span) = + match (obligation.predicate.kind(), &obligation.cause.code.peel_derives()) { + ( + ty::PredicateKind::Trait(pred, _), + ObligationCauseCode::BindingObligation(item_def_id, span), + ) => (pred, item_def_id, span), + _ => return, + }; + + let node = match ( + self.tcx.hir().get_if_local(*item_def_id), + Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), + ) { + (Some(node), true) => node, + _ => return, + }; + let generics = match node.generics() { + Some(generics) => generics, + None => return, + }; + for param in generics.params { + if param.span != *span + || param.bounds.iter().any(|bound| { + bound.trait_ref().and_then(|trait_ref| trait_ref.trait_def_id()) + == self.tcx.lang_items().sized_trait() + }) + { + continue; + } + match node { + hir::Node::Item( + item + @ + hir::Item { + kind: + hir::ItemKind::Enum(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Union(..), + .. + }, + ) => { + // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a + // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S(T);` + // is not. + let mut visitor = FindTypeParam { + param: param.name.ident().name, + invalid_spans: vec![], + nested: false, + }; + visitor.visit_item(item); + if !visitor.invalid_spans.is_empty() { + let mut multispan: MultiSpan = param.span.into(); + multispan.push_span_label( + param.span, + format!("this could be changed to `{}: ?Sized`...", param.name.ident()), + ); + for sp in visitor.invalid_spans { + multispan.push_span_label( + sp, + format!( + "...if indirection was used here: `Box<{}>`", + param.name.ident(), + ), + ); + } + err.span_help( + multispan, + &format!( + "you could relax the implicit `Sized` bound on `{T}` if it were \ + used through indirection like `&{T}` or `Box<{T}>`", + T = param.name.ident(), + ), ); return; } } + _ => {} } + let (span, separator) = match param.bounds { + [] => (span.shrink_to_hi(), ":"), + [.., bound] => (bound.span().shrink_to_hi(), " +"), + }; + err.span_suggestion_verbose( + span, + "consider relaxing the implicit `Sized` restriction", + format!("{} ?Sized", separator), + Applicability::MachineApplicable, + ); + return; } } @@ -1744,6 +1804,50 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { } } +/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting +/// `param: ?Sized` would be a valid constraint. +struct FindTypeParam { + param: rustc_span::Symbol, + invalid_spans: Vec, + nested: bool, +} + +impl<'v> Visitor<'v> for FindTypeParam { + type Map = rustc_hir::intravisit::ErasedMap<'v>; + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_ty(&mut self, ty: &hir::Ty<'_>) { + // We collect the spans of all uses of the "bare" type param, like in `field: T` or + // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be + // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized` + // obligations like `Box` and `Vec`, but we perform no extra analysis for those cases + // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors + // in that case should make what happened clear enough. + match ty.kind { + hir::TyKind::Ptr(_) | hir::TyKind::Rptr(..) | hir::TyKind::TraitObject(..) => {} + hir::TyKind::Path(hir::QPath::Resolved(None, path)) + if path.segments.len() == 1 && path.segments[0].ident.name == self.param => + { + if !self.nested { + self.invalid_spans.push(ty.span); + } + } + hir::TyKind::Path(_) => { + let prev = self.nested; + self.nested = true; + hir::intravisit::walk_ty(self, ty); + self.nested = prev; + } + _ => { + hir::intravisit::walk_ty(self, ty); + } + } + } +} + pub fn recursive_type_with_infinite_size_error( tcx: TyCtxt<'tcx>, type_def_id: DefId, diff --git a/src/test/ui/dst/dst-sized-trait-param.stderr b/src/test/ui/dst/dst-sized-trait-param.stderr index 749d569b9aedc..006a334021b14 100644 --- a/src/test/ui/dst/dst-sized-trait-param.stderr +++ b/src/test/ui/dst/dst-sized-trait-param.stderr @@ -9,6 +9,10 @@ LL | impl Foo<[isize]> for usize { } | = help: the trait `std::marker::Sized` is not implemented for `[isize]` = note: to learn more, visit +help: consider relaxing the implicit `Sized` restriction + | +LL | trait Foo : Sized { fn take(self, x: &T) { } } // Note: T is sized + | ^^^^^^^^ error[E0277]: the size for values of type `[usize]` cannot be known at compilation time --> $DIR/dst-sized-trait-param.rs:10:6 diff --git a/src/test/ui/extern/extern-types-unsized.stderr b/src/test/ui/extern/extern-types-unsized.stderr index 9ed52511fa399..0c7995fde3273 100644 --- a/src/test/ui/extern/extern-types-unsized.stderr +++ b/src/test/ui/extern/extern-types-unsized.stderr @@ -26,6 +26,10 @@ LL | assert_sized::(); = help: within `Foo`, the trait `std::marker::Sized` is not implemented for `A` = note: to learn more, visit = note: required because it appears within the type `Foo` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn assert_sized() { } + | ^^^^^^^^ error[E0277]: the size for values of type `A` cannot be known at compilation time --> $DIR/extern-types-unsized.rs:28:5 @@ -39,6 +43,10 @@ LL | assert_sized::>(); = help: within `Bar`, the trait `std::marker::Sized` is not implemented for `A` = note: to learn more, visit = note: required because it appears within the type `Bar` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn assert_sized() { } + | ^^^^^^^^ error[E0277]: the size for values of type `A` cannot be known at compilation time --> $DIR/extern-types-unsized.rs:31:5 @@ -53,6 +61,10 @@ LL | assert_sized::>>(); = note: to learn more, visit = note: required because it appears within the type `Bar` = note: required because it appears within the type `Bar>` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn assert_sized() { } + | ^^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/ui/issues/issue-10412.stderr b/src/test/ui/issues/issue-10412.stderr index 888576c43365f..d7a4bf4f21f18 100644 --- a/src/test/ui/issues/issue-10412.stderr +++ b/src/test/ui/issues/issue-10412.stderr @@ -57,6 +57,10 @@ LL | impl<'self> Serializable for &'self str { | = help: the trait `std::marker::Sized` is not implemented for `str` = note: to learn more, visit +help: consider relaxing the implicit `Sized` restriction + | +LL | trait Serializable<'self, T: ?Sized> { + | ^^^^^^^^ error: aborting due to 9 previous errors diff --git a/src/test/ui/issues/issue-18919.stderr b/src/test/ui/issues/issue-18919.stderr index 1ea40d0728ed1..383cdd4979ad9 100644 --- a/src/test/ui/issues/issue-18919.stderr +++ b/src/test/ui/issues/issue-18919.stderr @@ -9,6 +9,13 @@ LL | enum Option { | = help: the trait `std::marker::Sized` is not implemented for `dyn for<'r> std::ops::Fn(&'r isize) -> isize` = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/issue-18919.rs:7:13 + | +LL | enum Option { + | ^ this could be changed to `T: ?Sized`... +LL | Some(T), + | - ...if indirection was used here: `Box` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-23281.stderr b/src/test/ui/issues/issue-23281.stderr index 3b4b8997a7009..cffa52361696c 100644 --- a/src/test/ui/issues/issue-23281.stderr +++ b/src/test/ui/issues/issue-23281.stderr @@ -9,6 +9,13 @@ LL | struct Vec { | = help: the trait `std::marker::Sized` is not implemented for `(dyn std::ops::Fn() + 'static)` = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/issue-23281.rs:8:12 + | +LL | struct Vec { + | ^ this could be changed to `T: ?Sized`... +LL | t: T, + | - ...if indirection was used here: `Box` error: aborting due to previous error diff --git a/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.rs b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.rs new file mode 100644 index 0000000000000..ef64d799b65cf --- /dev/null +++ b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.rs @@ -0,0 +1,28 @@ +trait Trait { + fn func1() -> Struct1; //~ ERROR E0277 + fn func2<'a>() -> Struct2<'a, Self>; //~ ERROR E0277 + fn func3() -> Struct3; //~ ERROR E0277 + fn func4() -> Struct4; //~ ERROR E0277 +} + +struct Struct1{ + _t: std::marker::PhantomData<*const T>, +} +struct Struct2<'a, T>{ + _t: &'a T, +} +struct Struct3{ + _t: T, +} + +struct X(T); + +struct Struct4{ + _t: X, +} + +struct Struct5{ + _t: X, //~ ERROR E0277 +} + +fn main() {} diff --git a/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr new file mode 100644 index 0000000000000..ee08f51f80270 --- /dev/null +++ b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr @@ -0,0 +1,107 @@ +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/adt-param-with-implicit-sized-bound.rs:25:5 + | +LL | struct X(T); + | - required by this bound in `X` +... +LL | struct Struct5{ + | - this type parameter needs to be `std::marker::Sized` +LL | _t: X, + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `T` + = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/adt-param-with-implicit-sized-bound.rs:18:10 + | +LL | struct X(T); + | ^ - ...if indirection was used here: `Box` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/adt-param-with-implicit-sized-bound.rs:2:19 + | +LL | fn func1() -> Struct1; + | ^^^^^^^^^^^^^ doesn't have a size known at compile-time +... +LL | struct Struct1{ + | - required by this bound in `Struct1` + | + = help: the trait `std::marker::Sized` is not implemented for `Self` + = note: to learn more, visit +help: consider further restricting `Self` + | +LL | fn func1() -> Struct1 where Self: std::marker::Sized; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider relaxing the implicit `Sized` restriction + | +LL | struct Struct1{ + | ^^^^^^^^ + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/adt-param-with-implicit-sized-bound.rs:3:23 + | +LL | fn func2<'a>() -> Struct2<'a, Self>; + | ^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time +... +LL | struct Struct2<'a, T>{ + | - required by this bound in `Struct2` + | + = help: the trait `std::marker::Sized` is not implemented for `Self` + = note: to learn more, visit +help: consider further restricting `Self` + | +LL | fn func2<'a>() -> Struct2<'a, Self> where Self: std::marker::Sized; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider relaxing the implicit `Sized` restriction + | +LL | struct Struct2<'a, T: ?Sized>{ + | ^^^^^^^^ + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/adt-param-with-implicit-sized-bound.rs:4:19 + | +LL | fn func3() -> Struct3; + | ^^^^^^^^^^^^^ doesn't have a size known at compile-time +... +LL | struct Struct3{ + | - required by this bound in `Struct3` + | + = help: the trait `std::marker::Sized` is not implemented for `Self` + = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/adt-param-with-implicit-sized-bound.rs:14:16 + | +LL | struct Struct3{ + | ^ this could be changed to `T: ?Sized`... +LL | _t: T, + | - ...if indirection was used here: `Box` +help: consider further restricting `Self` + | +LL | fn func3() -> Struct3 where Self: std::marker::Sized; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/adt-param-with-implicit-sized-bound.rs:5:19 + | +LL | fn func4() -> Struct4; + | ^^^^^^^^^^^^^ doesn't have a size known at compile-time +... +LL | struct Struct4{ + | - required by this bound in `Struct4` + | + = help: the trait `std::marker::Sized` is not implemented for `Self` + = note: to learn more, visit +help: consider further restricting `Self` + | +LL | fn func4() -> Struct4 where Self: std::marker::Sized; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider relaxing the implicit `Sized` restriction + | +LL | struct Struct4{ + | ^^^^^^^^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unsized/unsized-enum.stderr b/src/test/ui/unsized/unsized-enum.stderr index f43d00f97398d..1908aee25ea7b 100644 --- a/src/test/ui/unsized/unsized-enum.stderr +++ b/src/test/ui/unsized/unsized-enum.stderr @@ -11,6 +11,13 @@ LL | fn foo2() { not_sized::>() } | = help: the trait `std::marker::Sized` is not implemented for `T` = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `U` if it were used through indirection like `&U` or `Box` + --> $DIR/unsized-enum.rs:4:10 + | +LL | enum Foo { FooSome(U), FooNone } + | ^ - ...if indirection was used here: `Box` + | | + | this could be changed to `U: ?Sized`... error: aborting due to previous error diff --git a/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr b/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr index 808c9c583d458..e0f077d66f99c 100644 --- a/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr +++ b/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr @@ -11,6 +11,13 @@ LL | impl S5 { | = help: the trait `std::marker::Sized` is not implemented for `X` = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `Y` if it were used through indirection like `&Y` or `Box` + --> $DIR/unsized-inherent-impl-self-type.rs:5:11 + | +LL | struct S5(Y); + | ^ - ...if indirection was used here: `Box` + | | + | this could be changed to `Y: ?Sized`... error: aborting due to previous error diff --git a/src/test/ui/unsized/unsized-struct.stderr b/src/test/ui/unsized/unsized-struct.stderr index 42fc5569eceb4..d92d1d9113e5c 100644 --- a/src/test/ui/unsized/unsized-struct.stderr +++ b/src/test/ui/unsized/unsized-struct.stderr @@ -11,6 +11,13 @@ LL | fn foo2() { not_sized::>() } | = help: the trait `std::marker::Sized` is not implemented for `T` = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/unsized-struct.rs:4:12 + | +LL | struct Foo { data: T } + | ^ - ...if indirection was used here: `Box` + | | + | this could be changed to `T: ?Sized`... error[E0277]: the size for values of type `T` cannot be known at compilation time --> $DIR/unsized-struct.rs:13:24 diff --git a/src/test/ui/unsized/unsized-trait-impl-self-type.stderr b/src/test/ui/unsized/unsized-trait-impl-self-type.stderr index c2b2fe40ce67f..73c5439da53b6 100644 --- a/src/test/ui/unsized/unsized-trait-impl-self-type.stderr +++ b/src/test/ui/unsized/unsized-trait-impl-self-type.stderr @@ -11,6 +11,13 @@ LL | impl T3 for S5 { | = help: the trait `std::marker::Sized` is not implemented for `X` = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `Y` if it were used through indirection like `&Y` or `Box` + --> $DIR/unsized-trait-impl-self-type.rs:8:11 + | +LL | struct S5(Y); + | ^ - ...if indirection was used here: `Box` + | | + | this could be changed to `Y: ?Sized`... error: aborting due to previous error diff --git a/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr b/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr index 4cf054d177f66..e423a9bdeab6f 100644 --- a/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr +++ b/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr @@ -11,6 +11,10 @@ LL | impl T2 for S4 { | = help: the trait `std::marker::Sized` is not implemented for `X` = note: to learn more, visit +help: consider relaxing the implicit `Sized` restriction + | +LL | trait T2 { + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/unsized3.stderr b/src/test/ui/unsized3.stderr index 828e8bc9f4aa1..e0a0389dc4690 100644 --- a/src/test/ui/unsized3.stderr +++ b/src/test/ui/unsized3.stderr @@ -48,6 +48,10 @@ LL | f5(x1); = help: within `S`, the trait `std::marker::Sized` is not implemented for `X` = note: to learn more, visit = note: required because it appears within the type `S` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn f5(x: &Y) {} + | ^^^^^^^^ error[E0277]: the size for values of type `X` cannot be known at compilation time --> $DIR/unsized3.rs:40:8 @@ -91,6 +95,10 @@ LL | f5(&(32, *x1)); = note: to learn more, visit = note: required because it appears within the type `S` = note: required because it appears within the type `({integer}, S)` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn f5(x: &Y) {} + | ^^^^^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/unsized7.stderr b/src/test/ui/unsized7.stderr index d18644f005a88..e616a5cf0f9c2 100644 --- a/src/test/ui/unsized7.stderr +++ b/src/test/ui/unsized7.stderr @@ -11,6 +11,10 @@ LL | impl T1 for S3 { | = help: the trait `std::marker::Sized` is not implemented for `X` = note: to learn more, visit +help: consider relaxing the implicit `Sized` restriction + | +LL | trait T1 { + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-fn-where-clause.stderr b/src/test/ui/wf/wf-fn-where-clause.stderr index 59a4f9bad9dfa..731d31ac34f62 100644 --- a/src/test/ui/wf/wf-fn-where-clause.stderr +++ b/src/test/ui/wf/wf-fn-where-clause.stderr @@ -23,6 +23,13 @@ LL | struct Vec { | = help: the trait `std::marker::Sized` is not implemented for `(dyn std::marker::Copy + 'static)` = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/wf-fn-where-clause.rs:16:12 + | +LL | struct Vec { + | ^ this could be changed to `T: ?Sized`... +LL | t: T, + | - ...if indirection was used here: `Box` error[E0038]: the trait `std::marker::Copy` cannot be made into an object --> $DIR/wf-fn-where-clause.rs:12:16