Fix several issues with the struct and enum representability-checking logic.

This commit is contained in:
Johannes Muenzel 2014-10-06 02:20:25 -04:00
parent 63fe80e1ff
commit 53ddf2e57d
9 changed files with 241 additions and 63 deletions

View File

@ -2823,11 +2823,14 @@ pub fn is_instantiable(cx: &ctxt, r_ty: t) -> bool {
/// distinguish between types that are recursive with themselves and types that
/// contain a different recursive type. These cases can therefore be treated
/// differently when reporting errors.
#[deriving(PartialEq)]
///
/// The ordering of the cases is significant. They are sorted so that cmp::max
/// will keep the "more erroneous" of two values.
#[deriving(PartialOrd, Ord, Eq, PartialEq, Show)]
pub enum Representability {
Representable,
SelfRecursive,
ContainsRecursive,
SelfRecursive,
}
/// Check whether a type is representable. This means it cannot contain unboxed
@ -2835,98 +2838,150 @@ pub enum Representability {
pub fn is_type_representable(cx: &ctxt, sp: Span, ty: t) -> Representability {
// Iterate until something non-representable is found
fn find_nonrepresentable<It: Iterator<t>>(cx: &ctxt, sp: Span, seen: &mut Vec<DefId>,
fn find_nonrepresentable<It: Iterator<t>>(cx: &ctxt, sp: Span, seen: &mut Vec<t>,
mut iter: It) -> Representability {
for ty in iter {
let r = type_structurally_recursive(cx, sp, seen, ty);
if r != Representable {
return r
}
}
Representable
iter.fold(Representable,
|r, ty| cmp::max(r, is_type_structurally_recursive(cx, sp, seen, ty)))
}
// Does the type `ty` directly (without indirection through a pointer)
// contain any types on stack `seen`?
fn type_structurally_recursive(cx: &ctxt, sp: Span, seen: &mut Vec<DefId>,
ty: t) -> Representability {
debug!("type_structurally_recursive: {}",
::util::ppaux::ty_to_string(cx, ty));
// Compare current type to previously seen types
fn are_inner_types_recursive(cx: &ctxt, sp: Span,
seen: &mut Vec<t>, ty: t) -> Representability {
match get(ty).sty {
ty_struct(did, _) |
ty_enum(did, _) => {
for (i, &seen_did) in seen.iter().enumerate() {
if did == seen_did {
return if i == 0 { SelfRecursive }
else { ContainsRecursive }
}
}
}
_ => (),
}
// Check inner types
match get(ty).sty {
// Tuples
ty_tup(ref ts) => {
find_nonrepresentable(cx, sp, seen, ts.iter().map(|t| *t))
}
// Fixed-length vectors.
// FIXME(#11924) Behavior undecided for zero-length vectors.
ty_vec(ty, Some(_)) => {
type_structurally_recursive(cx, sp, seen, ty)
is_type_structurally_recursive(cx, sp, seen, ty)
}
// Push struct and enum def-ids onto `seen` before recursing.
ty_struct(did, ref substs) => {
seen.push(did);
let fields = struct_fields(cx, did, substs);
let r = find_nonrepresentable(cx, sp, seen,
fields.iter().map(|f| f.mt.ty));
seen.pop();
r
find_nonrepresentable(cx, sp, seen, fields.iter().map(|f| f.mt.ty))
}
ty_enum(did, ref substs) => {
seen.push(did);
let vs = enum_variants(cx, did);
let iter = vs.iter()
.flat_map(|variant| { variant.args.iter() })
.map(|aty| { aty.subst_spanned(cx, substs, Some(sp)) });
let mut r = Representable;
for variant in vs.iter() {
let iter = variant.args.iter().map(|aty| {
aty.subst_spanned(cx, substs, Some(sp))
});
r = find_nonrepresentable(cx, sp, seen, iter);
if r != Representable { break }
}
seen.pop();
r
find_nonrepresentable(cx, sp, seen, iter)
}
ty_unboxed_closure(did, _) => {
let upvars = unboxed_closure_upvars(cx, did);
find_nonrepresentable(cx,
sp,
seen,
upvars.iter().map(|f| f.ty))
find_nonrepresentable(cx, sp, seen, upvars.iter().map(|f| f.ty))
}
_ => Representable,
}
}
fn same_struct_or_enum_def_id(ty: t, did: DefId) -> bool {
match get(ty).sty {
ty_struct(ty_did, _) | ty_enum(ty_did, _) => {
ty_did == did
}
_ => false
}
}
fn same_type(a: t, b: t) -> bool {
match (&get(a).sty, &get(b).sty) {
(&ty_struct(did_a, ref substs_a), &ty_struct(did_b, ref substs_b)) |
(&ty_enum(did_a, ref substs_a), &ty_enum(did_b, ref substs_b)) => {
if did_a != did_b {
return false;
}
let types_a = substs_a.types.get_slice(subst::TypeSpace);
let types_b = substs_b.types.get_slice(subst::TypeSpace);
let mut pairs = types_a.iter().zip(types_b.iter());
pairs.all(|(&a, &b)| same_type(a, b))
}
_ => {
type_id(a) == type_id(b)
}
}
}
// Does the type `ty` directly (without indirection through a pointer)
// contain any types on stack `seen`?
fn is_type_structurally_recursive(cx: &ctxt, sp: Span, seen: &mut Vec<t>,
ty: t) -> Representability {
debug!("is_type_structurally_recursive: {}",
::util::ppaux::ty_to_string(cx, ty));
match get(ty).sty {
ty_struct(did, _) | ty_enum(did, _) => {
{
// Iterate through stack of previously seen types.
let mut iter = seen.iter();
// The first item in `seen` is the type we are actually curious about.
// We want to return SelfRecursive if this type contains itself.
// It is important that we DON'T take generic parameters into account
// for this check, so that Bar<T> in this example counts as SelfRecursive:
//
// struct Foo;
// struct Bar<T> { x: Bar<Foo> }
match iter.next() {
Some(&seen_type) => {
if same_struct_or_enum_def_id(seen_type, did) {
debug!("SelfRecursive: {} contains {}",
::util::ppaux::ty_to_string(cx, seen_type),
::util::ppaux::ty_to_string(cx, ty));
return SelfRecursive;
}
}
None => {}
}
// We also need to know whether the first item contains other types that
// are structurally recursive. If we don't catch this case, we will recurse
// infinitely for some inputs.
//
// It is important that we DO take generic parameters into account here,
// so that code like this is considered SelfRecursive, not ContainsRecursive:
//
// struct Foo { Option<Option<Foo>> }
for &seen_type in iter {
if same_type(ty, seen_type) {
debug!("ContainsRecursive: {} contains {}",
::util::ppaux::ty_to_string(cx, seen_type),
::util::ppaux::ty_to_string(cx, ty));
return ContainsRecursive;
}
}
}
// For structs and enums, track all previously seen types by pushing them
// onto the 'seen' stack.
seen.push(ty);
let out = are_inner_types_recursive(cx, sp, seen, ty);
seen.pop();
out
}
_ => {
// No need to push in other cases.
are_inner_types_recursive(cx, sp, seen, ty)
}
}
}
debug!("is_type_representable: {}",
::util::ppaux::ty_to_string(cx, ty));
// To avoid a stack overflow when checking an enum variant or struct that
// contains a different, structurally recursive type, maintain a stack
// of seen types and check recursion for each of them (issues #3008, #3779).
let mut seen: Vec<DefId> = Vec::new();
type_structurally_recursive(cx, sp, &mut seen, ty)
let mut seen: Vec<t> = Vec::new();
let r = is_type_structurally_recursive(cx, sp, &mut seen, ty);
debug!("is_type_representable: {} is {}",
::util::ppaux::ty_to_string(cx, ty), r);
r
}
pub fn type_is_trait(ty: t) -> bool {

View File

@ -0,0 +1,16 @@
// Copyright 2014 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.
struct Foo { foo: Option<Option<Foo>> }
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
impl Foo { fn bar(&self) {} }
fn main() {}

View File

@ -0,0 +1,19 @@
// Copyright 2014 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.
struct Baz { q: Option<Foo> }
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
struct Foo { q: Option<Baz> }
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
impl Foo { fn bar(&self) {} }
fn main() {}

View File

@ -0,0 +1,18 @@
// Copyright 2014 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::sync::Mutex;
struct Foo { foo: Mutex<Option<Foo>> }
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
impl Foo { fn bar(&self) {} }
fn main() {}

View File

@ -0,0 +1,16 @@
// Copyright 2014 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.
struct Foo<T> { foo: Option<Option<Foo<T>>> }
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
impl<T> Foo<T> { fn bar(&self) {} }
fn main() {}

View File

@ -0,0 +1,18 @@
// Copyright 2014 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.
struct Foo { foo: Bar<Foo> }
struct Bar<T> { x: Bar<Foo> }
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
impl Foo { fn foo(&self) {} }
fn main() {
}

View File

@ -0,0 +1,18 @@
// Copyright 2014 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::sync::Mutex;
enum Foo { X(Mutex<Option<Foo>>) }
//~^ ERROR illegal recursive enum type; wrap the inner value in a box to make it representable
impl Foo { fn bar(self) {} }
fn main() {}

View File

@ -0,0 +1,16 @@
// Copyright 2014 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.
enum Foo { Voo(Option<Option<Foo>>) }
//~^ ERROR illegal recursive enum type; wrap the inner value in a box to make it representable
impl Foo { fn bar(&self) {} }
fn main() { }

View File

@ -12,5 +12,7 @@ enum E1 { V1(E2<E1>), }
enum E2<T> { V2(E2<E1>), }
//~^ ERROR illegal recursive enum type; wrap the inner value in a box to make it representable
impl E1 { fn foo(&self) {} }
fn main() {
}