diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 6eb698d34d207..c00856a0a98bd 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -168,8 +168,8 @@ pub fn check_expr(sess: Session, expr_field(*) | expr_index(*) | expr_tup(*) | - expr_struct(*) | - expr_rec(*) => { } + expr_struct(_, _, None) | + expr_rec(_, None) => { } expr_addr_of(*) => { sess.span_err( e.span, diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 2db3cae74e33b..5bfa314e39769 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -145,12 +145,12 @@ use core::prelude::*; use back::abi; -use lib; use lib::llvm::{llvm, ValueRef, BasicBlockRef}; use middle::const_eval; use middle::borrowck::root_map_key; use middle::pat_util::*; use middle::resolve::DefMap; +use middle::trans::adt; use middle::trans::base::*; use middle::trans::build::*; use middle::trans::callee; @@ -169,9 +169,7 @@ use util::common::indenter; use core::dvec::DVec; use core::dvec; -use core::libc::c_ulonglong; use std::oldmap::HashMap; -use syntax::ast::def_id; use syntax::ast; use syntax::ast::ident; use syntax::ast_util::path_to_ident; @@ -191,15 +189,15 @@ pub enum Lit { // range) pub enum Opt { lit(Lit), - var(/* disr val */int, /* variant dids (enm, var) */(def_id, def_id)), + var(/* disr val */int, @adt::Repr), range(@ast::expr, @ast::expr), vec_len_eq(uint), vec_len_ge(uint) } pub fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool { - match (*a, *b) { - (lit(a), lit(b)) => { + match (a, b) { + (&lit(a), &lit(b)) => { match (a, b) { (UnitLikeStructLit(a), UnitLikeStructLit(b)) => a == b, _ => { @@ -233,13 +231,13 @@ pub fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool { } } } - (range(a1, a2), range(b1, b2)) => { + (&range(a1, a2), &range(b1, b2)) => { const_eval::compare_lit_exprs(tcx, a1, b1) == 0 && const_eval::compare_lit_exprs(tcx, a2, b2) == 0 } - (var(a, _), var(b, _)) => a == b, - (vec_len_eq(a), vec_len_eq(b)) => a == b, - (vec_len_ge(a), vec_len_ge(b)) => a == b, + (&var(a, _), &var(b, _)) => a == b, + (&vec_len_eq(a), &vec_len_eq(b)) => a == b, + (&vec_len_ge(a), &vec_len_ge(b)) => a == b, _ => false } } @@ -267,8 +265,8 @@ pub fn trans_opt(bcx: block, o: &Opt) -> opt_result { let llval = consts::get_const_val(bcx.ccx(), lit_id); return single_result(rslt(bcx, llval)); } - var(disr_val, _) => { - return single_result(rslt(bcx, C_int(ccx, disr_val))); + var(disr_val, repr) => { + return adt::trans_case(bcx, repr, disr_val); } range(l1, l2) => { return range_result(rslt(bcx, consts::const_expr(ccx, l1)), @@ -283,13 +281,16 @@ pub fn trans_opt(bcx: block, o: &Opt) -> opt_result { } } -pub fn variant_opt(tcx: ty::ctxt, pat_id: ast::node_id) -> Opt { - match tcx.def_map.get(&pat_id) { +pub fn variant_opt(bcx: block, pat_id: ast::node_id) + -> Opt { + let ccx = bcx.ccx(); + match ccx.tcx.def_map.get(&pat_id) { ast::def_variant(enum_id, var_id) => { - let variants = ty::enum_variants(tcx, enum_id); + let variants = ty::enum_variants(ccx.tcx, enum_id); for vec::each(*variants) |v| { if var_id == v.id { - return var(v.disr_val, (enum_id, var_id)); + return var(v.disr_val, + adt::represent_node(bcx, pat_id)) } } ::core::util::unreachable(); @@ -298,7 +299,7 @@ pub fn variant_opt(tcx: ty::ctxt, pat_id: ast::node_id) -> Opt { return lit(UnitLikeStructLit(pat_id)); } _ => { - tcx.sess.bug(~"non-variant or struct in variant_opt()"); + ccx.sess.bug(~"non-variant or struct in variant_opt()"); } } } @@ -505,7 +506,7 @@ pub fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint, do enter_match(bcx, tcx.def_map, m, col, val) |p| { match /*bad*/copy p.node { ast::pat_enum(_, subpats) => { - if opt_eq(tcx, &variant_opt(tcx, p.id), opt) { + if opt_eq(tcx, &variant_opt(bcx, p.id), opt) { Some(option::get_or_default(subpats, vec::from_elem(variant_size, dummy))) @@ -515,7 +516,7 @@ pub fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint, } ast::pat_ident(_, _, None) if pat_is_variant_or_struct(tcx.def_map, p) => { - if opt_eq(tcx, &variant_opt(tcx, p.id), opt) { + if opt_eq(tcx, &variant_opt(bcx, p.id), opt) { Some(~[]) } else { None @@ -537,7 +538,7 @@ pub fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint, if opt_eq(tcx, &range(l1, l2), opt) {Some(~[])} else {None} } ast::pat_struct(_, field_pats, _) => { - if opt_eq(tcx, &variant_opt(tcx, p.id), opt) { + if opt_eq(tcx, &variant_opt(bcx, p.id), opt) { // Look up the struct variant ID. let struct_id; match tcx.def_map.get(&p.id) { @@ -762,8 +763,9 @@ pub fn enter_region(bcx: block, // Returns the options in one column of matches. An option is something that // needs to be conditionally matched at runtime; for example, the discriminant // on a set of enum variants or a literal. -pub fn get_options(ccx: @CrateContext, m: &[@Match], col: uint) -> ~[Opt] { - fn add_to_set(tcx: ty::ctxt, set: &DVec, val: Opt) { +pub fn get_options(bcx: block, m: &[@Match], col: uint) -> ~[Opt] { + let ccx = bcx.ccx(); + fn add_to_set(tcx: ty::ctxt, set: &DVec, +val: Opt) { if set.any(|l| opt_eq(tcx, l, &val)) {return;} set.push(val); } @@ -781,7 +783,7 @@ pub fn get_options(ccx: @CrateContext, m: &[@Match], col: uint) -> ~[Opt] { match ccx.tcx.def_map.find(&cur.id) { Some(ast::def_variant(*)) => { add_to_set(ccx.tcx, &found, - variant_opt(ccx.tcx, cur.id)); + variant_opt(bcx, cur.id)); } Some(ast::def_struct(*)) => { add_to_set(ccx.tcx, &found, @@ -800,7 +802,7 @@ pub fn get_options(ccx: @CrateContext, m: &[@Match], col: uint) -> ~[Opt] { match ccx.tcx.def_map.find(&cur.id) { Some(ast::def_variant(*)) => { add_to_set(ccx.tcx, &found, - variant_opt(ccx.tcx, cur.id)); + variant_opt(bcx, cur.id)); } _ => {} } @@ -827,34 +829,13 @@ pub struct ExtractedBlock { } pub fn extract_variant_args(bcx: block, - pat_id: ast::node_id, - vdefs: (def_id, def_id), + repr: &adt::Repr, + disr_val: int, val: ValueRef) - -> ExtractedBlock { - let (enm, evar) = vdefs; + -> ExtractedBlock { let _icx = bcx.insn_ctxt("match::extract_variant_args"); - let ccx = *bcx.fcx.ccx; - let enum_ty_substs = match ty::get(node_id_type(bcx, pat_id)).sty { - ty::ty_enum(id, ref substs) => { - assert id == enm; - /*bad*/copy (*substs).tps - } - _ => bcx.sess().bug(~"extract_variant_args: pattern has non-enum type") - }; - let mut blobptr = val; - let variants = ty::enum_variants(ccx.tcx, enm); - let size = ty::enum_variant_with_id(ccx.tcx, enm, - evar).args.len(); - if size > 0u && (*variants).len() != 1u { - let enumptr = - PointerCast(bcx, val, T_opaque_enum_ptr(ccx)); - blobptr = GEPi(bcx, enumptr, [0u, 1u]); - } - let vdefs_tg = enm; - let vdefs_var = evar; - let args = do vec::from_fn(size) |i| { - GEP_enum(bcx, blobptr, vdefs_tg, vdefs_var, - /*bad*/copy enum_ty_substs, i) + let args = do vec::from_fn(adt::num_args(repr, disr_val)) |i| { + adt::trans_field_ptr(bcx, repr, val, disr_val, i) }; ExtractedBlock { vals: args, bcx: bcx } @@ -1283,14 +1264,14 @@ pub fn compile_submatch(bcx: block, } bcx = root_pats_as_necessary(bcx, m, col, val); - let rec_fields = collect_record_or_struct_fields(bcx, m, col); if rec_fields.len() > 0 { let pat_ty = node_id_type(bcx, pat_id); - do expr::with_field_tys(tcx, pat_ty, None) |_has_dtor, field_tys| { + let pat_repr = adt::represent_type(bcx.ccx(), pat_ty); + do expr::with_field_tys(tcx, pat_ty, None) |discr, field_tys| { let rec_vals = rec_fields.map(|field_name| { let ix = ty::field_idx_strict(tcx, *field_name, field_tys); - GEPi(bcx, val, struct_field(ix)) + adt::trans_field_ptr(bcx, pat_repr, val, discr, ix) }); compile_submatch( bcx, @@ -1303,11 +1284,14 @@ pub fn compile_submatch(bcx: block, if any_tup_pat(m, col) { let tup_ty = node_id_type(bcx, pat_id); + let tup_repr = adt::represent_type(bcx.ccx(), tup_ty); let n_tup_elts = match /*bad*/copy ty::get(tup_ty).sty { ty::ty_tup(elts) => elts.len(), _ => ccx.sess.bug(~"non-tuple type in tuple pattern") }; - let tup_vals = vec::from_fn(n_tup_elts, |i| GEPi(bcx, val, [0u, i])); + let tup_vals = do vec::from_fn(n_tup_elts) |i| { + adt::trans_field_ptr(bcx, tup_repr, val, 0, i) + }; compile_submatch(bcx, enter_tup(bcx, dm, m, col, val, n_tup_elts), vec::append(tup_vals, vals_left), chk); return; @@ -1326,8 +1310,10 @@ pub fn compile_submatch(bcx: block, } } - let llstructvals = vec::from_fn( - struct_element_count, |i| GEPi(bcx, val, struct_field(i))); + let struct_repr = adt::represent_type(bcx.ccx(), struct_ty); + let llstructvals = do vec::from_fn(struct_element_count) |i| { + adt::trans_field_ptr(bcx, struct_repr, val, 0, i) + }; compile_submatch(bcx, enter_tuple_struct(bcx, dm, m, col, val, struct_element_count), @@ -1365,37 +1351,15 @@ pub fn compile_submatch(bcx: block, } // Decide what kind of branch we need - let opts = get_options(ccx, m, col); + let opts = get_options(bcx, m, col); let mut kind = no_branch; let mut test_val = val; if opts.len() > 0u { match opts[0] { - var(_, (enm, _)) => { - let variants = ty::enum_variants(tcx, enm); - if variants.len() == 1 { - kind = single; - } else { - let enumptr = - PointerCast(bcx, val, T_opaque_enum_ptr(ccx)); - let discrimptr = GEPi(bcx, enumptr, [0u, 0u]); - - - assert variants.len() > 1; - let min_discrim = do variants.foldr(0) |&x, y| { - int::min(x.disr_val, y) - }; - let max_discrim = do variants.foldr(0) |&x, y| { - int::max(x.disr_val, y) - }; - - test_val = LoadRangeAssert(bcx, discrimptr, - min_discrim as c_ulonglong, - (max_discrim + 1) - as c_ulonglong, - lib::llvm::True); - - kind = switch; - } + var(_, repr) => { + let (the_kind, val_opt) = adt::trans_switch(bcx, repr, val); + kind = the_kind; + for val_opt.each |&tval| { test_val = tval; } } lit(_) => { let pty = node_id_type(bcx, pat_id); @@ -1544,11 +1508,12 @@ pub fn compile_submatch(bcx: block, let mut size = 0u; let mut unpacked = ~[]; match *opt { - var(_, vdef) => { - let args = extract_variant_args(opt_cx, pat_id, vdef, val); - size = args.vals.len(); - unpacked = /*bad*/copy args.vals; - opt_cx = args.bcx; + var(disr_val, repr) => { + let ExtractedBlock {vals: argvals, bcx: new_bcx} = + extract_variant_args(opt_cx, repr, disr_val, val); + size = argvals.len(); + unpacked = argvals; + opt_cx = new_bcx; } vec_len_eq(n) | vec_len_ge(n) => { let tail = match *opt { @@ -1757,10 +1722,15 @@ pub fn bind_irrefutable_pat(bcx: block, } ast::pat_enum(_, sub_pats) => { match bcx.tcx().def_map.find(&pat.id) { - Some(ast::def_variant(*)) => { - let pat_def = ccx.tcx.def_map.get(&pat.id); - let vdefs = ast_util::variant_def_ids(pat_def); - let args = extract_variant_args(bcx, pat.id, vdefs, val); + Some(ast::def_variant(enum_id, var_id)) => { + let repr = adt::represent_node(bcx, pat.id); + let vinfo = ty::enum_variant_with_id(ccx.tcx, + enum_id, + var_id); + let args = extract_variant_args(bcx, + repr, + vinfo.disr_val, + val); for sub_pats.each |sub_pat| { for vec::eachi(args.vals) |i, argval| { bcx = bind_irrefutable_pat(bcx, @@ -1777,9 +1747,11 @@ pub fn bind_irrefutable_pat(bcx: block, // This is a unit-like struct. Nothing to do here. } Some(elems) => { - // This is the tuple variant case. + // This is the tuple struct case. + let repr = adt::represent_node(bcx, pat.id); for vec::eachi(elems) |i, elem| { - let fldptr = GEPi(bcx, val, struct_field(i)); + let fldptr = adt::trans_field_ptr(bcx, repr, + val, 0, i); bcx = bind_irrefutable_pat(bcx, *elem, fldptr, @@ -1797,10 +1769,12 @@ pub fn bind_irrefutable_pat(bcx: block, ast::pat_rec(fields, _) | ast::pat_struct(_, fields, _) => { let tcx = bcx.tcx(); let pat_ty = node_id_type(bcx, pat.id); - do expr::with_field_tys(tcx, pat_ty, None) |_hd, field_tys| { + let pat_repr = adt::represent_type(bcx.ccx(), pat_ty); + do expr::with_field_tys(tcx, pat_ty, None) |discr, field_tys| { for vec::each(fields) |f| { let ix = ty::field_idx_strict(tcx, f.ident, field_tys); - let fldptr = GEPi(bcx, val, struct_field(ix)); + let fldptr = adt::trans_field_ptr(bcx, pat_repr, val, + discr, ix); bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, @@ -1810,8 +1784,9 @@ pub fn bind_irrefutable_pat(bcx: block, } } ast::pat_tup(elems) => { + let repr = adt::represent_node(bcx, pat.id); for vec::eachi(elems) |i, elem| { - let fldptr = GEPi(bcx, val, [0u, i]); + let fldptr = adt::trans_field_ptr(bcx, repr, val, 0, i); bcx = bind_irrefutable_pat(bcx, *elem, fldptr, diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs new file mode 100644 index 0000000000000..3d3b40e4ff262 --- /dev/null +++ b/src/librustc/middle/trans/adt.rs @@ -0,0 +1,554 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * # Representation of Algebraic Data Types + * + * This module determines how to represent enums, structs, tuples, and + * (deprecated) structural records based on their monomorphized types; + * it is responsible both for choosing a representation and + * translating basic operations on values of those types. + * + * Note that the interface treats everything as a general case of an + * enum, so structs/tuples/etc. have one pseudo-variant with + * discriminant 0; i.e., as if they were a univariant enum. + * + * Having everything in one place will enable improvements to data + * structure representation; possibilities include: + * + * - Aligning enum bodies correctly, which in turn makes possible SIMD + * vector types (which are strict-alignment even on x86) and ports + * to strict-alignment architectures (PowerPC, SPARC, etc.). + * + * - User-specified alignment (e.g., cacheline-aligning parts of + * concurrently accessed data structures); LLVM can't represent this + * directly, so we'd have to insert padding fields in any structure + * that might contain one and adjust GEP indices accordingly. See + * issue #4578. + * + * - Rendering `Option<&T>` as a possibly-null `*T` instead of using + * an extra word (and likewise for `@T` and `~T`). Can and probably + * should also apply to any enum with one empty case and one case + * starting with a non-null pointer (e.g., `Result<(), ~str>`). + * + * - Using smaller integer types for discriminants. + * + * - Store nested enums' discriminants in the same word. Rather, if + * some variants start with enums, and those enums representations + * have unused alignment padding between discriminant and body, the + * outer enum's discriminant can be stored there and those variants + * can start at offset 0. Kind of fancy, and might need work to + * make copies of the inner enum type cooperate, but it could help + * with `Option` or `Result` wrapped around another enum. + * + * - Tagged pointers would be neat, but given that any type can be + * used unboxed and any field can have pointers (including mutable) + * taken to it, implementing them for Rust seems difficult. + */ + +use core::container::Map; +use core::libc::c_ulonglong; +use core::option::{Option, Some, None}; +use core::vec; + +use lib::llvm::{ValueRef, TypeRef, True, False}; +use middle::trans::_match; +use middle::trans::build::*; +use middle::trans::common::*; +use middle::trans::machine; +use middle::trans::type_of; +use middle::ty; +use syntax::ast; +use util::ppaux::ty_to_str; + + +/// Representations. +pub enum Repr { + /** + * `Unit` exists only so that an enum with a single C-like variant + * can occupy no space, for ABI compatibility with rustc from + * before (and during) the creation of this module. It may not be + * worth keeping around; `CEnum` and `Univariant` cover it + * overwise. + */ + Unit(int), + /// C-like enums; basically an int. + CEnum(int, int), // discriminant range + /// Single-case variants, and structs/tuples/records. + Univariant(Struct, Destructor), + /** + * General-case enums: discriminant as int, followed by fields. + * The fields start immediately after the discriminant, meaning + * that they may not be correctly aligned for the platform's ABI; + * see above. + */ + General(~[Struct]) +} + +/** + * Structs without destructors have historically had an extra layer of + * LLVM-struct to make accessing them work the same as structs with + * destructors. This could probably be flattened to a boolean now + * that this module exists. + */ +enum Destructor { + StructWithDtor, + StructWithoutDtor, + NonStruct +} + +/// For structs, and struct-like parts of anything fancier. +struct Struct { + size: u64, + align: u64, + fields: ~[ty::t] +} + +/** + * Convenience for `represent_type`. There should probably be more or + * these, for places in trans where the `ty::t` isn't directly + * available. + */ +pub fn represent_node(bcx: block, node: ast::node_id) -> @Repr { + represent_type(bcx.ccx(), node_id_type(bcx, node)) +} + +/// Decides how to represent a given type. +pub fn represent_type(cx: @CrateContext, t: ty::t) -> @Repr { + debug!("Representing: %s", ty_to_str(cx.tcx, t)); + match cx.adt_reprs.find(&t) { + Some(repr) => return *repr, + None => { } + } + let repr = @match ty::get(t).sty { + ty::ty_tup(ref elems) => { + Univariant(mk_struct(cx, *elems), NonStruct) + } + ty::ty_rec(ref fields) => { + // XXX: Are these in the right order? + Univariant(mk_struct(cx, fields.map(|f| f.mt.ty)), + StructWithoutDtor) + } + ty::ty_struct(def_id, ref substs) => { + let fields = ty::lookup_struct_fields(cx.tcx, def_id); + let dt = ty::ty_dtor(cx.tcx, def_id).is_present(); + Univariant(mk_struct(cx, fields.map(|field| { + ty::lookup_field_type(cx.tcx, def_id, field.id, substs) + })), if dt { StructWithDtor } else { StructWithoutDtor }) + } + ty::ty_enum(def_id, ref substs) => { + struct Case { discr: int, tys: ~[ty::t] }; + + let cases = do ty::enum_variants(cx.tcx, def_id).map |vi| { + let arg_tys = do vi.args.map |&raw_ty| { + ty::subst(cx.tcx, substs, raw_ty) + }; + Case { discr: vi.disr_val, tys: arg_tys } + }; + if cases.len() == 0 { + // Uninhabitable; represent as unit + Unit(0) + } else if cases.len() == 1 && cases[0].tys.len() == 0 { + // `()`-like; see comment on definition of `Unit`. + Unit(cases[0].discr) + } else if cases.len() == 1 { + // Equivalent to a struct/tuple/newtype. + assert cases[0].discr == 0; + Univariant(mk_struct(cx, cases[0].tys), NonStruct) + } else if cases.all(|c| c.tys.len() == 0) { + // All bodies empty -> intlike + let discrs = cases.map(|c| c.discr); + CEnum(discrs.min(), discrs.max()) + } else { + // The general case. Since there's at least one + // non-empty body, explicit discriminants should have + // been rejected by a checker before this point. + if !cases.alli(|i,c| c.discr == (i as int)) { + cx.sess.bug(fmt!("non-C-like enum %s with specified \ + discriminants", + ty::item_path_str(cx.tcx, def_id))) + } + General(cases.map(|c| mk_struct(cx, c.tys))) + } + } + _ => cx.sess.bug(~"adt::represent_type called on non-ADT type") + }; + cx.adt_reprs.insert(t, repr); + return repr; +} + +fn mk_struct(cx: @CrateContext, tys: &[ty::t]) -> Struct { + let lltys = tys.map(|&ty| type_of::sizing_type_of(cx, ty)); + let llty_rec = T_struct(lltys); + Struct { + size: machine::llsize_of_alloc(cx, llty_rec) /*bad*/as u64, + align: machine::llalign_of_min(cx, llty_rec) /*bad*/as u64, + fields: vec::from_slice(tys) + } +} + +/** + * Returns the fields of a struct for the given representation. + * All nominal types are LLVM structs, in order to be able to use + * forward-declared opaque types to prevent circularity in `type_of`. + */ +pub fn fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] { + generic_fields_of(cx, r, false) +} +/// Like `fields_of`, but for `type_of::sizing_type_of` (q.v.). +pub fn sizing_fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] { + generic_fields_of(cx, r, true) +} +fn generic_fields_of(cx: @CrateContext, r: &Repr, sizing: bool) + -> ~[TypeRef] { + match *r { + Unit(*) => ~[], + CEnum(*) => ~[T_enum_discrim(cx)], + Univariant(ref st, dt) => { + let f = if sizing { + st.fields.map(|&ty| type_of::sizing_type_of(cx, ty)) + } else { + st.fields.map(|&ty| type_of::type_of(cx, ty)) + }; + match dt { + NonStruct => f, + StructWithoutDtor => ~[T_struct(f)], + StructWithDtor => ~[T_struct(f), T_i8()] + } + } + General(ref sts) => { + ~[T_enum_discrim(cx), + T_array(T_i8(), sts.map(|st| st.size).max() /*bad*/as uint)] + } + } +} + +/** + * Obtain a representation of the discriminant sufficient to translate + * destructuring; this may or may not involve the actual discriminant. + * + * This should ideally be less tightly tied to `_match`. + */ +pub fn trans_switch(bcx: block, r: &Repr, scrutinee: ValueRef) + -> (_match::branch_kind, Option) { + match *r { + CEnum(*) | General(*) => { + (_match::switch, Some(trans_get_discr(bcx, r, scrutinee))) + } + Unit(*) | Univariant(*) => { + (_match::single, None) + } + } +} + +/// Obtain the actual discriminant of a value. +pub fn trans_get_discr(bcx: block, r: &Repr, scrutinee: ValueRef) + -> ValueRef { + match *r { + Unit(the_disc) => C_int(bcx.ccx(), the_disc), + CEnum(min, max) => load_discr(bcx, scrutinee, min, max), + Univariant(*) => C_int(bcx.ccx(), 0), + General(ref cases) => load_discr(bcx, scrutinee, 0, + (cases.len() - 1) as int) + } +} + +/// Helper for cases where the discriminant is simply loaded. +fn load_discr(bcx: block, scrutinee: ValueRef, min: int, max: int) + -> ValueRef { + let ptr = GEPi(bcx, scrutinee, [0, 0]); + if max + 1 == min { + // i.e., if the range is everything. The lo==hi case would be + // rejected by the LLVM verifier (it would mean either an + // empty set, which is impossible, or the entire range of the + // type, which is pointless). + Load(bcx, ptr) + } else { + // llvm::ConstantRange can deal with ranges that wrap around, + // so an overflow on (max + 1) is fine. + LoadRangeAssert(bcx, ptr, min as c_ulonglong, + (max + 1) as c_ulonglong, + /* signed: */ True) + } +} + +/** + * Yield information about how to dispatch a case of the + * discriminant-like value returned by `trans_switch`. + * + * This should ideally be less tightly tied to `_match`. + */ +pub fn trans_case(bcx: block, r: &Repr, discr: int) -> _match::opt_result { + match *r { + CEnum(*) => { + _match::single_result(rslt(bcx, C_int(bcx.ccx(), discr))) + } + Unit(*) | Univariant(*)=> { + bcx.ccx().sess.bug(~"no cases for univariants or structs") + } + General(*) => { + _match::single_result(rslt(bcx, C_int(bcx.ccx(), discr))) + } + } +} + +/** + * Begin initializing a new value of the given case of the given + * representation. The fields, if any, should then be initialized via + * `trans_field_ptr`. + */ +pub fn trans_start_init(bcx: block, r: &Repr, val: ValueRef, discr: int) { + match *r { + Unit(the_discr) => { + assert discr == the_discr; + } + CEnum(min, max) => { + assert min <= discr && discr <= max; + Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0])) + } + Univariant(_, StructWithDtor) => { + assert discr == 0; + Store(bcx, C_u8(1), GEPi(bcx, val, [0, 1])) + } + Univariant(*) => { + assert discr == 0; + } + General(*) => { + Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0])) + } + } +} + +/** + * The number of fields in a given case; for use when obtaining this + * information from the type or definition is less convenient. + */ +pub fn num_args(r: &Repr, discr: int) -> uint { + match *r { + Unit(*) | CEnum(*) => 0, + Univariant(ref st, _dt) => { assert discr == 0; st.fields.len() } + General(ref cases) => cases[discr as uint].fields.len() + } +} + +/// Access a field, at a point when the value's case is known. +pub fn trans_field_ptr(bcx: block, r: &Repr, val: ValueRef, discr: int, + ix: uint) -> ValueRef { + // Note: if this ever needs to generate conditionals (e.g., if we + // decide to do some kind of cdr-coding-like non-unique repr + // someday), it will need to return a possibly-new bcx as well. + match *r { + Unit(*) | CEnum(*) => { + bcx.ccx().sess.bug(~"element access in C-like enum") + } + Univariant(ref st, dt) => { + assert discr == 0; + let val = match dt { + NonStruct => val, + StructWithDtor | StructWithoutDtor => GEPi(bcx, val, [0, 0]) + }; + struct_field_ptr(bcx, st, val, ix, false) + } + General(ref cases) => { + struct_field_ptr(bcx, &cases[discr as uint], + GEPi(bcx, val, [0, 1]), ix, true) + } + } +} + +fn struct_field_ptr(bcx: block, st: &Struct, val: ValueRef, ix: uint, + needs_cast: bool) -> ValueRef { + let ccx = bcx.ccx(); + + let val = if needs_cast { + let real_llty = T_struct(st.fields.map( + |&ty| type_of::type_of(ccx, ty))); + PointerCast(bcx, val, T_ptr(real_llty)) + } else { + val + }; + + GEPi(bcx, val, [0, ix]) +} + +/// Access the struct drop flag, if present. +pub fn trans_drop_flag_ptr(bcx: block, r: &Repr, val: ValueRef) -> ValueRef { + match *r { + Univariant(_, StructWithDtor) => GEPi(bcx, val, [0, 1]), + _ => bcx.ccx().sess.bug(~"tried to get drop flag of non-droppable \ + type") + } +} + +/** + * Construct a constant value, suitable for initializing a + * GlobalVariable, given a case and constant values for its fields. + * Note that this may have a different LLVM type (and different + * alignment!) from the representation's `type_of`, so it needs a + * pointer cast before use. + * + * The LLVM type system does not directly support unions, and only + * pointers can be bitcast, so a constant (and, by extension, the + * GlobalVariable initialized by it) will have a type that can vary + * depending on which case of an enum it is. + * + * To understand the alignment situation, consider `enum E { V64(u64), + * V32(u32, u32) }` on win32. The type should have 8-byte alignment + * to accommodate the u64 (currently it doesn't; this is a known bug), + * but `V32(x, y)` would have LLVM type `{i32, i32, i32}`, which is + * 4-byte aligned. + * + * Currently the returned value has the same size as the type, but + * this may be changed in the future to avoid allocating unnecessary + * space after values of shorter-than-maximum cases. + */ +pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int, + vals: &[ValueRef]) -> ValueRef { + match *r { + Unit(*) => { + C_struct(~[]) + } + CEnum(min, max) => { + assert vals.len() == 0; + assert min <= discr && discr <= max; + C_int(ccx, discr) + } + Univariant(ref st, dt) => { + assert discr == 0; + let s = C_struct(build_const_struct(ccx, st, vals)); + match dt { + NonStruct => s, + // The actual destructor flag doesn't need to be present. + // But add an extra struct layer for compatibility. + StructWithDtor | StructWithoutDtor => C_struct(~[s]) + } + } + General(ref cases) => { + let case = &cases[discr as uint]; + let max_sz = cases.map(|s| s.size).max(); + let body = build_const_struct(ccx, case, vals); + + // The unary packed struct has alignment 1 regardless of + // its contents, so it will always be located at the + // expected offset at runtime. + C_struct([C_int(ccx, discr), + C_packed_struct([C_struct(body)]), + padding(max_sz - case.size)]) + } + } +} + +/** + * Building structs is a little complicated, because we might need to + * insert padding if a field's value is less aligned than its type. + * + * Continuing the example from `trans_const`, a value of type `(u32, + * E)` should have the `E` at offset 8, but if that field's + * initializer is 4-byte aligned then simply translating the tuple as + * a two-element struct will locate it at offset 4, and accesses to it + * will read the wrong memory. + */ +fn build_const_struct(ccx: @CrateContext, st: &Struct, vals: &[ValueRef]) + -> ~[ValueRef] { + assert vals.len() == st.fields.len(); + + let mut offset = 0; + let mut cfields = ~[]; + for st.fields.eachi |i, &ty| { + let llty = type_of::sizing_type_of(ccx, ty); + let type_align = machine::llalign_of_min(ccx, llty) + /*bad*/as u64; + let val_align = machine::llalign_of_min(ccx, val_ty(vals[i])) + /*bad*/as u64; + let target_offset = roundup(offset, type_align); + offset = roundup(offset, val_align); + if (offset != target_offset) { + cfields.push(padding(target_offset - offset)); + offset = target_offset; + } + assert !is_undef(vals[i]); + // If that assert fails, could change it to wrap in a struct? + // (See `const_struct_field` for why real fields must not be undef.) + cfields.push(vals[i]); + } + + return cfields; +} + +fn padding(size: u64) -> ValueRef { + C_undef(T_array(T_i8(), size /*bad*/as uint)) +} + +// XXX this utility routine should be somewhere more general +#[always_inline] +fn roundup(x: u64, a: u64) -> u64 { ((x + (a - 1)) / a) * a } + +/// Get the discriminant of a constant value. (Not currently used.) +pub fn const_get_discrim(ccx: @CrateContext, r: &Repr, val: ValueRef) + -> int { + match *r { + Unit(discr) => discr, + CEnum(*) => const_to_int(val) as int, + Univariant(*) => 0, + General(*) => const_to_int(const_get_elt(ccx, val, [0])) as int, + } +} + +/** + * Extract a field of a constant value, as appropriate for its + * representation. + * + * (Not to be confused with `common::const_get_elt`, which operates on + * raw LLVM-level structs and arrays.) + */ +pub fn const_get_field(ccx: @CrateContext, r: &Repr, val: ValueRef, + _discr: int, ix: uint) -> ValueRef { + match *r { + Unit(*) | CEnum(*) => ccx.sess.bug(~"element access in C-like enum \ + const"), + Univariant(_, NonStruct) => const_struct_field(ccx, val, ix), + Univariant(*) => const_struct_field(ccx, const_get_elt(ccx, val, + [0]), ix), + General(*) => const_struct_field(ccx, const_get_elt(ccx, val, + [1, 0]), ix) + } +} + +/// Extract field of struct-like const, skipping our alignment padding. +fn const_struct_field(ccx: @CrateContext, val: ValueRef, ix: uint) + -> ValueRef { + // Get the ix-th non-undef element of the struct. + let mut real_ix = 0; // actual position in the struct + let mut ix = ix; // logical index relative to real_ix + let mut field; + loop { + loop { + field = const_get_elt(ccx, val, [real_ix]); + if !is_undef(field) { + break; + } + real_ix = real_ix + 1; + } + if ix == 0 { + return field; + } + ix = ix - 1; + real_ix = real_ix + 1; + } +} + +/// Is it safe to bitcast a value to the one field of its one variant? +pub fn is_newtypeish(r: &Repr) -> bool { + match *r { + Univariant(ref st, StructWithoutDtor) + | Univariant(ref st, NonStruct) => st.fields.len() == 1, + _ => false + } +} diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index b6701c6c3df1f..8280243455d9b 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -39,6 +39,7 @@ use middle::astencode; use middle::borrowck::RootInfo; use middle::resolve; use middle::trans::_match; +use middle::trans::adt; use middle::trans::base; use middle::trans::build::*; use middle::trans::callee; @@ -66,6 +67,7 @@ use util::ppaux::{ty_to_str, ty_to_short_str}; use util::ppaux; use core::hash; +use core::hashmap::linear::LinearMap; use core::int; use core::io; use core::libc::{c_uint, c_ulonglong}; @@ -238,25 +240,6 @@ pub fn bump_ptr(bcx: block, t: ty::t, base: ValueRef, sz: ValueRef) -> PointerCast(bcx, bumped, typ) } -// Replacement for the LLVM 'GEP' instruction when field indexing into a enum. -// @llblobptr is the data part of a enum value; its actual type -// is meaningless, as it will be cast away. -pub fn GEP_enum(bcx: block, llblobptr: ValueRef, enum_id: ast::def_id, - variant_id: ast::def_id, ty_substs: &[ty::t], - ix: uint) -> ValueRef { - let _icx = bcx.insn_ctxt("GEP_enum"); - let ccx = bcx.ccx(); - let variant = ty::enum_variant_with_id(ccx.tcx, enum_id, variant_id); - assert ix < variant.args.len(); - - let arg_lltys = vec::map(variant.args, |aty| { - type_of(ccx, ty::subst_tps(ccx.tcx, ty_substs, None, *aty)) - }); - let typed_blobptr = PointerCast(bcx, llblobptr, - T_ptr(T_struct(arg_lltys))); - GEPi(bcx, typed_blobptr, [0u, ix]) -} - // Returns a pointer to the body for the box. The box may be an opaque // box. The result will be casted to the type of body_t, if it is statically // known. @@ -639,35 +622,17 @@ pub fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t, f: val_and_ty_fn) -> block { let _icx = cx.insn_ctxt("iter_structural_ty"); - fn iter_variant(cx: block, a_tup: ValueRef, + fn iter_variant(cx: block, repr: &adt::Repr, av: ValueRef, variant: ty::VariantInfo, - tps: &[ty::t], tid: ast::def_id, - f: val_and_ty_fn) -> block { + tps: &[ty::t], f: val_and_ty_fn) -> block { let _icx = cx.insn_ctxt("iter_variant"); - if variant.args.len() == 0u { return cx; } - let fn_ty = variant.ctor_ty; - let ccx = cx.ccx(); + let tcx = cx.tcx(); let mut cx = cx; - match ty::get(fn_ty).sty { - ty::ty_bare_fn(ref fn_ty) => { - let mut j = 0u; - let v_id = variant.id; - for vec::each(fn_ty.sig.inputs) |a| { - let llfldp_a = GEP_enum(cx, a_tup, tid, v_id, - /*bad*/copy tps, j); - // This assumes the self type is absent (it passes - // None for the self_ty_opt arg of substs_tps). - // I think that's ok since you can't have an enum - // inside a trait. - let ty_subst = ty::subst_tps(ccx.tcx, tps, None, a.ty); - cx = f(cx, llfldp_a, ty_subst); - j += 1u; - } - } - _ => cx.tcx().sess.bug(fmt!("iter_variant: not a function type: \ - %s (variant name = %s)", - cx.ty_to_str(fn_ty), - *cx.sess().str_of(variant.name))) + + for variant.args.eachi |i, &arg| { + cx = f(cx, + adt::trans_field_ptr(cx, repr, av, variant.disr_val, i), + ty::subst_tps(tcx, tps, None, arg)); } return cx; } @@ -675,9 +640,10 @@ pub fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t, let mut cx = cx; match /*bad*/copy ty::get(t).sty { ty::ty_rec(*) | ty::ty_struct(*) => { - do expr::with_field_tys(cx.tcx(), t, None) |_has_dtor, field_tys| { + let repr = adt::represent_type(cx.ccx(), t); + do expr::with_field_tys(cx.tcx(), t, None) |discr, field_tys| { for vec::eachi(field_tys) |i, field_ty| { - let llfld_a = GEPi(cx, av, struct_field(i)); + let llfld_a = adt::trans_field_ptr(cx, repr, av, discr, i); cx = f(cx, llfld_a, field_ty.mt.ty); } } @@ -688,51 +654,56 @@ pub fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t, cx = tvec::iter_vec_raw(cx, base, t, len, f); } ty::ty_tup(args) => { - for vec::eachi(args) |i, arg| { - let llfld_a = GEPi(cx, av, [0u, i]); - cx = f(cx, llfld_a, *arg); - } + let repr = adt::represent_type(cx.ccx(), t); + for vec::eachi(args) |i, arg| { + let llfld_a = adt::trans_field_ptr(cx, repr, av, 0, i); + cx = f(cx, llfld_a, *arg); + } } ty::ty_enum(tid, ref substs) => { - let variants = ty::enum_variants(cx.tcx(), tid); - let n_variants = (*variants).len(); - - // Cast the enums to types we can GEP into. - if n_variants == 1u { - return iter_variant(cx, - av, - variants[0], - /*bad*/copy substs.tps, - tid, - f); - } + let ccx = cx.ccx(); - let ccx = cx.ccx(); - let llenumty = T_opaque_enum_ptr(ccx); - let av_enum = PointerCast(cx, av, llenumty); - let lldiscrim_a_ptr = GEPi(cx, av_enum, [0u, 0u]); - let llunion_a_ptr = GEPi(cx, av_enum, [0u, 1u]); - let lldiscrim_a = Load(cx, lldiscrim_a_ptr); - - // NB: we must hit the discriminant first so that structural - // comparison know not to proceed when the discriminants differ. - cx = f(cx, lldiscrim_a_ptr, ty::mk_int(cx.tcx())); - let unr_cx = sub_block(cx, ~"enum-iter-unr"); - Unreachable(unr_cx); - let llswitch = Switch(cx, lldiscrim_a, unr_cx.llbb, n_variants); - let next_cx = sub_block(cx, ~"enum-iter-next"); - for vec::each(*variants) |variant| { - let variant_cx = - sub_block(cx, - ~"enum-iter-variant-" + - int::to_str(variant.disr_val)); - AddCase(llswitch, C_int(ccx, variant.disr_val), variant_cx.llbb); - let variant_cx = - iter_variant(variant_cx, llunion_a_ptr, *variant, - /*bad*/copy (*substs).tps, tid, f); - Br(variant_cx, next_cx.llbb); - } - return next_cx; + let repr = adt::represent_type(ccx, t); + let variants = ty::enum_variants(ccx.tcx, tid); + let n_variants = (*variants).len(); + + // NB: we must hit the discriminant first so that structural + // comparison know not to proceed when the discriminants differ. + + match adt::trans_switch(cx, repr, av) { + (_match::single, None) => { + cx = iter_variant(cx, repr, av, variants[0], + substs.tps, f); + } + (_match::switch, Some(lldiscrim_a)) => { + cx = f(cx, lldiscrim_a, ty::mk_int(cx.tcx())); + let unr_cx = sub_block(cx, ~"enum-iter-unr"); + Unreachable(unr_cx); + let llswitch = Switch(cx, lldiscrim_a, unr_cx.llbb, + n_variants); + let next_cx = sub_block(cx, ~"enum-iter-next"); + + for vec::each(*variants) |variant| { + let variant_cx = + sub_block(cx, ~"enum-iter-variant-" + + int::to_str(variant.disr_val)); + let variant_cx = + iter_variant(variant_cx, repr, av, *variant, + substs.tps, f); + match adt::trans_case(cx, repr, variant.disr_val) { + _match::single_result(r) => { + AddCase(llswitch, r.val, variant_cx.llbb) + } + _ => ccx.sess.unimpl(~"value from adt::trans_case \ + in iter_structural_ty") + } + Br(variant_cx, next_cx.llbb); + } + cx = next_cx; + } + _ => ccx.sess.unimpl(~"value from adt::trans_switch \ + in iter_structural_ty") + } } _ => cx.sess().unimpl(~"type in iter_structural_ty") } @@ -828,30 +799,6 @@ pub fn trans_external_path(ccx: @CrateContext, did: ast::def_id, t: ty::t) }; } -pub fn get_discrim_val(cx: @CrateContext, span: span, enum_did: ast::def_id, - variant_did: ast::def_id) -> ValueRef { - // Can't use `discrims` from the crate context here because - // those discriminants have an extra level of indirection, - // and there's no LLVM constant load instruction. - let mut lldiscrim_opt = None; - for ty::enum_variants(cx.tcx, enum_did).each |variant_info| { - if variant_info.id == variant_did { - lldiscrim_opt = Some(C_int(cx, - variant_info.disr_val)); - break; - } - } - - match lldiscrim_opt { - None => { - cx.tcx.sess.span_bug(span, ~"didn't find discriminant?!"); - } - Some(found_lldiscrim) => { - found_lldiscrim - } - } -} - pub fn invoke(bcx: block, llfn: ValueRef, +llargs: ~[ValueRef]) -> block { let _icx = bcx.insn_ctxt("invoke_"); if bcx.unreachable { return bcx; } @@ -1885,7 +1832,6 @@ pub fn trans_enum_variant(ccx: @CrateContext, variant: ast::variant, args: &[ast::variant_arg], disr: int, - is_degen: bool, param_substs: Option<@param_substs>, llfndecl: ValueRef) { let _icx = ccx.insn_ctxt("trans_enum_variant"); @@ -1914,21 +1860,16 @@ pub fn trans_enum_variant(ccx: @CrateContext, let arg_tys = ty::ty_fn_args(node_id_type(bcx, variant.node.id)); let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys); - // Cast the enum to a type we can GEP into. - let llblobptr = if is_degen { - fcx.llretptr - } else { - let llenumptr = - PointerCast(bcx, fcx.llretptr, T_opaque_enum_ptr(ccx)); - let lldiscrimptr = GEPi(bcx, llenumptr, [0u, 0u]); - Store(bcx, C_int(ccx, disr), lldiscrimptr); - GEPi(bcx, llenumptr, [0u, 1u]) - }; - let t_id = local_def(enum_id); - let v_id = local_def(variant.node.id); + // XXX is there a better way to reconstruct the ty::t? + let enum_ty = ty::subst_tps(ccx.tcx, ty_param_substs, None, + ty::node_id_to_type(ccx.tcx, enum_id)); + let repr = adt::represent_type(ccx, enum_ty); + + adt::trans_start_init(bcx, repr, fcx.llretptr, disr); for vec::eachi(args) |i, va| { - let lldestptr = GEP_enum(bcx, llblobptr, t_id, v_id, - /*bad*/copy ty_param_substs, i); + let lldestptr = adt::trans_field_ptr(bcx, repr, fcx.llretptr, + disr, i); + // If this argument to this function is a enum, it'll have come in to // this function as an opaque blob due to the way that type_of() // works. So we have to cast to the destination's view of the type. @@ -1981,8 +1922,23 @@ pub fn trans_tuple_struct(ccx: @CrateContext, let arg_tys = ty::ty_fn_args(node_id_type(bcx, ctor_id)); let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys); + // XXX is there a better way to reconstruct the ty::t? + let ty_param_substs = match param_substs { + Some(ref substs) => /*bad*/copy substs.tys, + None => ~[] + }; + let ctor_ty = ty::subst_tps(ccx.tcx, ty_param_substs, None, + ty::node_id_to_type(ccx.tcx, ctor_id)); + let tup_ty = match ty::get(ctor_ty).sty { + ty::ty_bare_fn(ref bft) => bft.sig.output, + _ => ccx.sess.bug(fmt!("trans_tuple_struct: unexpected ctor \ + return type %s", + ty_to_str(ccx.tcx, ctor_ty))) + }; + let repr = adt::represent_type(ccx, tup_ty); + for fields.eachi |i, field| { - let lldestptr = GEPi(bcx, fcx.llretptr, [0, 0, i]); + let lldestptr = adt::trans_field_ptr(bcx, repr, fcx.llretptr, 0, i); let llarg = match fcx.llargs.get(&field.node.id) { local_mem(x) => x, _ => { @@ -2038,7 +1994,7 @@ pub fn trans_struct_dtor(ccx: @CrateContext, } pub fn trans_enum_def(ccx: @CrateContext, enum_definition: ast::enum_def, - id: ast::node_id, degen: bool, + id: ast::node_id, path: @ast_map::path, vi: @~[ty::VariantInfo], i: &mut uint) { for vec::each(enum_definition.variants) |variant| { @@ -2049,7 +2005,7 @@ pub fn trans_enum_def(ccx: @CrateContext, enum_definition: ast::enum_def, ast::tuple_variant_kind(ref args) if args.len() > 0 => { let llfn = get_item_val(ccx, variant.node.id); trans_enum_variant(ccx, id, *variant, /*bad*/copy *args, - disr_val, degen, None, llfn); + disr_val, None, llfn); } ast::tuple_variant_kind(_) => { // Nothing to do. @@ -2062,7 +2018,6 @@ pub fn trans_enum_def(ccx: @CrateContext, enum_definition: ast::enum_def, trans_enum_def(ccx, *enum_definition, id, - degen, path, vi, &mut *i); @@ -2113,11 +2068,10 @@ pub fn trans_item(ccx: @CrateContext, item: ast::item) { } ast::item_enum(ref enum_definition, ref generics) => { if !generics.is_type_parameterized() { - let degen = (*enum_definition).variants.len() == 1u; let vi = ty::enum_variants(ccx.tcx, local_def(item.id)); let mut i = 0; trans_enum_def(ccx, (*enum_definition), item.id, - degen, path, vi, &mut i); + path, vi, &mut i); } } ast::item_const(_, expr) => consts::trans_const(ccx, expr, item.id), @@ -3099,6 +3053,7 @@ pub fn trans_crate(sess: session::Session, module_data: HashMap(), lltypes: ty::new_ty_hash(), llsizingtypes: ty::new_ty_hash(), + adt_reprs: @mut LinearMap::new(), 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 2d7149fdfb2bb..a363a950f9b24 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -26,6 +26,7 @@ use lib; use metadata::common::LinkMeta; use middle::astencode; use middle::resolve; +use middle::trans::adt; use middle::trans::base; use middle::trans::build; use middle::trans::callee; @@ -44,7 +45,8 @@ use util::ppaux::{expr_repr, ty_to_str}; use core::cast; use core::hash; -use core::libc::c_uint; +use core::hashmap::linear::LinearMap; +use core::libc::{c_uint, c_longlong, c_ulonglong}; use core::ptr; use core::str; use core::to_bytes; @@ -203,6 +205,7 @@ pub struct CrateContext { module_data: HashMap<~str, ValueRef>, lltypes: HashMap, llsizingtypes: HashMap, + adt_reprs: @mut LinearMap, names: namegen, next_addrspace: addrspace_gen, symbol_hasher: @hash::State, @@ -1016,22 +1019,6 @@ pub fn T_chan(cx: @CrateContext, _t: TypeRef) -> TypeRef { pub fn T_taskptr(cx: @CrateContext) -> TypeRef { return T_ptr(cx.task_type); } -// This type must never be used directly; it must always be cast away. -pub fn T_typaram(tn: @TypeNames) -> TypeRef { - let s = @"typaram"; - match name_has_type(tn, s) { - Some(t) => return t, - _ => () - } - let t = T_i8(); - associate_type(tn, s, t); - return t; -} - -pub fn T_typaram_ptr(tn: @TypeNames) -> TypeRef { - return T_ptr(T_typaram(tn)); -} - pub fn T_opaque_cbox_ptr(cx: @CrateContext) -> TypeRef { // closures look like boxes (even when they are ~fn or &fn) // see trans_closure.rs @@ -1042,21 +1029,6 @@ pub fn T_enum_discrim(cx: @CrateContext) -> TypeRef { return cx.int_type; } -pub fn T_opaque_enum(cx: @CrateContext) -> TypeRef { - let s = @"opaque_enum"; - match name_has_type(cx.tn, s) { - Some(t) => return t, - _ => () - } - let t = T_struct(~[T_enum_discrim(cx), T_i8()]); - associate_type(cx.tn, s, t); - return t; -} - -pub fn T_opaque_enum_ptr(cx: @CrateContext) -> TypeRef { - return T_ptr(T_opaque_enum(cx)); -} - pub fn T_captured_tydescs(cx: @CrateContext, n: uint) -> TypeRef { return T_struct(vec::from_elem::(n, T_ptr(cx.tydesc_type))); } @@ -1087,6 +1059,12 @@ pub fn C_null(t: TypeRef) -> ValueRef { } } +pub fn C_undef(t: TypeRef) -> ValueRef { + unsafe { + return llvm::LLVMGetUndef(t); + } +} + pub fn C_integral(t: TypeRef, u: u64, sign_extend: Bool) -> ValueRef { unsafe { return llvm::LLVMConstInt(t, u, sign_extend); @@ -1254,6 +1232,38 @@ pub fn get_param(fndecl: ValueRef, param: uint) -> ValueRef { } } +pub fn const_get_elt(cx: @CrateContext, v: ValueRef, us: &[c_uint]) + -> ValueRef { + unsafe { + let r = do vec::as_imm_buf(us) |p, len| { + llvm::LLVMConstExtractValue(v, p, len as c_uint) + }; + + debug!("const_get_elt(v=%s, us=%?, r=%s)", + val_str(cx.tn, v), us, val_str(cx.tn, r)); + + return r; + } +} + +pub fn const_to_int(v: ValueRef) -> c_longlong { + unsafe { + llvm::LLVMConstIntGetSExtValue(v) + } +} + +pub fn const_to_uint(v: ValueRef) -> c_ulonglong { + unsafe { + llvm::LLVMConstIntGetZExtValue(v) + } +} + +pub fn is_undef(val: ValueRef) -> bool { + unsafe { + llvm::LLVMIsUndef(val) != False + } +} + // Used to identify cached monomorphized functions and vtables #[deriving_eq] pub enum mono_param_id { @@ -1430,18 +1440,6 @@ pub fn dummy_substs(+tps: ~[ty::t]) -> ty::substs { } } -pub fn struct_field(index: uint) -> [uint * 3] { - //! The GEPi sequence to access a field of a record/struct. - - [0, 0, index] -} - -pub fn struct_dtor() -> [uint * 2] { - //! The GEPi sequence to access the dtor of a struct. - - [0, 1] -} - // Casts a Rust bool value to an i1. pub fn bool_to_i1(bcx: block, llval: ValueRef) -> ValueRef { build::ICmp(bcx, lib::llvm::IntNE, llval, C_bool(false)) diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index e585e433ef239..d19ffe8cb211f 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -12,6 +12,7 @@ use core::prelude::*; use lib::llvm::{llvm, ValueRef, TypeRef, Bool, True, False}; use middle::const_eval; +use middle::trans::adt; use middle::trans::base; use middle::trans::base::get_insn_ctxt; use middle::trans::common::*; @@ -103,20 +104,6 @@ pub fn const_deref(cx: @CrateContext, v: ValueRef) -> ValueRef { } } -pub fn const_get_elt(cx: @CrateContext, v: ValueRef, us: &[c_uint]) - -> ValueRef { - unsafe { - let r = do vec::as_imm_buf(us) |p, len| { - llvm::LLVMConstExtractValue(v, p, len as c_uint) - }; - - debug!("const_get_elt(v=%s, us=%?, r=%s)", - val_str(cx.tn, v), us, val_str(cx.tn, r)); - - return r; - } -} - pub fn const_autoderef(cx: @CrateContext, ty: ty::t, v: ValueRef) -> (ty::t, ValueRef) { let mut t1 = ty; @@ -253,16 +240,12 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { } ast::expr_field(base, field, _) => { let bt = ty::expr_ty(cx.tcx, base); + let brepr = adt::represent_type(cx, bt); let bv = const_expr(cx, base); let (bt, bv) = const_autoderef(cx, bt, bv); - do expr::with_field_tys(cx.tcx, bt, None) |_, field_tys| { + do expr::with_field_tys(cx.tcx, bt, None) |discr, field_tys| { let ix = ty::field_idx_strict(cx.tcx, field, field_tys); - - // Note: ideally, we'd use `struct_field()` here instead - // of hardcoding [0, ix], but we can't because it yields - // the wrong type and also inserts an extra 0 that is - // not needed in the constant variety: - const_get_elt(cx, bv, [0, ix as c_uint]) + adt::const_get_field(cx, brepr, bv, discr, ix) } } @@ -342,24 +325,8 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { } (expr::cast_enum, expr::cast_integral) | (expr::cast_enum, expr::cast_float) => { - let def = ty::resolve_expr(cx.tcx, base); - let (enum_did, variant_did) = match def { - ast::def_variant(enum_did, variant_did) => { - (enum_did, variant_did) - } - _ => cx.sess.bug(~"enum cast source is not enum") - }; - // Note that we know this is a C-like (nullary) enum - // variant or we wouldn't have gotten here - let variants = ty::enum_variants(cx.tcx, enum_did); - let iv = if variants.len() == 1 { - // Univariants don't have a discriminant field, - // because there's only one value it could have: - C_integral(T_i64(), - variants[0].disr_val as u64, True) - } else { - base::get_discrim_val(cx, e.span, enum_did, variant_did) - }; + let repr = adt::represent_type(cx, basety); + let iv = C_int(cx, adt::const_get_discrim(cx, repr, v)); let ety_cast = expr::cast_type_kind(ety); match ety_cast { expr::cast_integral => { @@ -387,18 +354,22 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { gv } ast::expr_tup(es) => { - C_struct(es.map(|e| const_expr(cx, *e))) + let ety = ty::expr_ty(cx.tcx, e); + let repr = adt::represent_type(cx, ety); + adt::trans_const(cx, repr, 0, es.map(|e| const_expr(cx, *e))) } ast::expr_rec(ref fs, None) => { - C_struct([C_struct( - (*fs).map(|f| const_expr(cx, f.node.expr)))]) + let ety = ty::expr_ty(cx.tcx, e); + let repr = adt::represent_type(cx, ety); + adt::trans_const(cx, repr, 0, + fs.map(|f| const_expr(cx, f.node.expr))) } - ast::expr_struct(_, ref fs, _) => { + ast::expr_struct(_, ref fs, None) => { let ety = ty::expr_ty(cx.tcx, e); - let cs = do expr::with_field_tys(cx.tcx, - ety, - None) |_hd, field_tys| { - field_tys.map(|field_ty| { + let repr = adt::represent_type(cx, ety); + do expr::with_field_tys(cx.tcx, ety, Some(e.id)) + |discr, field_tys| { + let cs = field_tys.map(|field_ty| { match fs.find(|f| field_ty.ident == f.node.ident) { Some(ref f) => const_expr(cx, (*f).node.expr), None => { @@ -406,9 +377,9 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { e.span, ~"missing struct field"); } } - }) - }; - C_struct([C_struct(cs)]) + }); + adt::trans_const(cx, repr, discr, cs) + } } ast::expr_vec(es, ast::m_imm) => { let (v, _, _) = const_vec(cx, e, es); @@ -466,25 +437,12 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { get_const_val(cx, def_id) } Some(ast::def_variant(enum_did, variant_did)) => { - // Note that we know this is a C-like (nullary) enum - // variant or we wouldn't have gotten here -- the constant - // checker forbids paths that don't map to C-like enum - // variants. - if ty::enum_is_univariant(cx.tcx, enum_did) { - // Univariants have no discriminant field. - C_struct(~[]) - } else { - let lldiscrim = base::get_discrim_val(cx, e.span, - enum_did, - variant_did); - // However, we still have to pad it out to the - // size of the full enum; see the expr_call case, - // below. let ety = ty::expr_ty(cx.tcx, e); - let size = machine::static_size_of_enum(cx, ety); - let padding = C_null(T_array(T_i8(), size)); - C_struct(~[lldiscrim, padding]) - } + let repr = adt::represent_type(cx, ety); + let vinfo = ty::enum_variant_with_id(cx.tcx, + enum_did, + variant_did); + adt::trans_const(cx, repr, vinfo.disr_val, []) } Some(ast::def_struct(_)) => { let ety = ty::expr_ty(cx.tcx, e); @@ -492,52 +450,31 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { C_null(llty) } _ => { - cx.sess.span_bug(e.span, - ~"expected a const, fn, or variant def") + cx.sess.span_bug(e.span, ~"expected a const, fn, \ + struct, or variant def") } } } ast::expr_call(callee, args, _) => { - match cx.tcx.def_map.find(&callee.id) { - Some(ast::def_struct(def_id)) => { - let llstructbody = - C_struct(args.map(|a| const_expr(cx, *a))); - if ty::ty_dtor(cx.tcx, def_id).is_present() { - C_struct(~[ llstructbody, C_u8(0) ]) - } else { - C_struct(~[ llstructbody ]) - } - } - Some(ast::def_variant(tid, vid)) => { - let ety = ty::expr_ty(cx.tcx, e); - let univar = ty::enum_is_univariant(cx.tcx, tid); - let size = machine::static_size_of_enum(cx, ety); - - let discrim = base::get_discrim_val(cx, e.span, tid, vid); - let c_args = C_struct(args.map(|a| const_expr(cx, *a))); - - // FIXME (#1645): enum body alignment is generaly wrong. - if !univar { - // Pad out the data to the size of its type_of; - // this is necessary if the enum is contained - // within an aggregate (tuple, struct, vector) so - // that the next element is at the right offset. - let actual_size = - machine::llsize_of_real(cx, llvm::LLVMTypeOf(c_args)); - let padding = - C_null(T_array(T_i8(), size - actual_size)); - // A packed_struct has an alignment of 1; thus, - // wrapping one around c_args will misalign it the - // same way we normally misalign enum bodies - // without affecting its internal alignment or - // changing the alignment of the enum. - C_struct(~[discrim, C_packed_struct(~[c_args]), padding]) - } else { - C_struct(~[c_args]) - } - } - _ => cx.sess.span_bug(e.span, ~"expected a struct def") - } + match cx.tcx.def_map.find(&callee.id) { + Some(ast::def_struct(_)) => { + let ety = ty::expr_ty(cx.tcx, e); + let repr = adt::represent_type(cx, ety); + adt::trans_const(cx, repr, 0, + args.map(|a| const_expr(cx, *a))) + } + Some(ast::def_variant(enum_did, variant_did)) => { + let ety = ty::expr_ty(cx.tcx, e); + let repr = adt::represent_type(cx, ety); + let vinfo = ty::enum_variant_with_id(cx.tcx, + enum_did, + variant_did); + adt::trans_const(cx, repr, vinfo.disr_val, + args.map(|a| const_expr(cx, *a))) + } + _ => cx.sess.span_bug(e.span, ~"expected a struct or \ + variant def") + } } ast::expr_paren(e) => { return const_expr(cx, e); } _ => cx.sess.span_bug(e.span, diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index ce472fd9f1fdb..e608991456417 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -90,6 +90,7 @@ use core::prelude::*; use lib; use lib::llvm::ValueRef; use middle::borrowck::{RootInfo, root_map_key}; +use middle::trans::adt; use middle::trans::base::*; use middle::trans::build::*; use middle::trans::callee; @@ -511,14 +512,13 @@ pub impl Datum { } } - fn GEPi(&self, bcx: block, - ixs: &[uint], - ty: ty::t, - source: DatumCleanup) - -> Datum { + fn get_element(&self, bcx: block, + ty: ty::t, + source: DatumCleanup, + gep: fn(ValueRef) -> ValueRef) -> Datum { let base_val = self.to_ref_llval(bcx); Datum { - val: GEPi(bcx, base_val, ixs), + val: gep(base_val), mode: ByRef, ty: ty, source: source @@ -678,15 +678,17 @@ pub impl Datum { return (None, bcx); } + let repr = adt::represent_type(ccx, self.ty); + assert adt::is_newtypeish(repr); let ty = ty::subst(ccx.tcx, substs, variants[0].args[0]); return match self.mode { ByRef => { // Recast lv.val as a pointer to the newtype // rather than a ptr to the enum type. - let llty = T_ptr(type_of::type_of(ccx, ty)); ( Some(Datum { - val: PointerCast(bcx, self.val, llty), + val: adt::trans_field_ptr(bcx, repr, self.val, + 0, 0), ty: ty, mode: ByRef, source: ZeroMem @@ -716,6 +718,8 @@ pub impl Datum { return (None, bcx); } + let repr = adt::represent_type(ccx, self.ty); + assert adt::is_newtypeish(repr); let ty = fields[0].mt.ty; return match self.mode { ByRef => { @@ -725,7 +729,8 @@ pub impl Datum { // destructors. ( Some(Datum { - val: GEPi(bcx, self.val, [0, 0, 0]), + val: adt::trans_field_ptr(bcx, repr, self.val, + 0, 0), ty: ty, mode: ByRef, source: ZeroMem diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index d41bf3571938e..1dae9fccc6211 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -126,6 +126,7 @@ use lib; use lib::llvm::{ValueRef, TypeRef, llvm, True}; use middle::borrowck::root_map_key; use middle::trans::_match; +use middle::trans::adt; use middle::trans::base; use middle::trans::base::*; use middle::trans::build::*; @@ -602,7 +603,9 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr, return trans_rec_or_struct(bcx, (*fields), base, expr.id, dest); } ast::expr_tup(ref args) => { - return trans_tup(bcx, *args, dest); + let repr = adt::represent_type(bcx.ccx(), expr_ty(bcx, expr)); + return trans_adt(bcx, repr, 0, args.mapi(|i, arg| (i, *arg)), + None, dest); } ast::expr_lit(@codemap::spanned {node: ast::lit_str(s), _}) => { return tvec::trans_lit_str(bcx, expr, s, dest); @@ -719,14 +722,12 @@ fn trans_def_dps_unadjusted(bcx: block, ref_expr: @ast::expr, let fn_data = callee::trans_fn_ref(bcx, vid, ref_expr.id); Store(bcx, fn_data.llfn, lldest); return bcx; - } else if !ty::enum_is_univariant(ccx.tcx, tid) { - // Nullary variant. - let lldiscrimptr = GEPi(bcx, lldest, [0u, 0u]); - let lldiscrim = C_int(bcx.ccx(), variant_info.disr_val); - Store(bcx, lldiscrim, lldiscrimptr); - return bcx; } else { - // Nullary univariant. + // Nullary variant. + let ty = expr_ty(bcx, ref_expr); + let repr = adt::represent_type(ccx, ty); + adt::trans_start_init(bcx, repr, lldest, + variant_info.disr_val); return bcx; } } @@ -883,13 +884,15 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock { let _icx = bcx.insn_ctxt("trans_rec_field"); let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base)); - do with_field_tys(bcx.tcx(), base_datum.ty, None) |_dtor, field_tys| { + let repr = adt::represent_type(bcx.ccx(), base_datum.ty); + do with_field_tys(bcx.tcx(), base_datum.ty, None) |discr, field_tys| { let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys); DatumBlock { - datum: base_datum.GEPi(bcx, - [0u, 0u, ix], - field_tys[ix].mt.ty, - ZeroMem), + datum: do base_datum.get_element(bcx, + field_tys[ix].mt.ty, + ZeroMem) |srcval| { + adt::trans_field_ptr(bcx, repr, srcval, discr, ix) + }, bcx: bcx } } @@ -1096,15 +1099,14 @@ pub fn trans_local_var(bcx: block, def: ast::def) -> Datum { pub fn with_field_tys(tcx: ty::ctxt, ty: ty::t, node_id_opt: Option, - op: fn(bool, (&[ty::field])) -> R) -> R { + op: fn(int, (&[ty::field])) -> R) -> R { match ty::get(ty).sty { ty::ty_rec(ref fields) => { - op(false, *fields) + op(0, *fields) } ty::ty_struct(did, ref substs) => { - let has_dtor = ty::ty_dtor(tcx, did).is_present(); - op(has_dtor, struct_mutable_fields(tcx, did, substs)) + op(0, struct_mutable_fields(tcx, did, substs)) } ty::ty_enum(_, ref substs) => { @@ -1118,8 +1120,10 @@ pub fn with_field_tys(tcx: ty::ctxt, } Some(node_id) => { match tcx.def_map.get(&node_id) { - ast::def_variant(_, variant_id) => { - op(false, struct_mutable_fields( + ast::def_variant(enum_id, variant_id) => { + let variant_info = ty::enum_variant_with_id( + tcx, enum_id, variant_id); + op(variant_info.disr_val, struct_mutable_fields( tcx, variant_id, substs)) } _ => { @@ -1148,133 +1152,122 @@ fn trans_rec_or_struct(bcx: block, let _icx = bcx.insn_ctxt("trans_rec"); let mut bcx = bcx; - // Handle the case where the result is ignored. - let addr; - match dest { - SaveIn(p) => { - addr = p; - } - Ignore => { - // just evaluate the values for each field and drop them - // on the floor - for vec::each(fields) |fld| { - bcx = trans_into(bcx, fld.node.expr, Ignore); - } - return bcx; - } - } - - // If this is a struct-like variant, write in the discriminant if - // necessary, position the address at the right location, and cast the - // address. let ty = node_id_type(bcx, id); let tcx = bcx.tcx(); - let addr = match ty::get(ty).sty { - ty::ty_enum(_, ref substs) => { - match tcx.def_map.get(&id) { - ast::def_variant(enum_id, variant_id) => { - let variant_info = ty::enum_variant_with_id( - tcx, enum_id, variant_id); - let addr = if ty::enum_is_univariant(tcx, enum_id) { - addr - } else { - Store(bcx, - C_int(bcx.ccx(), variant_info.disr_val), - GEPi(bcx, addr, [0, 0])); - GEPi(bcx, addr, [0, 1]) - }; - let fields = ty::struct_mutable_fields( - tcx, variant_id, substs); - let field_lltys = do fields.map |field| { - type_of::type_of(bcx.ccx(), - ty::subst_tps( - tcx, substs.tps, None, field.mt.ty)) - }; - PointerCast(bcx, addr, - T_ptr(T_struct(~[T_struct(field_lltys)]))) + do with_field_tys(tcx, ty, Some(id)) |discr, field_tys| { + let mut need_base = vec::from_elem(field_tys.len(), true); + + let numbered_fields = do fields.map |field| { + let opt_pos = vec::position(field_tys, |field_ty| + field_ty.ident == field.node.ident); + match opt_pos { + Some(i) => { + need_base[i] = false; + (i, field.node.expr) } - _ => { - tcx.sess.bug(~"resolve didn't write the right def in for \ - this struct-like variant") + None => { + tcx.sess.span_bug(field.span, + ~"Couldn't find field in struct type") } } - } - _ => addr - }; - - do with_field_tys(tcx, ty, Some(id)) |has_dtor, field_tys| { - // evaluate each of the fields and store them into their - // correct locations - let mut temp_cleanups = ~[]; - for fields.each |field| { - let ix = ty::field_idx_strict(tcx, field.node.ident, field_tys); - let dest = GEPi(bcx, addr, struct_field(ix)); - bcx = trans_into(bcx, field.node.expr, SaveIn(dest)); - add_clean_temp_mem(bcx, dest, field_tys[ix].mt.ty); - temp_cleanups.push(dest); - } - - // copy over any remaining fields from the base (for - // functional record update) - for base.each |base_expr| { - let base_datum = unpack_datum!( - bcx, trans_to_datum(bcx, *base_expr)); - - // Copy/move over inherited fields - for field_tys.eachi |i, field_ty| { - if !fields.any(|f| f.node.ident == field_ty.ident) { - let dest = GEPi(bcx, addr, struct_field(i)); - let base_field = - base_datum.GEPi(bcx, - struct_field(i), - field_ty.mt.ty, - ZeroMem); - bcx = base_field.store_to(bcx, base_expr.id, INIT, dest); + }; + let optbase = match base { + Some(base_expr) => { + let mut leftovers = ~[]; + for need_base.eachi |i, b| { + if *b { + leftovers.push((i, field_tys[i].mt.ty)) + } } + Some(StructBaseInfo {expr: base_expr, + fields: leftovers }) } - } - - // Add the drop flag if necessary. - if has_dtor { - let dest = GEPi(bcx, addr, struct_dtor()); - Store(bcx, C_u8(1), dest); - } + None => { + if need_base.any(|b| *b) { + // XXX should be span bug + tcx.sess.bug(~"missing fields and no base expr") + } + None + } + }; - // Now revoke the cleanups as we pass responsibility for the data - // structure on to the caller - for temp_cleanups.each |cleanup| { - revoke_clean(bcx, *cleanup); - } - bcx + let repr = adt::represent_type(bcx.ccx(), ty); + trans_adt(bcx, repr, discr, numbered_fields, optbase, dest) } } -fn trans_tup(bcx: block, elts: &[@ast::expr], dest: Dest) -> block { - let _icx = bcx.insn_ctxt("trans_tup"); +/** + * Information that `trans_adt` needs in order to fill in the fields + * of a struct copied from a base struct (e.g., from an expression + * like `Foo { a: b, ..base }`. + * + * Note that `fields` may be empty; the base expression must always be + * evaluated for side-effects. + */ +struct StructBaseInfo { + /// The base expression; will be evaluated after all explicit fields. + expr: @ast::expr, + /// The indices of fields to copy paired with their types. + fields: ~[(uint, ty::t)] +} + +/** + * Constructs an ADT instance: + * + * - `fields` should be a list of field indices paired with the + * expression to store into that field. The initializers will be + * evaluated in the order specified by `fields`. + * + * - `optbase` contains information on the base struct (if any) from + * which remaining fields are copied; see comments on `StructBaseInfo`. + */ +fn trans_adt(bcx: block, repr: &adt::Repr, discr: int, + fields: &[(uint, @ast::expr)], + optbase: Option, + dest: Dest) -> block { + let _icx = bcx.insn_ctxt("trans_adt"); let mut bcx = bcx; let addr = match dest { Ignore => { - for vec::each(elts) |ex| { - bcx = trans_into(bcx, *ex, Ignore); + for fields.each |&(_i, e)| { + bcx = trans_into(bcx, e, Ignore); + } + for optbase.each |sbi| { + bcx = trans_into(bcx, sbi.expr, Ignore); } return bcx; } - SaveIn(pos) => pos, + SaveIn(pos) => pos }; let mut temp_cleanups = ~[]; - for vec::eachi(elts) |i, e| { - let dest = GEPi(bcx, addr, [0u, i]); - let e_ty = expr_ty(bcx, *e); - bcx = trans_into(bcx, *e, SaveIn(dest)); + adt::trans_start_init(bcx, repr, addr, discr); + for fields.each |&(i, e)| { + let dest = adt::trans_field_ptr(bcx, repr, addr, discr, i); + let e_ty = expr_ty(bcx, e); + bcx = trans_into(bcx, e, SaveIn(dest)); add_clean_temp_mem(bcx, dest, e_ty); temp_cleanups.push(dest); } + for optbase.each |base| { + // XXX is it sound to use the destination's repr on the base? + // XXX would it ever be reasonable to be here with discr != 0? + let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base.expr)); + for base.fields.each |&(i, t)| { + let datum = do base_datum.get_element(bcx, t, ZeroMem) |srcval| { + adt::trans_field_ptr(bcx, repr, srcval, discr, i) + }; + let dest = adt::trans_field_ptr(bcx, repr, addr, discr, i); + bcx = datum.store_to(bcx, base.expr.id, INIT, dest); + } + } + for vec::each(temp_cleanups) |cleanup| { revoke_clean(bcx, *cleanup); } return bcx; } + fn trans_immediate_lit(bcx: block, expr: @ast::expr, lit: ast::lit) -> DatumBlock { // must not be a string constant, that is a RvalueDpsExpr @@ -1671,22 +1664,8 @@ fn trans_imm_cast(bcx: block, expr: @ast::expr, (cast_enum, cast_integral) | (cast_enum, cast_float) => { let bcx = bcx; - let in_tid = match ty::get(t_in).sty { - ty::ty_enum(did, _) => did, - _ => ccx.sess.bug(~"enum cast source is not enum") - }; - let variants = ty::enum_variants(ccx.tcx, in_tid); - let lldiscrim_a = if variants.len() == 1 { - // Univariants don't have a discriminant field, - // because there's only one value it could have: - C_integral(T_enum_discrim(ccx), - variants[0].disr_val as u64, True) - } else { - let llenumty = T_opaque_enum_ptr(ccx); - let av_enum = PointerCast(bcx, llexpr, llenumty); - let lldiscrim_a_ptr = GEPi(bcx, av_enum, [0u, 0u]); - Load(bcx, lldiscrim_a_ptr) - }; + let repr = adt::represent_type(ccx, t_in); + let lldiscrim_a = adt::trans_get_discr(bcx, repr, llexpr); match k_out { cast_integral => int_cast(bcx, ll_t_out, val_ty(lldiscrim_a), diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index 7bed3e86190a2..b692ae67950ec 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -19,6 +19,7 @@ use back::link::*; use driver::session; use lib; use lib::llvm::{llvm, ValueRef, TypeRef, True}; +use middle::trans::adt; use middle::trans::base::*; use middle::trans::callee; use middle::trans::closure; @@ -447,10 +448,10 @@ pub fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) { match ty::ty_dtor(bcx.tcx(), did) { ty::NoDtor => bcx, ty::LegacyDtor(ref dt_id) => { - trans_struct_drop(bcx, v, *dt_id, did, substs, false) + trans_struct_drop(bcx, t, v, *dt_id, did, substs, false) } ty::TraitDtor(ref dt_id) => { - trans_struct_drop(bcx, v, *dt_id, did, substs, true) + trans_struct_drop(bcx, t, v, *dt_id, did, substs, true) } } } @@ -460,13 +461,15 @@ pub fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) { } pub fn trans_struct_drop(bcx: block, + t: ty::t, v0: ValueRef, dtor_did: ast::def_id, class_did: ast::def_id, substs: &ty::substs, take_ref: bool) -> block { - let drop_flag = GEPi(bcx, v0, struct_dtor()); + let repr = adt::represent_type(bcx.ccx(), t); + let drop_flag = adt::trans_drop_flag_ptr(bcx, repr, v0); do with_cond(bcx, IsNotNull(bcx, Load(bcx, drop_flag))) |cx| { let mut bcx = cx; @@ -504,7 +507,7 @@ pub fn trans_struct_drop(bcx: block, ty::struct_mutable_fields(bcx.tcx(), class_did, substs); for vec::eachi(field_tys) |i, fld| { - let llfld_a = GEPi(bcx, v0, struct_field(i)); + let llfld_a = adt::trans_field_ptr(bcx, repr, v0, 0, i); bcx = drop_ty(bcx, llfld_a, fld.mt.ty); } @@ -534,10 +537,10 @@ pub fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) { let tcx = bcx.tcx(); match ty::ty_dtor(tcx, did) { ty::TraitDtor(dtor) => { - trans_struct_drop(bcx, v0, dtor, did, substs, true) + trans_struct_drop(bcx, t, v0, dtor, did, substs, true) } ty::LegacyDtor(dtor) => { - trans_struct_drop(bcx, v0, dtor, did, substs, false) + trans_struct_drop(bcx, t, v0, dtor, did, substs, false) } ty::NoDtor => { // No dtor? Just the default case diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index c0af1f4fad2a7..ffc5d132c9fd8 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -196,8 +196,7 @@ pub fn monomorphic_fn(ccx: @CrateContext, match (*v).node.kind { ast::tuple_variant_kind(ref args) => { trans_enum_variant(ccx, enum_item.id, *v, /*bad*/copy *args, - this_tv.disr_val, tvs.len() == 1u, - psubsts, d); + this_tv.disr_val, psubsts, d); } ast::struct_variant_kind(_) => ccx.tcx.sess.bug(~"can't monomorphize struct variants"), diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index 26cf91b03e145..8dac607bd5287 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -11,15 +11,14 @@ use lib::llvm::llvm; use lib::llvm::{TypeRef}; +use middle::trans::adt; use middle::trans::base; use middle::trans::common::*; use middle::trans::common; -use middle::trans::machine; use middle::ty; use util::ppaux; use core::option::None; -use core::vec; use syntax::ast; pub fn type_of_explicit_arg(ccx: @CrateContext, arg: ty::arg) -> TypeRef { @@ -143,32 +142,12 @@ pub fn sizing_type_of(cx: @CrateContext, t: ty::t) -> TypeRef { 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_tup(*) | ty::ty_rec(*) | ty::ty_struct(*) + | ty::ty_enum(*) => { + let repr = adt::represent_type(cx, t); + T_struct(adt::sizing_fields_of(cx, repr)) } - 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( fmt!("fictitious type %? in sizing_type_of()", @@ -257,28 +236,13 @@ pub fn type_of(cx: @CrateContext, t: ty::t) -> TypeRef { T_array(type_of(cx, mt.ty), n) } - ty::ty_rec(fields) => { - let mut tys: ~[TypeRef] = ~[]; - for vec::each(fields) |f| { - let mt_ty = f.mt.ty; - tys.push(type_of(cx, mt_ty)); - } - - // n.b.: introduce an extra layer of indirection to match - // structs - T_struct(~[T_struct(tys)]) - } - ty::ty_bare_fn(_) => T_ptr(type_of_fn_from_ty(cx, t)), ty::ty_closure(_) => T_fn_pair(cx, type_of_fn_from_ty(cx, t)), ty::ty_trait(_, _, vstore) => T_opaque_trait(cx, vstore), ty::ty_type => T_ptr(cx.tydesc_type), - ty::ty_tup(elts) => { - let mut tys = ~[]; - for vec::each(elts) |elt| { - tys.push(type_of(cx, *elt)); - } - T_struct(tys) + ty::ty_tup(*) | ty::ty_rec(*) => { + let repr = adt::represent_type(cx, t); + T_struct(adt::fields_of(cx, repr)) } ty::ty_opaque_closure_ptr(_) => T_opaque_box_ptr(cx), ty::ty_struct(did, ref substs) => { @@ -301,24 +265,9 @@ pub fn type_of(cx: @CrateContext, t: ty::t) -> TypeRef { // If this was an enum or struct, fill in the type now. match ty::get(t).sty { - ty::ty_enum(did, _) => { - fill_type_of_enum(cx, did, t, llty); - } - ty::ty_struct(did, ref substs) => { - // Only instance vars are record fields at runtime. - let fields = ty::lookup_struct_fields(cx.tcx, did); - let mut tys = do vec::map(fields) |f| { - let t = ty::lookup_field_type(cx.tcx, did, f.id, substs); - type_of(cx, t) - }; - - // include a byte flag if there is a dtor so that we know when we've - // been dropped - if ty::ty_dtor(cx.tcx, did).is_present() { - common::set_struct_body(llty, ~[T_struct(tys), T_i8()]); - } else { - common::set_struct_body(llty, ~[T_struct(tys)]); - } + ty::ty_enum(*) | ty::ty_struct(*) => { + let repr = adt::represent_type(cx, t); + common::set_struct_body(llty, adt::fields_of(cx, repr)); } _ => () } @@ -326,34 +275,6 @@ pub fn type_of(cx: @CrateContext, t: ty::t) -> TypeRef { return llty; } -pub fn enum_body_types(cx: @CrateContext, did: ast::def_id, t: ty::t) - -> ~[TypeRef] { - let univar = ty::enum_is_univariant(cx.tcx, did); - if !univar { - let size = machine::static_size_of_enum(cx, t); - ~[T_enum_discrim(cx), T_array(T_i8(), size)] - } - else { - // Use the actual fields, so we get the alignment right. - match ty::get(t).sty { - ty::ty_enum(_, ref substs) => { - do ty::enum_variants(cx.tcx, did)[0].args.map |&field_ty| { - sizing_type_of(cx, ty::subst(cx.tcx, substs, field_ty)) - } - } - _ => cx.sess.bug(~"enum is not an enum") - } - } -} - -pub fn fill_type_of_enum(cx: @CrateContext, - did: ast::def_id, - t: ty::t, - llty: TypeRef) { - debug!("type_of_enum %?: %?", t, ty::get(t)); - common::set_struct_body(llty, enum_body_types(cx, did, t)); -} - // Want refinements! (Or case classes, I guess pub enum named_ty { a_struct, an_enum } diff --git a/src/librustc/rustc.rc b/src/librustc/rustc.rc index b204c458d659e..355ecaed7d7e0 100644 --- a/src/librustc/rustc.rc +++ b/src/librustc/rustc.rc @@ -65,6 +65,7 @@ pub mod middle { pub mod type_use; pub mod reachable; pub mod machine; + pub mod adt; } pub mod ty; pub mod resolve; diff --git a/src/test/run-pass/const-enum-structlike.rs b/src/test/run-pass/const-enum-structlike.rs new file mode 100644 index 0000000000000..83d8174075938 --- /dev/null +++ b/src/test/run-pass/const-enum-structlike.rs @@ -0,0 +1,23 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +enum E { + S0 { s: ~str }, + S1 { u: uint } +} + +const C: E = S1 { u: 23 }; + +fn main() { + match C { + S0 { _ } => fail!(), + S1 { u } => assert u == 23 + } +} diff --git a/src/test/run-pass/enum-discrim-range-overflow.rs b/src/test/run-pass/enum-discrim-range-overflow.rs new file mode 100644 index 0000000000000..a6806fba14269 --- /dev/null +++ b/src/test/run-pass/enum-discrim-range-overflow.rs @@ -0,0 +1,31 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub enum E64 { + H64 = 0x7FFF_FFFF_FFFF_FFFF, + L64 = 0x8000_0000_0000_0000 +} +pub enum E32 { + H32 = 0x7FFF_FFFF, + L32 = 0x8000_0000 +} + +pub fn f(e64: E64, e32: E32) -> (bool,bool) { + (match e64 { + H64 => true, + L64 => false + }, + match e32 { + H32 => true, + L32 => false + }) +} + +pub fn main() { } diff --git a/src/test/run-pass/struct-order-of-eval-1.rs b/src/test/run-pass/struct-order-of-eval-1.rs new file mode 100644 index 0000000000000..db7c73cbfc5ea --- /dev/null +++ b/src/test/run-pass/struct-order-of-eval-1.rs @@ -0,0 +1,16 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct S { f0: ~str, f1: int } + +pub fn main() { + let s = ~"Hello, world!"; + let _s = S { f0: str::from_slice(s), ..S { f0: s, f1: 23 } }; +} diff --git a/src/test/run-pass/struct-order-of-eval-2.rs b/src/test/run-pass/struct-order-of-eval-2.rs new file mode 100644 index 0000000000000..413f185659a6f --- /dev/null +++ b/src/test/run-pass/struct-order-of-eval-2.rs @@ -0,0 +1,16 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct S { f0: ~str, f1: ~str } + +pub fn main() { + let s = ~"Hello, world!"; + let _s = S { f1: str::from_slice(s), f0: s }; +}