Point at fields that make the type recursive

On recursive types of infinite size, point at all the fields that make
the type recursive.

```rust
struct Foo {
    bar: Bar,
}

struct Bar {
    foo: Foo,
}
```

outputs

```
error[E0072]: recursive type `Foo` has infinite size
 --> file.rs:1:1
1 |   struct Foo {
  |  _^ starting here...
2 | |     bar: Bar,
  | |     -------- recursive here
3 | | }
  | |_^ ...ending here: recursive type has infinite size
  |
  = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Foo` representable

error[E0072]: recursive type `Bar` has infinite size
 --> file.rs:5:1
  |
5 |   struct Bar {
  |  _^ starting here...
6 | |     foo: Foo,
  | |     -------- recursive here
7 | | }
  | |_^ ...ending here: recursive type has infinite size
  |
  = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Bar` representable
```
This commit is contained in:
Esteban Küber 2017-03-27 09:35:27 -07:00
parent 96e2c34286
commit ed6ad0952f
4 changed files with 204 additions and 1 deletions

View File

@ -1299,6 +1299,20 @@ 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 {

View File

@ -27,7 +27,7 @@ use errors::DiagnosticBuilder;
use fmt_macros::{Parser, Piece, Position};
use hir::{self, intravisit, Local, Pat, Body};
use hir::intravisit::{Visitor, NestedVisitorMap};
use hir::map::NodeExpr;
use hir::map::{Node, NodeExpr};
use hir::def_id::DefId;
use infer::{self, InferCtxt};
use infer::type_variable::TypeVariableOrigin;
@ -779,6 +779,18 @@ 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");
}
true
} else {
false
}
}
pub fn recursive_type_with_infinite_size_error(self,
type_def_id: DefId)
-> DiagnosticBuilder<'tcx>
@ -793,9 +805,119 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
err.help(&format!("insert indirection (e.g., a `Box`, `Rc`, or `&`) \
at some point to make `{}` representable",
self.item_path_str(type_def_id)));
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");
}
}
}
_ => (),
}
}
}
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;
}
}
}
_ => (),
}
}
}
_ => (),
}
}
err
}
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;
}
}
}
}
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
}
pub fn report_object_safety_error(self,
span: Span,
trait_def_id: DefId,

View File

@ -0,0 +1,28 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::rc::Rc;
struct Foo<'a> {
bar: Bar<'a>,
b: Rc<Bar<'a>>,
}
struct Bar<'a> {
y: (Foo<'a>, Foo<'a>),
z: Option<Bar<'a>>,
a: &'a Foo<'a>,
c: &'a [Bar<'a>],
d: [Bar<'a>; 1],
e: Foo<'a>,
x: Bar<'a>,
}
fn main() {}

View File

@ -0,0 +1,39 @@
error[E0072]: recursive type `Foo` has infinite size
--> $DIR/recursive-type-field.rs:13:1
|
13 | struct Foo<'a> {
| _^ starting here...
14 | | bar: Bar<'a>,
| | ------------ recursive here
15 | | b: Rc<Bar<'a>>,
| | -------------- recursive here
16 | | }
| |_^ ...ending here: recursive type has infinite size
|
= help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Foo` representable
error[E0072]: recursive type `Bar` has infinite size
--> $DIR/recursive-type-field.rs:18:1
|
18 | struct Bar<'a> {
| _^ starting here...
19 | | y: (Foo<'a>, Foo<'a>),
| | --------------------- recursive here
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
24 | | e: Foo<'a>,
| | ---------- recursive here
25 | | x: Bar<'a>,
| | ---------- recursive here
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