Skip to content

Commit

Permalink
Check for unsized types in enums.
Browse files Browse the repository at this point in the history
And allow the last field of a struct or variant to be unsized.
  • Loading branch information
nrc committed Apr 23, 2014
1 parent f78add1 commit 0540a59
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 16 deletions.
4 changes: 4 additions & 0 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2608,6 +2608,10 @@ pub fn type_is_sized(cx: &ctxt, ty: ty::t) -> bool {
!tps.any(|ty| !type_is_sized(cx, ty))
}
ty_tup(ref ts) => !ts.iter().any(|t| !type_is_sized(cx, *t)),
ty_enum(did, ref substs) => {
let variants = substd_enum_variants(cx, did, substs);
!variants.iter().any(|v| v.args.iter().any(|t| !type_is_sized(cx, *t)))
}
_ => true
}
}
Expand Down
82 changes: 66 additions & 16 deletions src/librustc/middle/typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,23 @@ impl<'a> Visitor<()> for CheckItemTypesVisitor<'a> {
}
}

struct CheckItemSizedTypesVisitor<'a> { ccx: &'a CrateCtxt<'a> }

impl<'a> Visitor<()> for CheckItemSizedTypesVisitor<'a> {
fn visit_item(&mut self, i: &ast::Item, _: ()) {
check_item_sized(self.ccx, i);
visit::walk_item(self, i, ());
}
}

pub fn check_item_types(ccx: &CrateCtxt, krate: &ast::Crate) {
let mut visit = CheckItemTypesVisitor { ccx: ccx };
visit::walk_crate(&mut visit, krate, ());

ccx.tcx.sess.abort_if_errors();

let mut visit = CheckItemSizedTypesVisitor { ccx: ccx };
visit::walk_crate(&mut visit, krate, ());
}

