Only point at the fields that cause infinite size

* clean up code
* point only fields that cause the type to be of infinite size
* fix unittests
This commit is contained in:
Esteban Küber 2017-03-27 14:21:27 -07:00
parent ed6ad0952f
commit f7108e18cd
6 changed files with 112 additions and 126 deletions

View file

@ -1299,20 +1299,6 @@ impl fmt::Debug for Ty {
}
}
impl Ty {
pub fn ty_def_id(&self) -> Option<DefId> {
match self.node {
TyPath(QPath::Resolved(_, ref path)) => {
match path.def {
Def::Struct(did) | Def::Enum(did) => Some(did),
_ => None,
}
},
_ => None,
}
}
}
/// Not represented directly in the AST, referred to by name through a ty_path.
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
pub enum PrimTy {
@ -1753,6 +1739,24 @@ impl Item_ {
ItemDefaultImpl(..) => "item",
}
}
pub fn fields(&self) -> &[StructField] {
match *self {
ItemStruct(ref vd, _) => {
return vd.fields();
}
ItemEnum(EnumDef { ref variants }, _) => {
for variant in variants {
let fields = variant.node.data.fields();
if fields.len() > 0 {
return fields;
}
}
}
_ => (),
}
&[]
}
}
/// A reference from an trait to one of its associated items. This

View file

