Skip to content

Commit 8ea5e38

Browse files
isabelmubrson
authored andcommitted
Check enum and struct representability properly (issues rust-lang#3008 and rust-lang#3779)
1 parent 16317fb commit 8ea5e38

File tree

2 files changed

+123
-58
lines changed

2 files changed

+123
-58
lines changed

src/librustc/middle/ty.rs

+90-40
Original file line numberDiff line numberDiff line change
@@ -2348,53 +2348,103 @@ 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+
/// Describes whether a type is representable. For types that are not
2352+
/// representable, 'SelfRecursive' and 'ContainsRecursive' are used to
2353+
/// distinguish between types that are recursive with themselves and types that
2354+
/// contain a different recursive type. These cases can therefore be treated
2355+
/// differently when reporting errors.
2356+
#[deriving(Eq)]
2357+
pub enum Representability {
2358+
Representable,
2359+
SelfRecursive,
2360+
ContainsRecursive,
2361+
}
2362+
2363+
/// Check whether a type is representable. This means it cannot contain unboxed
2364+
/// structural recursion. This check is needed for structs and enums.
2365+
pub fn is_type_representable(cx: ctxt, ty: t) -> Representability {
2366+
2367+
// Iterate until something non-representable is found
2368+
fn find_nonrepresentable<It: Iterator<t>>(cx: ctxt, seen: &mut ~[DefId],
2369+
mut iter: It) -> Representability {
2370+
for ty in iter {
2371+
let r = type_structurally_recursive(cx, seen, ty);
2372+
if r != Representable {
2373+
return r
23632374
}
23642375
}
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; }
2376+
Representable
2377+
}
2378+
2379+
// Does the type `ty` directly (without indirection through a pointer)
2380+
// contain any types on stack `seen`?
2381+
fn type_structurally_recursive(cx: ctxt, seen: &mut ~[DefId],
2382+
ty: t) -> Representability {
2383+
debug!("type_structurally_recursive: {}",
2384+
::util::ppaux::ty_to_str(cx, ty));
2385+
2386+
// Compare current type to previously seen types
2387+
match get(ty).sty {
2388+
ty_struct(did, _) |
2389+
ty_enum(did, _) => {
2390+
for (i, &seen_did) in seen.iter().enumerate() {
2391+
if did == seen_did {
2392+
return if i == 0 { SelfRecursive }
2393+
else { ContainsRecursive }
2394+
}
2395+
}
2396+
}
2397+
_ => (),
23722398
}
2373-
return false;
2374-
}
23752399

2376-
ty_tup(ref ts) => {
2377-
for tt in ts.iter() {
2378-
if type_structurally_contains(cx, *tt, |x| test(x)) { return true; }
2400+
// Check inner types
2401+
match get(ty).sty {
2402+
// Tuples
2403+
ty_tup(ref ts) => {
2404+
find_nonrepresentable(cx, seen, ts.iter().map(|t| *t))
2405+
}
2406+
// Non-zero fixed-length vectors.
2407+
ty_vec(mt, vstore_fixed(len)) if len != 0 => {
2408+
type_structurally_recursive(cx, seen, mt.ty)
2409+
}
2410+
2411+
// Push struct and enum def-ids onto `seen` before recursing.
2412+
ty_struct(did, ref substs) => {
2413+
seen.push(did);
2414+
let fields = struct_fields(cx, did, substs);
2415+
let r = find_nonrepresentable(cx, seen,
2416+
fields.iter().map(|f| f.mt.ty));
2417+
seen.pop();
2418+
r
2419+
}
2420+
ty_enum(did, ref substs) => {
2421+
seen.push(did);
2422+
let vs = enum_variants(cx, did);
2423+
2424+
let mut r = Representable;
2425+
for variant in vs.iter() {
2426+
let iter = variant.args.iter().map(|aty| subst(cx, substs, *aty));
2427+
r = find_nonrepresentable(cx, seen, iter);
2428+
2429+
if r != Representable { break }
2430+
}
2431+
2432+
seen.pop();
2433+
r
2434+
}
2435+
2436+
_ => Representable,
23792437
}
2380-
return false;
2381-
}
2382-
ty_vec(ref mt, vstore_fixed(_)) => {
2383-
return type_structurally_contains(cx, mt.ty, test);
2384-
}
2385-
_ => return false
23862438
}
2387-
}
23882439

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-
});
2440+
debug!("is_type_representable: {}",
2441+
::util::ppaux::ty_to_str(cx, ty));
2442+
2443+
// To avoid a stack overflow when checking an enum variant or struct that
2444+
// contains a different, structurally recursive type, maintain a stack
2445+
// of seen types and check recursion for each of them (issues #3008, #3779).
2446+
let mut seen: ~[DefId] = ~[];
2447+
type_structurally_recursive(cx, &mut seen, ty)
23982448
}
23992449

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

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

