Skip to content

Commit 53ddf2e

Browse files
committed
Fix several issues with the struct and enum representability-checking logic.
1 parent 63fe80e commit 53ddf2e

File tree

9 files changed

+240
-62
lines changed

9 files changed

+240
-62
lines changed

src/librustc/middle/ty.rs

+117-62
Original file line numberDiff line numberDiff line change
@@ -2823,99 +2823,151 @@ pub fn is_instantiable(cx: &ctxt, r_ty: t) -> bool {
28232823
/// distinguish between types that are recursive with themselves and types that
28242824
/// contain a different recursive type. These cases can therefore be treated
28252825
/// differently when reporting errors.
2826-
#[deriving(PartialEq)]
2826+
///
2827+
/// The ordering of the cases is significant. They are sorted so that cmp::max
2828+
/// will keep the "more erroneous" of two values.
2829+
#[deriving(PartialOrd, Ord, Eq, PartialEq, Show)]
28272830
pub enum Representability {
28282831
Representable,
2829-
SelfRecursive,
28302832
ContainsRecursive,
2833+
SelfRecursive,
28312834
}
28322835

28332836
/// Check whether a type is representable. This means it cannot contain unboxed
28342837
/// structural recursion. This check is needed for structs and enums.
28352838
pub fn is_type_representable(cx: &ctxt, sp: Span, ty: t) -> Representability {
28362839

28372840
// Iterate until something non-representable is found
2838-
fn find_nonrepresentable<It: Iterator<t>>(cx: &ctxt, sp: Span, seen: &mut Vec<DefId>,
2841+
fn find_nonrepresentable<It: Iterator<t>>(cx: &ctxt, sp: Span, seen: &mut Vec<t>,
28392842
mut iter: It) -> Representability {
2840-
for ty in iter {
2841-
let r = type_structurally_recursive(cx, sp, seen, ty);
2842-
if r != Representable {
2843-
return r
2844-
}
2845-
}
2846-
Representable
2843+
iter.fold(Representable,
2844+
|r, ty| cmp::max(r, is_type_structurally_recursive(cx, sp, seen, ty)))
28472845
}
28482846

2849-
// Does the type `ty` directly (without indirection through a pointer)
2850-
// contain any types on stack `seen`?
2851-
fn type_structurally_recursive(cx: &ctxt, sp: Span, seen: &mut Vec<DefId>,
2852-
ty: t) -> Representability {
2853-
debug!("type_structurally_recursive: {}",
2854-
::util::ppaux::ty_to_string(cx, ty));
2855-
2856-
// Compare current type to previously seen types
2857-
match get(ty).sty {
2858-
ty_struct(did, _) |
2859-
ty_enum(did, _) => {
2860-
for (i, &seen_did) in seen.iter().enumerate() {
2861-
if did == seen_did {
2862-
return if i == 0 { SelfRecursive }
2863-
else { ContainsRecursive }
2864-
}
2865-
}
2866-
}
2867-
_ => (),
2868-
}
2869-
2870-
// Check inner types
2847+
fn are_inner_types_recursive(cx: &ctxt, sp: Span,
2848+
seen: &mut Vec<t>, ty: t) -> Representability {
28712849
match get(ty).sty {
2872-
// Tuples
28732850
ty_tup(ref ts) => {
28742851
find_nonrepresentable(cx, sp, seen, ts.iter().map(|t| *t))
28752852
}
28762853
// Fixed-length vectors.
28772854
// FIXME(#11924) Behavior undecided for zero-length vectors.
28782855
ty_vec(ty, Some(_)) => {
2879-
type_structurally_recursive(cx, sp, seen, ty)
2856+
is_type_structurally_recursive(cx, sp, seen, ty)
28802857
}
2881-
2882-
// Push struct and enum def-ids onto `seen` before recursing.
28832858
ty_struct(did, ref substs) => {
2884-
seen.push(did);
28852859
let fields = struct_fields(cx, did, substs);
2886-
let r = find_nonrepresentable(cx, sp, seen,
2887-
fields.iter().map(|f| f.mt.ty));
2888-
seen.pop();
2889-
r
2860+
find_nonrepresentable(cx, sp, seen, fields.iter().map(|f| f.mt.ty))
28902861
}
2891-
28922862
ty_enum(did, ref substs) => {
2893-
seen.push(did);
28942863
let vs = enum_variants(cx, did);
2864+
let iter = vs.iter()
2865+
.flat_map(|variant| { variant.args.iter() })
2866+
.map(|aty| { aty.subst_spanned(cx, substs, Some(sp)) });
28952867

2896-
let mut r = Representable;
2897-
for variant in vs.iter() {
2898-
let iter = variant.args.iter().map(|aty| {
2899-
aty.subst_spanned(cx, substs, Some(sp))
2900-
});
2901-
r = find_nonrepresentable(cx, sp, seen, iter);
2868+
find_nonrepresentable(cx, sp, seen, iter)
2869+
}
2870+
ty_unboxed_closure(did, _) => {
2871+
let upvars = unboxed_closure_upvars(cx, did);
2872+
find_nonrepresentable(cx, sp, seen, upvars.iter().map(|f| f.ty))
2873+
}
2874+
_ => Representable,
2875+
}
2876+
}
29022877

2903-
if r != Representable { break }
2878+
fn same_struct_or_enum_def_id(ty: t, did: DefId) -> bool {
2879+
match get(ty).sty {
2880+
ty_struct(ty_did, _) | ty_enum(ty_did, _) => {
2881+
ty_did == did
2882+
}
2883+
_ => false
2884+
}
2885+
}
2886+
2887+
fn same_type(a: t, b: t) -> bool {
2888+
match (&get(a).sty, &get(b).sty) {
2889+
(&ty_struct(did_a, ref substs_a), &ty_struct(did_b, ref substs_b)) |
2890+
(&ty_enum(did_a, ref substs_a), &ty_enum(did_b, ref substs_b)) => {
2891+
if did_a != did_b {
2892+
return false;
29042893
}
29052894

2906-
seen.pop();
2907-
r
2908-
}
2895+
let types_a = substs_a.types.get_slice(subst::TypeSpace);
2896+
let types_b = substs_b.types.get_slice(subst::TypeSpace);
29092897

2910-
ty_unboxed_closure(did, _) => {
2911-
let upvars = unboxed_closure_upvars(cx, did);
2912-
find_nonrepresentable(cx,
2913-
sp,
2914-
seen,
2915-
upvars.iter().map(|f| f.ty))
2898+
let mut pairs = types_a.iter().zip(types_b.iter());
2899+
2900+
pairs.all(|(&a, &b)| same_type(a, b))
2901+
}
2902+
_ => {
2903+
type_id(a) == type_id(b)
29162904
}
2905+
}
2906+
}
29172907

2918-
_ => Representable,
2908+
// Does the type `ty` directly (without indirection through a pointer)
2909+
// contain any types on stack `seen`?
2910+
fn is_type_structurally_recursive(cx: &ctxt, sp: Span, seen: &mut Vec<t>,
2911+
ty: t) -> Representability {
2912+
debug!("is_type_structurally_recursive: {}",
2913+
::util::ppaux::ty_to_string(cx, ty));
2914+
2915+
match get(ty).sty {
2916+
ty_struct(did, _) | ty_enum(did, _) => {
2917+
{
2918+
// Iterate through stack of previously seen types.
2919+
let mut iter = seen.iter();
2920+
2921+
// The first item in `seen` is the type we are actually curious about.
2922+
// We want to return SelfRecursive if this type contains itself.
2923+
// It is important that we DON'T take generic parameters into account
2924+
// for this check, so that Bar<T> in this example counts as SelfRecursive:
2925+
//
2926+
// struct Foo;
2927+
// struct Bar<T> { x: Bar<Foo> }
2928+
2929+
match iter.next() {
2930+
Some(&seen_type) => {
2931+
if same_struct_or_enum_def_id(seen_type, did) {
2932+
debug!("SelfRecursive: {} contains {}",
2933+
::util::ppaux::ty_to_string(cx, seen_type),
2934+
::util::ppaux::ty_to_string(cx, ty));
2935+
return SelfRecursive;
2936+
}
2937+
}
2938+
None => {}
2939+
}
2940+
2941+
// We also need to know whether the first item contains other types that
2942+
// are structurally recursive. If we don't catch this case, we will recurse
2943+
// infinitely for some inputs.
2944+
//
2945+
// It is important that we DO take generic parameters into account here,
2946+
// so that code like this is considered SelfRecursive, not ContainsRecursive:
2947+
//
2948+
// struct Foo { Option<Option<Foo>> }
2949+
2950+
for &seen_type in iter {
2951+
if same_type(ty, seen_type) {
2952+
debug!("ContainsRecursive: {} contains {}",
2953+
::util::ppaux::ty_to_string(cx, seen_type),
2954+
::util::ppaux::ty_to_string(cx, ty));
2955+
return ContainsRecursive;
2956+
}
2957+
}
2958+
}
2959+
2960+
// For structs and enums, track all previously seen types by pushing them
2961+
// onto the 'seen' stack.
2962+
seen.push(ty);
2963+
let out = are_inner_types_recursive(cx, sp, seen, ty);
2964+
seen.pop();
2965+
out
2966+
}
2967+
_ => {
2968+
// No need to push in other cases.
2969+
are_inner_types_recursive(cx, sp, seen, ty)
2970+
}
29192971
}
29202972
}
29212973

@@ -2925,8 +2977,11 @@ pub fn is_type_representable(cx: &ctxt, sp: Span, ty: t) -> Representability {
29252977
// To avoid a stack overflow when checking an enum variant or struct that
29262978
// contains a different, structurally recursive type, maintain a stack
29272979
// of seen types and check recursion for each of them (issues #3008, #3779).
2928-
let mut seen: Vec<DefId> = Vec::new();
2929-
type_structurally_recursive(cx, sp, &mut seen, ty)
2980+
let mut seen: Vec<t> = Vec::new();
2981+
let r = is_type_structurally_recursive(cx, sp, &mut seen, ty);
2982+
debug!("is_type_representable: {} is {}",
2983+
::util::ppaux::ty_to_string(cx, ty), r);
2984+
r
29302985
}
29312986

29322987
pub fn type_is_trait(ty: t) -> bool {
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct Foo { foo: Option<Option<Foo>> }
12+
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
13+
14+
impl Foo { fn bar(&self) {} }
15+
16+
fn main() {}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct Baz { q: Option<Foo> }
12+
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
13+
14+
struct Foo { q: Option<Baz> }
15+
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
16+
17+
impl Foo { fn bar(&self) {} }
18+
19+
fn main() {}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::sync::Mutex;
12+
13+
struct Foo { foo: Mutex<Option<Foo>> }
14+
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
15+
16+
impl Foo { fn bar(&self) {} }
17+
18+
fn main() {}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct Foo<T> { foo: Option<Option<Foo<T>>> }
12+
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
13+
14+
impl<T> Foo<T> { fn bar(&self) {} }
15+
16+
fn main() {}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct Foo { foo: Bar<Foo> }
12+
struct Bar<T> { x: Bar<Foo> }
13+
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
14+
15+
impl Foo { fn foo(&self) {} }
16+
17+
fn main() {
18+
}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::sync::Mutex;
12+
13+
enum Foo { X(Mutex<Option<Foo>>) }
14+
//~^ ERROR illegal recursive enum type; wrap the inner value in a box to make it representable
15+
16+
impl Foo { fn bar(self) {} }
17+
18+
fn main() {}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
enum Foo { Voo(Option<Option<Foo>>) }
12+
//~^ ERROR illegal recursive enum type; wrap the inner value in a box to make it representable
13+
14+
impl Foo { fn bar(&self) {} }
15+
16+
fn main() { }

src/test/compile-fail/issue-3008-3.rs

+2
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,7 @@ enum E1 { V1(E2<E1>), }
1212
enum E2<T> { V2(E2<E1>), }
1313
//~^ ERROR illegal recursive enum type; wrap the inner value in a box to make it representable
1414

15+
impl E1 { fn foo(&self) {} }
16+
1517
fn main() {
1618
}

0 commit comments

Comments
 (0)