From ed6ad0952f980285c671277a9fd0559ab9d125d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 27 Mar 2017 09:35:27 -0700 Subject: [PATCH] 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 ``` --- src/librustc/hir/mod.rs | 14 +++ src/librustc/traits/error_reporting.rs | 124 ++++++++++++++++++- src/test/ui/span/recursive-type-field.rs | 28 +++++ src/test/ui/span/recursive-type-field.stderr | 39 ++++++ 4 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/span/recursive-type-field.rs create mode 100644 src/test/ui/span/recursive-type-field.stderr diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index cb7f530b995..333b64b08d7 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1299,6 +1299,20 @@ impl fmt::Debug for Ty { } } +impl Ty { + pub fn ty_def_id(&self) -> Option { + 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 { diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index e846d74febf..c462a56e562 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -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 + 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, diff --git a/src/test/ui/span/recursive-type-field.rs b/src/test/ui/span/recursive-type-field.rs new file mode 100644 index 00000000000..6fef4d30f7a --- /dev/null +++ b/src/test/ui/span/recursive-type-field.rs @@ -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 or the MIT license +// , 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>, +} + +struct Bar<'a> { + y: (Foo<'a>, Foo<'a>), + z: Option>, + a: &'a Foo<'a>, + c: &'a [Bar<'a>], + d: [Bar<'a>; 1], + e: Foo<'a>, + x: Bar<'a>, +} + +fn main() {} diff --git a/src/test/ui/span/recursive-type-field.stderr b/src/test/ui/span/recursive-type-field.stderr new file mode 100644 index 00000000000..69d88f3a277 --- /dev/null +++ b/src/test/ui/span/recursive-type-field.stderr @@ -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>, + | | -------------- 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>, + | | ------------------ 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