+33-18
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)) {
@@ -3410,6 +3413,33 @@ pub fn check_const_with_ty(fcx: @FnCtxt,
34103413
writeback::resolve_type_vars_in_expr(fcx, e);
34113414
}
34123415

3416+
/// Checks whether a type can be represented in memory. In particular, it
3417+
/// identifies types that contain themselves without indirection through a
3418+
/// pointer, which would mean their size is unbounded. This is different from
3419+
/// the question of whether a type can be instantiated. See the definition of
3420+
/// `check_instantiable`.
3421+
pub fn check_representable(tcx: ty::ctxt,
3422+
sp: Span,
3423+
item_id: ast::NodeId,
3424+
designation: &str) {
3425+
let rty = ty::node_id_to_type(tcx, item_id);
3426+
3427+
// Check that it is possible to represent this type. This call identifies
3428+
// (1) types that contain themselves and (2) types that contain a different
3429+
// recursive type. It is only necessary to throw an error on those that
3430+
// contain themselves. For case 2, there must be an inner type that will be
3431+
// caught by case 1.
3432+
match ty::is_type_representable(tcx, rty) {
3433+
ty::SelfRecursive => {
3434+
tcx.sess.span_err(
3435+
sp, format!("illegal recursive {} type; \
3436+
wrap the inner value in a box to make it representable",
3437+
designation));
3438+
}
3439+
ty::Representable | ty::ContainsRecursive => (),
3440+
}
3441+
}
3442+
34133443
/// Checks whether a type can be created without an instance of itself.
34143444
/// This is similar but different from the question of whether a type
34153445
/// can be represented. For example, the following type:
@@ -3565,7 +3595,6 @@ pub fn check_enum_variants(ccx: @CrateCtxt,
35653595
return variants;
35663596
}
35673597

3568-
let rty = ty::node_id_to_type(ccx.tcx, id);
35693598
let hint = ty::lookup_repr_hint(ccx.tcx, ast::DefId { crate: ast::LOCAL_CRATE, node: id });
35703599
if hint != attr::ReprAny && vs.len() <= 1 {
35713600
ccx.tcx.sess.span_err(sp, format!("unsupported representation for {}variant enum",
@@ -3580,22 +3609,8 @@ pub fn check_enum_variants(ccx: @CrateCtxt,
35803609
enum_var_cache.get().insert(local_def(id), @variants);
35813610
}
35823611

3583-
// Check that it is possible to represent this enum:
3584-
let mut outer = true;
3585-
let did = local_def(id);
3586-
if ty::type_structurally_contains(ccx.tcx, rty, |sty| {
3587-
match *sty {
3588-
ty::ty_enum(id, _) if id == did => {
3589-
if outer { outer = false; false }
3590-
else { true }
3591-
}
3592-
_ => false
3593-
}
3594-
}) {
3595-
ccx.tcx.sess.span_err(sp,
3596-
"illegal recursive enum type; \
3597-
wrap the inner value in a box to make it representable");
3598-
}
3612+
// Check that it is possible to represent this enum.
3613+
check_representable(ccx.tcx, sp, id, "enum");
35993614

36003615
// Check that it is possible to instantiate this enum:
36013616
//

0 commit comments

Comments
 (0)