@ -778,19 +778,53 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
fn foo(&self, id: ast::NodeId, ty: &hir::Ty, sp: Span, err: &mut DiagnosticBuilder<'tcx>) -> bool {
if let Some(Node::NodeItem(item)) = ty.ty_def_id().and_then(|id| {
self.hir.get_if_local(id)
}) {
if self.is_node_id_referenced_in_item(item, id) {
err.span_label(sp, &"recursive here");
/// Get the `DefId` for a given struct or enum `Ty`.
fn ty_def_id(ty: &hir::Ty) -> Option<DefId> {
match ty.node {
hir::TyPath(hir::QPath::Resolved(_, ref path)) => {
match path.def {
hir::def::Def::Struct(did) | hir::def::Def::Enum(did) => {
Some(did)
}
_ => None,
}
true
} else {
false
}
},
_ => None,
}
}
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
/// Add a span label to `err` pointing at `sp` if the field represented by `node_id` points
/// recursively at the type `ty` without indirection.
fn annotate_recursive_field_ty(&self,
node_id: ast::NodeId,
ty: &hir::Ty,
sp: Span,
err: &mut DiagnosticBuilder<'tcx>) -> bool {
if let Some(did) = ty_def_id(ty) {
return self.annotate_recursive_field_id(node_id, did, sp, err);
}
false
}
/// Add a span label to `err` pointing at `sp` if the field represented by `node_id` points
/// recursively at the type represented by `did` without indirection.
fn annotate_recursive_field_id(&self,
node_id:
ast::NodeId,
did: DefId,
sp: Span,
err: &mut DiagnosticBuilder<'tcx>) -> bool
{
if let Some(Node::NodeItem(item)) = self.hir.get_if_local(did) {
if self.is_node_id_referenced_in_item(item, node_id) {
err.span_label(sp, &"recursive without indirection");
return true;
}
}
false
}
pub fn recursive_type_with_infinite_size_error(self,
type_def_id: DefId)
-> DiagnosticBuilder<'tcx>
@ -806,114 +840,62 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
at some point to make `{}` representable",
self.item_path_str(type_def_id)));
// Look at the type for the the recursive type's fields and label those that are causing it
// to be of infinite size.
if let Some(Node::NodeItem(self_item)) = self.hir.get_if_local(type_def_id) {
match self_item.node {
hir::ItemStruct(hir::VariantData::Struct(ref fields, _), _) |
hir::ItemStruct(hir::VariantData::Tuple(ref fields, _), _) => {
for field in fields {
match field.ty.node {
hir::TyPath(ref qpath) => {
// Foo | Option<Foo>
if let &hir::QPath::Resolved(_, ref path) = qpath {
for segment in path.segments.iter() {
if let hir::AngleBracketedParameters(
hir::AngleBracketedParameterData {
ref types, ..
}) = segment.parameters
{
for ty in types {
if self.foo(self_item.id, &ty, field.span,
&mut err) {
break;
}
}
}
}
match path.def {
hir::def::Def::Struct(did) | hir::def::Def::Enum(did) => {
let local = self.hir.get_if_local(did);
if let Some(Node::NodeItem(item)) = local {
if self.is_node_id_referenced_in_item(item,
self_item.id)
{
err.span_label(field.span, &"recursive here");
}
}
}
_ => (),
}
for field in self_item.node.fields() {
match field.ty.node {
hir::TyPath(ref qpath) => {
// Foo
if let &hir::QPath::Resolved(_, ref path) = qpath {
match path.def {
hir::def::Def::Struct(did) |
hir::def::Def::Enum(did) => {
self.annotate_recursive_field_id(self_item.id,
did,
field.span,
&mut err);
}
_ => (),
}
hir::TySlice(ref ty) |
hir::TyArray(ref ty, _) |
hir::TyPtr(hir::MutTy { ref ty, .. }) |
hir::TyRptr(_, hir::MutTy { ref ty, .. }) => {
// &[Foo] | [Foo] | &'a [Foo]
if let hir::TySlice(ref ty) = ty.node {
// &'a [Foo]
let _ = self.foo(self_item.id, &ty, field.span, &mut err);
} else {
let _ = self.foo(self_item.id, &ty, field.span, &mut err);
}
}
hir::TyTup(ref tys) => {
// (Foo, Bar)
for ty in tys {
if self.foo(self_item.id, &ty, field.span, &mut err) {
break;
}
}
}
_ => (),
}
}
hir::TyArray(ref ty, _) => {
// [Foo]
self.annotate_recursive_field_ty(self_item.id, &ty, field.span, &mut err);
}
hir::TyTup(ref tys) => {
// (Foo, Bar)
for ty in tys {
if self.annotate_recursive_field_ty(self_item.id,
&ty,
field.span,
&mut err) {
break;
}
}
}
_ => (),
}
_ => (),
}
}
err
}
/// Given `item`, determine wether the node identified by `node_id` is referenced without any
/// indirection in any of `item`'s fields.
fn is_node_id_referenced_in_item(&self, item: &hir::Item, node_id: ast::NodeId) -> bool {
if item.id == node_id {
return true;
}
match item.node {
hir::ItemStruct(hir::VariantData::Struct(ref fields, _), _) |
hir::ItemStruct(hir::VariantData::Tuple(ref fields, _), _) => {
for field in fields {
if let Some(Node::NodeItem(ref item)) = field.ty.ty_def_id().and_then(|id| {
self.hir.get_if_local(id)
}) {
if self.is_node_id_referenced_in_item(item, node_id) {
return true;
}
}
for field in item.node.fields() {
if let Some(Node::NodeItem(ref item)) = ty_def_id(&field.ty).and_then(|id| {
self.hir.get_if_local(id)
}) {
if self.is_node_id_referenced_in_item(item, node_id) {
return true;
}
}
hir::ItemEnum(hir::EnumDef { ref variants }, _) => {
for variant in variants {
match variant.node.data {
hir::VariantData::Struct(ref fields, _) |
hir::VariantData::Tuple(ref fields, _) => {
for field in fields {
if let Some(Node::NodeItem(ref item)) = field.ty
.ty_def_id().and_then(|id| {
self.hir.get_if_local(id)
})
{
if self.is_node_id_referenced_in_item(item, node_id) {
return true;
}
}
}
}
_ => (),
}
}
}
_ => (),
}
false
}

View file

@ -12,6 +12,7 @@ enum foo { foo_(bar) }
struct bar { x: bar }
//~^ ERROR E0072
//~| NOTE recursive type has infinite size
//~| NOTE recursive without indirection
fn main() {
}

View file

@ -15,6 +15,8 @@
enum Expr { //~ ERROR E0072
//~| NOTE recursive type has infinite size
Plus(Expr, Expr),
//~^ NOTE recursive without indirection
//~| NOTE recursive without indirection
Literal(i64),
}

View file

@ -11,7 +11,7 @@
struct t1 { //~ ERROR E0072
//~| NOTE recursive type has infinite size
foo: isize,
foolish: t1
foolish: t1 //~ NOTE recursive without indirection
}
fn main() { }

View file

@ -4,9 +4,8 @@ error[E0072]: recursive type `Foo` has infinite size
13 | struct Foo<'a> {
| _^ starting here...
14 | | bar: Bar<'a>,
| | ------------ recursive here
| | ------------ recursive without indirection
15 | | b: Rc<Bar<'a>>,
| | -------------- recursive here
16 | | }
| |_^ ...ending here: recursive type has infinite size
|
@ -18,22 +17,20 @@ error[E0072]: recursive type `Bar` has infinite size
18 | struct Bar<'a> {
| _^ starting here...
19 | | y: (Foo<'a>, Foo<'a>),
| | --------------------- recursive here
| | --------------------- recursive without indirection
20 | | z: Option<Bar<'a>>,
| | ------------------ recursive here
21 | | a: &'a Foo<'a>,
| | -------------- recursive here
22 | | c: &'a [Bar<'a>],
| | ---------------- recursive here
23 | | d: [Bar<'a>; 1],
| | --------------- recursive here
| | --------------- recursive without indirection
24 | | e: Foo<'a>,
| | ---------- recursive here
| | ---------- recursive without indirection
25 | | x: Bar<'a>,
| | ---------- recursive here
| | ---------- recursive without indirection
26 | | }
| |_^ ...ending here: recursive type has infinite size
|
= help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Bar` representable
error: aborting due to 2 previous errors