Move logic to is_representable instead of climbing HIR

This commit is contained in:
Esteban Küber 2017-04-15 16:18:09 -07:00
parent f7108e18cd
commit a4fc925192
9 changed files with 74 additions and 170 deletions

View file

@ -1739,24 +1739,6 @@ 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

@ -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::{Node, NodeExpr};
use hir::map::NodeExpr;
use hir::def_id::DefId;
use infer::{self, InferCtxt};
use infer::type_variable::TypeVariableOrigin;
@ -778,53 +778,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}
/// 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,
}
},
_ => 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>
@ -839,67 +793,9 @@ 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)));
// 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) {
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::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;
}
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;
}
}
}
false
}
pub fn report_object_safety_error(self,
span: Span,
trait_def_id: DefId,

View file

@ -145,11 +145,11 @@ pub enum CopyImplementationError<'tcx> {
///
/// The ordering of the cases is significant. They are sorted so that cmp::max
/// will keep the "more erroneous" of two values.
#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
pub enum Representability {
Representable,
ContainsRecursive,
SelfRecursive,
SelfRecursive(Vec<Span>),
}
impl<'tcx> ParameterEnvironment<'tcx> {
@ -1003,18 +1003,22 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
/// Check whether a type is representable. This means it cannot contain unboxed
/// structural recursion. This check is needed for structs and enums.
pub fn is_representable(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span)
pub fn is_representable(&'tcx self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
sp: Span)
-> Representability {
// Iterate until something non-representable is found
fn find_nonrepresentable<'a, 'tcx, It>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
sp: Span,
seen: &mut Vec<Ty<'tcx>>,
iter: It)
-> Representability
where It: Iterator<Item=Ty<'tcx>> {
iter.fold(Representability::Representable,
|r, ty| cmp::max(r, is_type_structurally_recursive(tcx, sp, seen, ty)))
fn fold_repr<It: Iterator<Item=Representability>>(iter: It) -> Representability {
iter.fold(Representability::Representable, |r1, r2| {
match (r1, r2) {
(Representability::SelfRecursive(v1),
Representability::SelfRecursive(v2)) => {
Representability::SelfRecursive(v1.iter().map(|s| *s).chain(v2).collect())
}
(r1, r2) => cmp::max(r1, r2)
}
})
}
fn are_inner_types_recursive<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span,
@ -1022,7 +1026,10 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
-> Representability {
match ty.sty {
TyTuple(ref ts, _) => {
find_nonrepresentable(tcx, sp, seen, ts.iter().cloned())
// Find non representable
fold_repr(ts.iter().map(|ty| {
is_type_structurally_recursive(tcx, sp, seen, ty)
}))
}
// Fixed-length vectors.
// FIXME(#11924) Behavior undecided for zero-length vectors.
@ -1030,10 +1037,17 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
is_type_structurally_recursive(tcx, sp, seen, ty)
}
TyAdt(def, substs) => {
find_nonrepresentable(tcx,
sp,
seen,
def.all_fields().map(|f| f.ty(tcx, substs)))
// Find non representable fields with their spans
fold_repr(def.all_fields().map(|field| {
let ty = field.ty(tcx, substs);
let span = tcx.hir.span_if_local(field.did).unwrap_or(sp);
match is_type_structurally_recursive(tcx, span, seen, ty) {
Representability::SelfRecursive(_) => {
Representability::SelfRecursive(vec![span])
}
x => x,
}
}))
}
TyClosure(..) => {
// this check is run on type definitions, so we don't expect
@ -1072,7 +1086,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
sp: Span,
seen: &mut Vec<Ty<'tcx>>,
ty: Ty<'tcx>) -> Representability {
debug!("is_type_structurally_recursive: {:?}", ty);
debug!("is_type_structurally_recursive: {:?} {:?}", ty, sp);
match ty.sty {
TyAdt(def, _) => {
@ -1093,7 +1107,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
debug!("SelfRecursive: {:?} contains {:?}",
seen_type,
ty);
return Representability::SelfRecursive;
return Representability::SelfRecursive(vec![sp]);
}
}

View file

@ -1378,8 +1378,12 @@ fn check_representable<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// contain themselves. For case 2, there must be an inner type that will be
// caught by case 1.
match rty.is_representable(tcx, sp) {
Representability::SelfRecursive => {
tcx.recursive_type_with_infinite_size_error(item_def_id).emit();
Representability::SelfRecursive(spans) => {
let mut err = tcx.recursive_type_with_infinite_size_error(item_def_id);
for span in spans {
err.span_label(span, &"recursive without indirection");
}
err.emit();
return false
}
Representability::Representable | Representability::ContainsRecursive => (),

View file

@ -8,9 +8,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
enum foo { foo_(bar) }
enum bar { bar_none, bar_some(bar) }
//~^ ERROR recursive type `bar` has infinite size
enum Foo {
Foo_(Bar)
}
enum Bar {
//~^ ERROR recursive type `Bar` has infinite size
//~| NOTE recursive type has infinite size
BarNone,
BarSome(Bar) //~ NOTE recursive without indirection
}
fn main() {
}

View file

@ -8,9 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct S { //~ ERROR E0072
//~| NOTE recursive type has infinite size
struct S {
//~^ ERROR E0072
//~| NOTE recursive type has infinite size
element: Option<S>
//~^ NOTE recursive without indirection
}
fn main() {

View file

@ -3,6 +3,9 @@ error[E0072]: recursive type `ListNode` has infinite size
|
11 | struct ListNode {
| ^^^^^^^^^^^^^^^ recursive type has infinite size
12 | head: u8,
13 | tail: Option<ListNode>,
| ---------------------- recursive without indirection
|
= help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `ListNode` representable

View file

@ -6,6 +6,7 @@ error[E0072]: recursive type `ListNode` has infinite size
14 | | {
15 | | head: u8,
16 | | tail: Option<ListNode>,
| | ---------------------- recursive without indirection
17 | | }
| |_^ recursive type has infinite size
|

View file

@ -1,34 +1,29 @@
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 without indirection
15 | | b: Rc<Bar<'a>>,
16 | | }
| |_^ ...ending here: recursive type has infinite size
13 | struct Foo<'a> {
| ^^^^^^^^^^^^^^ recursive type has infinite size
14 | bar: Bar<'a>,
| ------------ recursive without indirection
|
= 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 without indirection
20 | | z: Option<Bar<'a>>,
21 | | a: &'a Foo<'a>,
22 | | c: &'a [Bar<'a>],
23 | | d: [Bar<'a>; 1],
| | --------------- recursive without indirection
24 | | e: Foo<'a>,
| | ---------- recursive without indirection
25 | | x: Bar<'a>,
| | ---------- recursive without indirection
26 | | }
| |_^ ...ending here: recursive type has infinite size
18 | struct Bar<'a> {
| ^^^^^^^^^^^^^^ recursive type has infinite size
19 | y: (Foo<'a>, Foo<'a>),
| --------------------- recursive without indirection
20 | z: Option<Bar<'a>>,
| ------------------ recursive without indirection
...
23 | d: [Bar<'a>; 1],
| --------------- recursive without indirection
24 | e: Foo<'a>,
| ---------- recursive without indirection
25 | x: Bar<'a>,
| ---------- recursive without indirection
|
= help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Bar` representable