Skip to content

Commit 460f63c

Browse files
committed
Check enum and struct representability properly (issues rust-lang#3008 and rust-lang#3779)
1 parent 8c6c229 commit 460f63c

File tree

2 files changed

+111
-59
lines changed

2 files changed

+111
-59
lines changed

src/librustc/middle/ty.rs

Lines changed: 83 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2348,53 +2348,95 @@ pub fn is_instantiable(cx: ctxt, r_ty: t) -> bool {
23482348
!subtypes_require(cx, &mut seen, r_ty, r_ty)
23492349
}
23502350

2351-
pub fn type_structurally_contains(cx: ctxt, ty: t, test: |x: &sty| -> bool)
2352-
-> bool {
2353-
let sty = &get(ty).sty;
2354-
debug!("type_structurally_contains: {}",
2355-
::util::ppaux::ty_to_str(cx, ty));
2356-
if test(sty) { return true; }
2357-
match *sty {
2358-
ty_enum(did, ref substs) => {
2359-
for variant in (*enum_variants(cx, did)).iter() {
2360-
for aty in variant.args.iter() {
2361-
let sty = subst(cx, substs, *aty);
2362-
if type_structurally_contains(cx, sty, |x| test(x)) { return true; }
2351+
// Needed to distinguish between types that are recursive with themselves,
2352+
// and types that contain a different recursive type.
2353+
#[deriving(Eq)]
2354+
pub enum Repr {
2355+
Representable,
2356+
SelfRecursive,
2357+
ContainsRecursive,
2358+
}
2359+
2360+
// True if `ty` contains no structural recursion. This is necessary
2361+
// for structs and enums to have finite size.
2362+
pub fn is_type_representable(cx: ctxt, ty: t) -> Repr {
2363+
2364+
// Does the type `ty` directly (without indirection through a pointer)
2365+
// contain any types on stack `seen`?
2366+
fn type_structurally_recursive(cx: ctxt, ty: t,
2367+
seen: &mut ~[DefId]) -> Repr {
2368+
debug!("type_structurally_recursive: {}",
2369+
::util::ppaux::ty_to_str(cx, ty));
2370+
2371+
// Compare current type to previously seen types (stack 'seen')
2372+
match get(ty).sty {
2373+
ty_struct(did, _) |
2374+
ty_enum(did, _) => {
2375+
for (i, &seen_did) in seen.iter().enumerate() {
2376+
if did == seen_did {
2377+
return if i == 0 { SelfRecursive }
2378+
else { ContainsRecursive }
2379+
}
2380+
}
23632381
}
2382+
_ => (),
23642383
}
2365-
return false;
2366-
}
2367-
ty_struct(did, ref substs) => {
2368-
let r = lookup_struct_fields(cx, did);
2369-
for field in r.iter() {
2370-
let ft = lookup_field_type(cx, did, field.id, substs);
2371-
if type_structurally_contains(cx, ft, |x| test(x)) { return true; }
2372-
}
2373-
return false;
2374-
}
23752384

2376-
ty_tup(ref ts) => {
2377-
for tt in ts.iter() {
2378-
if type_structurally_contains(cx, *tt, |x| test(x)) { return true; }
2385+
// Check inner types
2386+
match get(ty).sty {
2387+
// Tuples
2388+
ty_tup(ref ts) => {
2389+
let mut r = Representable;
2390+
for t in ts.iter() {
2391+
r = type_structurally_recursive(cx, *t, seen);
2392+
if r != Representable { break; }
2393+
}
2394+
r
2395+
}
2396+
// Non-zero fixed-length vectors.
2397+
ty_vec(mt, vstore_fixed(len)) if len != 0 => {
2398+
type_structurally_recursive(cx, mt.ty, seen)
2399+
}
2400+
2401+
// Push struct and enum def-ids/substs onto `seen` before recursing.
2402+
ty_struct(did, ref substs) => {
2403+
seen.push(did);
2404+
let fields = struct_fields(cx, did, substs);
2405+
let mut r = Representable;
2406+
for f in fields.iter() {
2407+
r = type_structurally_recursive(cx, f.mt.ty, seen);
2408+
if r != Representable { break; }
2409+
}
2410+
seen.pop();
2411+
r
2412+
}
2413+
ty_enum(did, ref substs) => {
2414+
seen.push(did);
2415+
let vs = enum_variants(cx, did);
2416+
let mut r = Representable;
2417+
'outer: for variant in vs.iter() {
2418+
for aty in variant.args.iter() {
2419+
let sty = subst(cx, substs, *aty);
2420+
r = type_structurally_recursive(cx, sty, seen);
2421+
if r != Representable { break 'outer }
2422+
}
2423+
};
2424+
seen.pop();
2425+
r
2426+
}
2427+
2428+
_ => Representable,
23792429
}
2380-
return false;
2381-
}
2382-
ty_vec(ref mt, vstore_fixed(_)) => {
2383-
return type_structurally_contains(cx, mt.ty, test);
2384-
}
2385-
_ => return false
23862430
}
2387-
}
23882431

2389-
pub fn type_structurally_contains_uniques(cx: ctxt, ty: t) -> bool {
2390-
return type_structurally_contains(cx, ty, |sty| {
2391-
match *sty {
2392-
ty_uniq(_) |
2393-
ty_vec(_, vstore_uniq) |
2394-
ty_str(vstore_uniq) => true,
2395-
_ => false,
2396-
}
2397-
});
2432+
debug!("is_type_representable: {}",
2433+
::util::ppaux::ty_to_str(cx, ty));
2434+
2435+
// To avoid a stack overflow when checking an enum with a variant that
2436+
// that contains a different, structurally recursive type, maintain a
2437+
// stack of seen types and check recursion for each of them (issue #3008).
2438+
let mut seen: ~[DefId] = ~[];
2439+
type_structurally_recursive(cx, ty, &mut seen)
23982440
}
23992441

24002442
pub fn type_is_trait(ty: t) -> bool {

src/librustc/middle/typeck/check/mod.rs

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,10 @@ pub fn check_no_duplicate_fields(tcx: ty::ctxt,
531531
pub fn check_struct(ccx: @CrateCtxt, id: ast::NodeId, span: Span) {
532532
let tcx = ccx.tcx;
533533

534-
// Check that the class is instantiable
534+
// Check that the struct is representable
535+
check_representable(tcx, span, id, "struct");
536+
537+
// Check that the struct is instantiable
535538
check_instantiable(tcx, span, id);
536539

537540
if ty::lookup_simd(tcx, local_def(id)) {
@@ -3451,6 +3454,28 @@ pub fn check_const_with_ty(fcx: @FnCtxt,
34513454
writeback::resolve_type_vars_in_expr(fcx, e);
34523455
}
34533456

3457+
pub fn check_representable(tcx: ty::ctxt,
3458+
sp: Span,
3459+
item_id: ast::NodeId,
3460+
designation: &str) {
3461+
let rty = ty::node_id_to_type(tcx, item_id);
3462+
3463+
// Check that it is possible to represent this type. This call identifies
3464+
// (1) types that contain themselves and (2) types that contain a different
3465+
// recursive type. It is only necessary to throw an error on those that
3466+
// contain themselves. For case 2, there must be an inner type that will be
3467+
// caught by case 1.
3468+
match ty::is_type_representable(tcx, rty) {
3469+
ty::SelfRecursive => {
3470+
tcx.sess.span_err(
3471+
sp, format!("illegal recursive {} type; \
3472+
wrap the inner value in a box to make it representable",
3473+
designation));
3474+
}
3475+
ty::Representable | ty::ContainsRecursive => (),
3476+
}
3477+
}
3478+
34543479
/// Checks whether a type can be created without an instance of itself.
34553480
/// This is similar but different from the question of whether a type
34563481
/// can be represented. For example, the following type:
@@ -3606,7 +3631,6 @@ pub fn check_enum_variants(ccx: @CrateCtxt,
36063631
return variants;
36073632
}
36083633

3609-
let rty = ty::node_id_to_type(ccx.tcx, id);
36103634
let hint = ty::lookup_repr_hint(ccx.tcx, ast::DefId { crate: ast::LOCAL_CRATE, node: id });
36113635
if hint != attr::ReprAny && vs.len() <= 1 {
36123636
ccx.tcx.sess.span_err(sp, format!("unsupported representation for {}variant enum",
@@ -3621,22 +3645,8 @@ pub fn check_enum_variants(ccx: @CrateCtxt,
36213645
enum_var_cache.get().insert(local_def(id), @variants);
36223646
}
36233647

3624-
// Check that it is possible to represent this enum:
3625-
let mut outer = true;
3626-
let did = local_def(id);
3627-
if ty::type_structurally_contains(ccx.tcx, rty, |sty| {
3628-
match *sty {
3629-
ty::ty_enum(id, _) if id == did => {
3630-
if outer { outer = false; false }
3631-
else { true }
3632-
}
3633-
_ => false
3634-
}
3635-
}) {
3636-
ccx.tcx.sess.span_err(sp,
3637-
"illegal recursive enum type; \
3638-
wrap the inner value in a box to make it representable");
3639-
}
3648+
// Check that it is possible to represent this enum.
3649+
check_representable(ccx.tcx, sp, id, "enum");
36403650

36413651
// Check that it is possible to instantiate this enum:
36423652
//

0 commit comments

Comments
 (0)