diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 7110365034093..13f29724ba80e 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -3030,6 +3030,7 @@ pub fn trans_crate(sess: session::Session, const_values: HashMap(), module_data: HashMap(), lltypes: ty::new_ty_hash(), + llsizingtypes: ty::new_ty_hash(), names: new_namegen(sess.parse_sess.interner), next_addrspace: new_addrspace_gen(), symbol_hasher: symbol_hasher, diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 13902be776be5..b0ec0e1b90738 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -202,6 +202,7 @@ pub struct crate_ctxt { const_values: HashMap, module_data: HashMap<~str, ValueRef>, lltypes: HashMap, + llsizingtypes: HashMap, names: namegen, next_addrspace: addrspace_gen, symbol_hasher: @hash::State, diff --git a/src/librustc/middle/trans/machine.rs b/src/librustc/middle/trans/machine.rs index 75e494f7bfcd9..58a53cd878a70 100644 --- a/src/librustc/middle/trans/machine.rs +++ b/src/librustc/middle/trans/machine.rs @@ -13,60 +13,8 @@ use middle::trans::common::*; use middle::trans::type_of; -use middle::ty::field; use middle::ty; - -use syntax::parse::token::special_idents; - -// Creates a simpler, size-equivalent type. The resulting type is guaranteed -// to have (a) the same size as the type that was passed in; (b) to be non- -// recursive. This is done by replacing all boxes in a type with boxed unit -// types. -// This should reduce all pointers to some simple pointer type, to -// ensure that we don't recurse endlessly when computing the size of a -// nominal type that has pointers to itself in it. -pub fn simplify_type(tcx: ty::ctxt, typ: ty::t) -> ty::t { - fn nilptr(tcx: ty::ctxt) -> ty::t { - ty::mk_ptr(tcx, ty::mt {ty: ty::mk_nil(tcx), mutbl: ast::m_imm}) - } - fn simplifier(tcx: ty::ctxt, typ: ty::t) -> ty::t { - match ty::get(typ).sty { - ty::ty_box(_) | ty::ty_opaque_box | ty::ty_uniq(_) | - ty::ty_evec(_, ty::vstore_uniq) | ty::ty_evec(_, ty::vstore_box) | - ty::ty_estr(ty::vstore_uniq) | ty::ty_estr(ty::vstore_box) | - ty::ty_ptr(_) | ty::ty_rptr(*) => nilptr(tcx), - - ty::ty_bare_fn(*) | // FIXME(#4804) Bare fn repr - ty::ty_closure(*) => ty::mk_tup(tcx, ~[nilptr(tcx), nilptr(tcx)]), - - ty::ty_evec(_, ty::vstore_slice(_)) | - ty::ty_estr(ty::vstore_slice(_)) => { - ty::mk_tup(tcx, ~[nilptr(tcx), ty::mk_int(tcx)]) - } - // Reduce a class type to a record type in which all the fields are - // simplified - ty::ty_struct(did, ref substs) => { - let simpl_fields = (if ty::ty_dtor(tcx, did).is_present() { - // remember the drop flag - ~[field { - ident: special_idents::dtor, - mt: ty::mt {ty: ty::mk_u8(tcx), mutbl: ast::m_mutbl} - }] } - else { ~[] }) + - do ty::lookup_struct_fields(tcx, did).map |f| { - let t = ty::lookup_field_type(tcx, did, f.id, substs); - field { - ident: f.ident, - mt: ty::mt {ty: simplify_type(tcx, t), mutbl: ast::m_const - }} - }; - ty::mk_rec(tcx, simpl_fields) - } - _ => typ - } - } - ty::fold_ty(tcx, typ, |t| simplifier(tcx, t)) -} +use util::ppaux::ty_to_str; // ______________________________________________________________________ // compute sizeof / alignof @@ -180,27 +128,40 @@ pub fn llalign_of(cx: @crate_ctxt, t: TypeRef) -> ValueRef { // Computes the size of the data part of an enum. pub fn static_size_of_enum(cx: @crate_ctxt, t: ty::t) -> uint { - if cx.enum_sizes.contains_key(&t) { return cx.enum_sizes.get(&t); } + if cx.enum_sizes.contains_key(&t) { + return cx.enum_sizes.get(&t); + } + + debug!("static_size_of_enum %s", ty_to_str(cx.tcx, t)); + match ty::get(t).sty { - ty::ty_enum(tid, ref substs) => { - // Compute max(variant sizes). - let mut max_size = 0u; - let variants = ty::enum_variants(cx.tcx, tid); - for vec::each(*variants) |variant| { - let tup_ty = simplify_type( - cx.tcx, - ty::mk_tup(cx.tcx, /*bad*/copy variant.args)); - // Perform any type parameter substitutions. - let tup_ty = ty::subst(cx.tcx, substs, tup_ty); - // Here we possibly do a recursive call. - let this_size = - llsize_of_real(cx, type_of::type_of(cx, tup_ty)); - if max_size < this_size { max_size = this_size; } + ty::ty_enum(tid, ref substs) => { + // Compute max(variant sizes). + let mut max_size = 0; + let variants = ty::enum_variants(cx.tcx, tid); + for variants.each |variant| { + if variant.args.len() == 0 { + loop; + } + + let lltypes = variant.args.map(|&variant_arg| { + let substituted = ty::subst(cx.tcx, substs, variant_arg); + type_of::sizing_type_of(cx, substituted) + }); + + debug!("static_size_of_enum: variant %s type %s", + cx.tcx.sess.str_of(variant.name), + ty_str(cx.tn, T_struct(lltypes))); + + let this_size = llsize_of_real(cx, T_struct(lltypes)); + if max_size < this_size { + max_size = this_size; + } + } + cx.enum_sizes.insert(t, max_size); + return max_size; } - cx.enum_sizes.insert(t, max_size); - return max_size; - } - _ => cx.sess.bug(~"static_size_of_enum called on non-enum") + _ => cx.sess.bug(~"static_size_of_enum called on non-enum") } } diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index 972f702c18a81..d87674f6b6e84 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -89,6 +89,95 @@ pub fn type_of_non_gc_box(cx: @crate_ctxt, t: ty::t) -> TypeRef { } } +// A "sizing type" is an LLVM type, the size and alignment of which are +// guaranteed to be equivalent to what you would get out of `type_of()`. It's +// useful because: +// +// (1) It may be cheaper to compute the sizing type than the full type if all +// you're interested in is the size and/or alignment; +// +// (2) It won't make any recursive calls to determine the structure of the +// type behind pointers. This can help prevent infinite loops for +// recursive types. For example, `static_size_of_enum()` relies on this +// behavior. + +pub fn sizing_type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef { + if cx.llsizingtypes.contains_key(&t) { + return cx.llsizingtypes.get(&t); + } + + let llsizingty = match ty::get(t).sty { + ty::ty_nil | ty::ty_bot => T_nil(), + ty::ty_bool => T_bool(), + ty::ty_int(t) => T_int_ty(cx, t), + ty::ty_uint(t) => T_uint_ty(cx, t), + ty::ty_float(t) => T_float_ty(cx, t), + + ty::ty_estr(ty::vstore_uniq) | + ty::ty_estr(ty::vstore_box) | + ty::ty_evec(_, ty::vstore_uniq) | + ty::ty_evec(_, ty::vstore_box) | + ty::ty_box(*) | + ty::ty_opaque_box | + ty::ty_uniq(*) | + ty::ty_ptr(*) | + ty::ty_rptr(*) | + ty::ty_type | + ty::ty_opaque_closure_ptr(*) => T_ptr(T_i8()), + + ty::ty_estr(ty::vstore_slice(*)) | + ty::ty_evec(_, ty::vstore_slice(*)) => { + T_struct(~[T_ptr(T_i8()), T_ptr(T_i8())]) + } + + // FIXME(#4804) Bare fn repr + ty::ty_bare_fn(*) => T_struct(~[T_ptr(T_i8()), T_ptr(T_i8())]), + ty::ty_closure(*) => T_struct(~[T_ptr(T_i8()), T_ptr(T_i8())]), + ty::ty_trait(_, _, vstore) => T_opaque_trait(cx, vstore), + + ty::ty_estr(ty::vstore_fixed(size)) => T_array(T_i8(), size), + ty::ty_evec(mt, ty::vstore_fixed(size)) => { + T_array(sizing_type_of(cx, mt.ty), size) + } + + ty::ty_unboxed_vec(mt) => T_vec(cx, sizing_type_of(cx, mt.ty)), + + ty::ty_tup(ref elems) => { + T_struct(elems.map(|&t| sizing_type_of(cx, t))) + } + + ty::ty_rec(ref fields) => { + T_struct(fields.map(|f| sizing_type_of(cx, f.mt.ty))) + } + + ty::ty_struct(def_id, ref substs) => { + let fields = ty::lookup_struct_fields(cx.tcx, def_id); + let lltype = T_struct(fields.map(|field| { + let field_type = ty::lookup_field_type(cx.tcx, + def_id, + field.id, + substs); + sizing_type_of(cx, field_type) + })); + if ty::ty_dtor(cx.tcx, def_id).is_present() { + T_struct(~[lltype, T_i8()]) + } else { + lltype + } + } + + ty::ty_enum(def_id, _) => T_struct(enum_body_types(cx, def_id, t)), + + ty::ty_self | ty::ty_infer(*) | ty::ty_param(*) | ty::ty_err(*) => { + cx.tcx.sess.bug(~"fictitious type in sizing_type_of()") + } + }; + + cx.llsizingtypes.insert(t, llsizingty); + llsizingty +} + +// NB: If you update this, be sure to update `sizing_type_of()` as well. pub fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef { debug!("type_of %?: %?", t, ty::get(t)); @@ -236,23 +325,23 @@ pub fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef { return llty; } -pub fn fill_type_of_enum(cx: @crate_ctxt, did: ast::def_id, t: ty::t, - llty: TypeRef) { +pub fn enum_body_types(cx: @crate_ctxt, did: ast::def_id, t: ty::t) + -> ~[TypeRef] { + let univar = ty::enum_is_univariant(cx.tcx, did); + let size = machine::static_size_of_enum(cx, t); + if !univar { + ~[T_enum_discrim(cx), T_array(T_i8(), size)] + } else { + ~[T_array(T_i8(), size)] + } +} +pub fn fill_type_of_enum(cx: @crate_ctxt, + did: ast::def_id, + t: ty::t, + llty: TypeRef) { debug!("type_of_enum %?: %?", t, ty::get(t)); - - let lltys = { - let univar = ty::enum_is_univariant(cx.tcx, did); - let size = machine::static_size_of_enum(cx, t); - if !univar { - ~[T_enum_discrim(cx), T_array(T_i8(), size)] - } - else { - ~[T_array(T_i8(), size)] - } - }; - - common::set_struct_body(llty, lltys); + common::set_struct_body(llty, enum_body_types(cx, did, t)); } // Want refinements! (Or case classes, I guess