diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 87921c80502e0..e3ca52281de62 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -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), } 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>, - iter: It) - -> Representability - where It: Iterator> { - iter.fold(Representability::Representable, - |r, ty| cmp::max(r, is_type_structurally_recursive(tcx, sp, seen, ty))) + fn fold_repr>(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: 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]); } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9185b6ec7b1dd..c07bd991b3e7b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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 => (), diff --git a/src/test/compile-fail/issue-3008-1.rs b/src/test/compile-fail/issue-3008-1.rs index d3c15763eb00e..7ca6d9301a689 100644 --- a/src/test/compile-fail/issue-3008-1.rs +++ b/src/test/compile-fail/issue-3008-1.rs @@ -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() { } diff --git a/src/test/compile-fail/issue-3008-2.rs b/src/test/compile-fail/issue-3008-2.rs index 3bc8413cbca73..061d1facda0ca 100644 --- a/src/test/compile-fail/issue-3008-2.rs +++ b/src/test/compile-fail/issue-3008-2.rs @@ -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() { } diff --git a/src/test/compile-fail/issue-32326.rs b/src/test/compile-fail/issue-32326.rs index afffe2a2c8d03..70a7cd8b97021 100644 --- a/src/test/compile-fail/issue-32326.rs +++ b/src/test/compile-fail/issue-32326.rs @@ -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), } diff --git a/src/test/compile-fail/issue-3779.rs b/src/test/compile-fail/issue-3779.rs index 71e9325ab75d1..10f73dc086288 100644 --- a/src/test/compile-fail/issue-3779.rs +++ b/src/test/compile-fail/issue-3779.rs @@ -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 + //~^ NOTE recursive without indirection } fn main() { diff --git a/src/test/compile-fail/type-recursive.rs b/src/test/compile-fail/type-recursive.rs index 7b56c6c15ebb3..5dd76ce32c7a4 100644 --- a/src/test/compile-fail/type-recursive.rs +++ b/src/test/compile-fail/type-recursive.rs @@ -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() { } diff --git a/src/test/ui/span/E0072.stderr b/src/test/ui/span/E0072.stderr index 5204390ef9d2a..1f6dd6b1d165f 100644 --- a/src/test/ui/span/E0072.stderr +++ b/src/test/ui/span/E0072.stderr @@ -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, + | ---------------------- recursive without indirection | = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `ListNode` representable diff --git a/src/test/ui/span/multiline-span-E0072.stderr b/src/test/ui/span/multiline-span-E0072.stderr index 9c6816e736313..a06cbd04deb4f 100644 --- a/src/test/ui/span/multiline-span-E0072.stderr +++ b/src/test/ui/span/multiline-span-E0072.stderr @@ -6,6 +6,7 @@ error[E0072]: recursive type `ListNode` has infinite size 14 | | { 15 | | head: u8, 16 | | tail: Option, + | | ---------------------- recursive without indirection 17 | | } | |_^ recursive type has infinite size | 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 0000000000000..6fef4d30f7a77 --- /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 0000000000000..b4d0b5a6a25d0 --- /dev/null +++ b/src/test/ui/span/recursive-type-field.stderr @@ -0,0 +1,31 @@ +error[E0072]: recursive type `Foo` has infinite size + --> $DIR/recursive-type-field.rs:13:1 + | +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> { + | ^^^^^^^^^^^^^^ recursive type has infinite size +19 | y: (Foo<'a>, Foo<'a>), + | --------------------- recursive without indirection +20 | z: Option>, + | ------------------ 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 + +error: aborting due to 2 previous errors +