auto merge of #5183 : jld/rust/adt-renovation, r=brson

This series of changes moves the representation details of algebraic datatypes (enums, and special cases like structs and tuples and (until they're fully removed) records) from various places around rustc::middle::trans into a single module, to enable future improvements in this area.

r?(@nikomatsakis), and the core developers in general; this seems like a “super-review” kind of change.
This commit is contained in:
bors 2013-03-06 20:57:37 -08:00
commit 194c27bcd1
16 changed files with 1040 additions and 627 deletions

View File

@ -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,

View File

@ -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<Opt>, 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<Opt>, +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,

View File

@ -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 <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.
/*!
* # 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<ValueRef>) {
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
}
}

View File

@ -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();
let ccx = cx.ccx();
// 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 repr = adt::represent_type(ccx, t);
let variants = ty::enum_variants(ccx.tcx, tid);
let n_variants = (*variants).len();
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.
// 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;
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,

View File

@ -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<ty::t, TypeRef>,
llsizingtypes: HashMap<ty::t, TypeRef>,
adt_reprs: @mut LinearMap<ty::t, @adt::Repr>,
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::<TypeRef>(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))

View File

@ -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)))])
}
ast::expr_struct(_, ref fs, _) => {
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);
adt::trans_const(cx, repr, 0,
fs.map(|f| const_expr(cx, f.node.expr)))
}
ast::expr_struct(_, ref fs, None) => {
let ety = ty::expr_ty(cx.tcx, e);
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,

View File

@ -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

View File

@ -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<R>(tcx: ty::ctxt,
ty: ty::t,
node_id_opt: Option<ast::node_id>,
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<R>(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 })
}
}
None => {
if need_base.any(|b| *b) {
// XXX should be span bug
tcx.sess.bug(~"missing fields and no base expr")
}
None
}
};
// Add the drop flag if necessary.
if has_dtor {
let dest = GEPi(bcx, addr, struct_dtor());
Store(bcx, C_u8(1), dest);
}
// 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<StructBaseInfo>,
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),

View File

@ -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

View File

@ -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"),

View File

@ -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 }

View File

@ -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;

View File

@ -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 <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.
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
}
}

View File

@ -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 <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.
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() { }

View File

@ -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 <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.
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 } };
}

View File

@ -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 <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.
struct S { f0: ~str, f1: ~str }
pub fn main() {
let s = ~"Hello, world!";
let _s = S { f1: str::from_slice(s), f0: s };
}