fn check_bare_fn(ccx: &CrateCtxt,
Expand Down Expand Up @@ -562,19 +576,19 @@ fn check_for_field_shadowing(tcx: &ty::ctxt,
}

fn check_fields_sized(tcx: &ty::ctxt,
id: ast::NodeId) {
let struct_def = tcx.map.expect_struct(id);
// FIXME(#13121) allow the last field to be DST
for f in struct_def.fields.iter() {
struct_def: @ast::StructDef) {
let len = struct_def.fields.len();
for i in range(0, len) {
let f = struct_def.fields.get(i);
let t = ty::node_id_to_type(tcx, f.node.id);
if !ty::type_is_sized(tcx, t) {
if !ty::type_is_sized(tcx, t) && i < (len - 1) {
match f.node.kind {
ast::NamedField(ident, _) => {
tcx.sess.span_err(f.span, format!("Dynamically sized type in field {}",
tcx.sess.span_err(f.span, format!("type of field {} is dynamically sized",
token::get_ident(ident)));
}
ast::UnnamedField(_) => {
tcx.sess.span_err(f.span, "Dynamically sized type in field");
tcx.sess.span_err(f.span, "dynamically sized type in field");
}
}
}
Expand All @@ -584,14 +598,8 @@ fn check_fields_sized(tcx: &ty::ctxt,
pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
let tcx = ccx.tcx;

// Check that the struct is representable
check_representable(tcx, span, id, "struct");

// Check that the struct is instantiable
if check_instantiable(tcx, span, id) {
// This might cause stack overflow if id is not instantiable.
check_fields_sized(tcx, id);
}
check_instantiable(tcx, span, id);

// Check there are no overlapping fields in super-structs
check_for_field_shadowing(tcx, local_def(id));
Expand All @@ -601,6 +609,24 @@ pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
}
}

pub fn check_item_sized(ccx: &CrateCtxt, it: &ast::Item) {
debug!("check_item(it.id={}, it.ident={})",
it.id,
ty::item_path_str(ccx.tcx, local_def(it.id)));
let _indenter = indenter();

match it.node {
ast::ItemEnum(ref enum_definition, _) => {
check_enum_variants_sized(ccx,
enum_definition.variants.as_slice());
}
ast::ItemStruct(..) => {
check_fields_sized(ccx.tcx, ccx.tcx.map.expect_struct(it.id));
}
_ => {}
}
}

pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
debug!("check_item(it.id={}, it.ident={})",
it.id,
Expand Down Expand Up @@ -3459,7 +3485,7 @@ pub fn check_const_with_ty(fcx: &FnCtxt,
pub fn check_representable(tcx: &ty::ctxt,
sp: Span,
item_id: ast::NodeId,
designation: &str) {
designation: &str) -> bool {
let rty = ty::node_id_to_type(tcx, item_id);

// Check that it is possible to represent this type. This call identifies
Expand All @@ -3473,9 +3499,11 @@ pub fn check_representable(tcx: &ty::ctxt,
sp, format!("illegal recursive {} type; \
wrap the inner value in a box to make it representable",
designation));
return false
}
ty::Representable | ty::ContainsRecursive => (),
}
return true
}

/// Checks whether a type can be created without an instance of itself.
Expand Down Expand Up @@ -3532,6 +3560,29 @@ pub fn check_simd(tcx: &ty::ctxt, sp: Span, id: ast::NodeId) {
}
}

pub fn check_enum_variants_sized(ccx: &CrateCtxt,
vs: &[ast::P<ast::Variant>]) {
for &v in vs.iter() {
match v.node.kind {
ast::TupleVariantKind(ref args) if args.len() > 0 => {
let ctor_ty = ty::node_id_to_type(ccx.tcx, v.node.id);
let arg_tys: Vec<ty::t> = ty::ty_fn_args(ctor_ty).iter().map(|a| *a).collect();
for i in range(0, args.len()) {
let t = arg_tys.get(i);
// Allow the last field in an enum to be unsized.
if !ty::type_is_sized(ccx.tcx, *t) && i < args.len() -1 {
ccx.tcx.sess.span_err(args.get(i).ty.span,
format!("type {} is dynamically sized",
ppaux::ty_to_str(ccx.tcx, *t)));
}
}
},
ast::StructVariantKind(struct_def) => check_fields_sized(ccx.tcx, struct_def),
_ => {}
}
}
}

pub fn check_enum_variants(ccx: &CrateCtxt,
sp: Span,
vs: &[ast::P<ast::Variant>],
Expand Down Expand Up @@ -3652,7 +3703,6 @@ pub fn check_enum_variants(ccx: &CrateCtxt,
// cache so that ty::enum_variants won't repeat this work
ccx.tcx.enum_var_cache.borrow_mut().insert(local_def(id), Rc::new(variants));

// Check that it is possible to represent this enum.
check_representable(ccx.tcx, sp, id, "enum");

// Check that it is possible to instantiate this enum:
Expand Down
7 changes: 7 additions & 0 deletions src/libsyntax/ast_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,13 @@ impl Map {
}
}

pub fn expect_variant(&self, id: NodeId) -> P<Variant> {
match self.find(id) {
Some(NodeVariant(variant)) => variant,
_ => fail!(format!("expected variant, found {}", self.node_to_str(id))),
}
}

pub fn expect_foreign_item(&self, id: NodeId) -> @ForeignItem {
match self.find(id) {
Some(NodeForeignItem(item)) => item,
Expand Down
29 changes: 29 additions & 0 deletions src/test/compile-fail/unsized3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,35 @@ fn f3<type X: T>(x: &X) {
fn f4<X: T>(x: &X) {
}

// Test with unsized enum.
enum E<type X> {
V(X),
}

fn f5<Y>(x: &Y) {}
fn f6<type X>(x: &X) {}
fn f7<type X>(x1: &E<X>, x2: &E<X>) {
f5(x1); //~ERROR instantiating a type parameter with an incompatible type `E<X>`, which does not
f6(x2); // ok
}


// Test with unsized struct.
struct S<type X> {
x: X,
}

fn f8<type X>(x1: &S<X>, x2: &S<X>) {
f5(x1); //~ERROR instantiating a type parameter with an incompatible type `S<X>`, which does not
f6(x2); // ok
}

// Test some tuples.
fn f9<type X>(x1: ~S<X>, x2: ~E<X>) {
f5(&(*x1, 34)); //~ERROR instantiating a type parameter with an incompatible type `(S<X>,int)`,
f5(&(32, *x2)); //~ERROR instantiating a type parameter with an incompatible type `(int,E<X>)`,
}

// I would like these to fail eventually.
/*
// impl - bounded
Expand Down
41 changes: 41 additions & 0 deletions src/test/compile-fail/unsized5.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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.
#![feature(struct_variant)]

// Test `type` types not allowed in fields or local variables.

/*trait T for type {}
fn f5<type X>(x: &X) {
let _: X; // ERROR local variable with dynamically sized type X
let _: (int, (X, int)); // ERROR local variable with dynamically sized type (int,(X,int))
}
fn f6<type X: T>(x: &X) {
let _: X; // ERROR local variable with dynamically sized type X
let _: (int, (X, int)); // ERROR local variable with dynamically sized type (int,(X,int))
}*/

struct S1<type X> {
f1: X, //~ ERROR type of field f1 is dynamically sized
f2: int,
}
struct S2<type X> {
f: int,
g: X, //~ ERROR type of field g is dynamically sized
h: int,
}

enum E<type X> {
V1(X, int), //~ERROR type X is dynamically sized
V2{f1: X, f: int}, //~ERROR type of field f1 is dynamically sized
}

pub fn main() {
}
1 change: 1 addition & 0 deletions src/test/run-pass/unsized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ trait T6<Y, type X> {}
trait T7<type X, type Y> {}
trait T8<type X: T2> {}
struct S1<type X>;
enum E<type X> {}
impl <type X> T1 for S1<X> {}
fn f<type X>() {}

Expand Down
16 changes: 16 additions & 0 deletions src/test/run-pass/unsized2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// <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.
#![feature(struct_variant)]

// Test sized-ness checking in substitution.

Expand Down Expand Up @@ -78,5 +79,20 @@ trait T7<type X: T> {
fn m2(x: &T5<X>);
}

// The last field in a struct or variant may be unsized
struct S2<type X> {
f: X,
}
struct S3<type X> {
f1: int,
f2: X,
}
enum E<type X> {
V1(X),
V2{x: X},
V3(int, X),
V4{u: int, x: X},
}

pub fn main() {
}

0 comments on commit 0540a59

Please sign in to comment.