Suggest ?Sized when applicable for ADTs

Fix #71790.
This commit is contained in:
Esteban Küber 2020-06-11 17:41:16 -07:00
parent ff4a2533a0
commit 95e5605108
4 changed files with 166 additions and 34 deletions

View File

@ -2729,14 +2729,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,
}
}

View File

@ -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;
@ -1695,36 +1696,69 @@ 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)
let (pred, item_def_id, span) = match (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,
);
return;
(
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: ?Sized>(T);`
// is not.
let mut visitor = FindTypeParam { param: param.name.ident().name, valid: true };
visitor.visit_item(item);
if !visitor.valid {
continue;
}
}
_ => {}
}
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 +1778,34 @@ 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,
valid: bool,
}
impl<'v> Visitor<'v> for FindTypeParam {
type Map = rustc_hir::intravisit::ErasedMap<'v>;
fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
hir::intravisit::NestedVisitorMap::None
}
fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
match ty.kind {
hir::TyKind::Ptr(_) | hir::TyKind::Rptr(..) | hir::TyKind::TraitObject(..) => return,
hir::TyKind::Path(hir::QPath::Resolved(None, path))
if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
{
self.valid = false;
}
_ => {}
}
hir::intravisit::walk_ty(self, ty);
}
}
pub fn recursive_type_with_infinite_size_error(
tcx: TyCtxt<'tcx>,
type_def_id: DefId,

View File

@ -0,0 +1,17 @@
trait Trait {
fn func1() -> Struct1<Self>; //~ ERROR E0277
fn func2<'a>() -> Struct2<'a, Self>; //~ ERROR E0277
fn func3() -> Struct3<Self>; //~ ERROR E0277
}
struct Struct1<T>{
_t: std::marker::PhantomData<*const T>,
}
struct Struct2<'a, T>{
_t: &'a T,
}
struct Struct3<T>{
_t: T,
}
fn main() {}

View File

@ -0,0 +1,59 @@
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<Self>;
| ^^^^^^^^^^^^^ doesn't have a size known at compile-time
...
LL | struct Struct1<T>{
| - required by this bound in `Struct1`
|
= help: the trait `std::marker::Sized` is not implemented for `Self`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
help: consider further restricting `Self`
|
LL | fn func1() -> Struct1<Self> where Self: std::marker::Sized;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider relaxing the implicit `Sized` restriction
|
LL | struct Struct1<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: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 <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
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<Self>;
| ^^^^^^^^^^^^^ doesn't have a size known at compile-time
...
LL | struct Struct3<T>{
| - required by this bound in `Struct3`
|
= help: the trait `std::marker::Sized` is not implemented for `Self`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
help: consider further restricting `Self`
|
LL | fn func3() -> Struct3<Self> where Self: std::marker::Sized;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`.