mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 23:34:48 +00:00
Add lots of comments to adt.rs, and some minor cleanup.
This commit is contained in:
parent
a9026c7f19
commit
d6acb96c9c
@ -8,10 +8,56 @@
|
||||
// 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::*;
|
||||
@ -23,31 +69,58 @@ use syntax::ast;
|
||||
use util::ppaux::ty_to_str;
|
||||
|
||||
|
||||
// XXX: should this be done with boxed traits instead of ML-style?
|
||||
/// 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),
|
||||
CEnum(int, int), /* discriminant range */
|
||||
/// 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 {
|
||||
DtorPresent,
|
||||
DtorAbsent,
|
||||
NoDtor
|
||||
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) {
|
||||
@ -56,18 +129,19 @@ pub fn represent_type(cx: @CrateContext, t: ty::t) -> @Repr {
|
||||
}
|
||||
let repr = @match ty::get(t).sty {
|
||||
ty::ty_tup(ref elems) => {
|
||||
Univariant(mk_struct(cx, *elems), NoDtor)
|
||||
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)), DtorAbsent)
|
||||
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 { DtorPresent } else { DtorAbsent })
|
||||
})), if dt { StructWithDtor } else { StructWithoutDtor })
|
||||
}
|
||||
ty::ty_enum(def_id, ref substs) => {
|
||||
struct Case { discr: int, tys: ~[ty::t] };
|
||||
@ -80,17 +154,22 @@ pub fn represent_type(cx: @CrateContext, t: ty::t) -> @Repr {
|
||||
};
|
||||
if cases.len() == 0 {
|
||||
// Uninhabitable; represent as unit
|
||||
Univariant(mk_struct(cx, ~[]), NoDtor)
|
||||
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 {
|
||||
// struct, tuple, newtype, etc.
|
||||
// Equivalent to a struct/tuple/newtype.
|
||||
assert cases[0].discr == 0;
|
||||
Univariant(mk_struct(cx, cases[0].tys), NoDtor)
|
||||
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",
|
||||
@ -115,13 +194,18 @@ fn mk_struct(cx: @CrateContext, tys: &[ty::t]) -> Struct {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn sizing_fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] {
|
||||
generic_fields_of(cx, r, true)
|
||||
}
|
||||
/**
|
||||
* 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 {
|
||||
@ -134,9 +218,9 @@ fn generic_fields_of(cx: @CrateContext, r: &Repr, sizing: bool)
|
||||
st.fields.map(|&ty| type_of::type_of(cx, ty))
|
||||
};
|
||||
match dt {
|
||||
NoDtor => f,
|
||||
DtorAbsent => ~[T_struct(f)],
|
||||
DtorPresent => ~[T_struct(f), T_i8()]
|
||||
NonStruct => f,
|
||||
StructWithoutDtor => ~[T_struct(f)],
|
||||
StructWithDtor => ~[T_struct(f), T_i8()]
|
||||
}
|
||||
}
|
||||
General(ref sts) => {
|
||||
@ -164,6 +248,10 @@ fn load_discr(bcx: block, scrutinee: ValueRef, min: int, max: int)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain as much of a "discriminant" as this representation has.
|
||||
* 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 {
|
||||
@ -176,6 +264,10 @@ pub fn trans_switch(bcx: block, r: &Repr, scrutinee: ValueRef)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the representation is potentially of a C-like enum, implement
|
||||
* coercion to numeric types.
|
||||
*/
|
||||
pub fn trans_cast_to_int(bcx: block, r: &Repr, scrutinee: ValueRef)
|
||||
-> ValueRef {
|
||||
match *r {
|
||||
@ -183,11 +275,18 @@ pub fn trans_cast_to_int(bcx: block, r: &Repr, scrutinee: ValueRef)
|
||||
CEnum(min, max) => load_discr(bcx, scrutinee, min, max),
|
||||
Univariant(*) => bcx.ccx().sess.bug(~"type has no explicit \
|
||||
discriminant"),
|
||||
// Note: this case is used internally by trans_switch,
|
||||
// even though it shouldn't be reached by an external caller.
|
||||
General(ref cases) => load_discr(bcx, scrutinee, 0,
|
||||
(cases.len() - 1) as int)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(*) => {
|
||||
@ -202,6 +301,11 @@ pub fn trans_case(bcx: block, r: &Repr, discr: int) -> _match::opt_result {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin initializing a new value of the given case of the given
|
||||
* representation. The fields should then be initialized with
|
||||
* `trans_GEP` and stores.
|
||||
*/
|
||||
pub fn trans_set_discr(bcx: block, r: &Repr, val: ValueRef, discr: int) {
|
||||
match *r {
|
||||
Unit(the_discr) => {
|
||||
@ -211,7 +315,7 @@ pub fn trans_set_discr(bcx: block, r: &Repr, val: ValueRef, discr: int) {
|
||||
assert min <= discr && discr <= max;
|
||||
Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0]))
|
||||
}
|
||||
Univariant(_, DtorPresent) => {
|
||||
Univariant(_, StructWithDtor) => {
|
||||
assert discr == 0;
|
||||
Store(bcx, C_u8(1), GEPi(bcx, val, [0, 1]))
|
||||
}
|
||||
@ -224,6 +328,10 @@ pub fn trans_set_discr(bcx: block, r: &Repr, val: ValueRef, discr: int) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
@ -232,11 +340,12 @@ pub fn num_args(r: &Repr, discr: int) -> uint {
|
||||
}
|
||||
}
|
||||
|
||||
/// Access a field, at a point when the value's case is known.
|
||||
pub fn trans_GEP(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'll need to return a possibly-new bcx as well.
|
||||
// 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")
|
||||
@ -244,8 +353,8 @@ pub fn trans_GEP(bcx: block, r: &Repr, val: ValueRef, discr: int, ix: uint)
|
||||
Univariant(ref st, dt) => {
|
||||
assert discr == 0;
|
||||
let val = match dt {
|
||||
NoDtor => val,
|
||||
DtorPresent | DtorAbsent => GEPi(bcx, val, [0, 0])
|
||||
NonStruct => val,
|
||||
StructWithDtor | StructWithoutDtor => GEPi(bcx, val, [0, 0])
|
||||
};
|
||||
struct_GEP(bcx, st, val, ix, false)
|
||||
}
|
||||
@ -271,14 +380,26 @@ fn struct_GEP(bcx: block, st: &Struct, val: ValueRef, ix: uint,
|
||||
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(_, DtorPresent) => GEPi(bcx, val, [0, 1]),
|
||||
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.
|
||||
*
|
||||
* Currently it 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 {
|
||||
@ -294,10 +415,10 @@ pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int,
|
||||
assert discr == 0;
|
||||
let s = C_struct(build_const_struct(ccx, st, vals));
|
||||
match dt {
|
||||
NoDtor => s,
|
||||
NonStruct => s,
|
||||
// The actual destructor flag doesn't need to be present.
|
||||
// But add an extra struct layer for compatibility.
|
||||
DtorPresent | DtorAbsent => C_struct(~[s])
|
||||
StructWithDtor | StructWithoutDtor => C_struct(~[s])
|
||||
}
|
||||
}
|
||||
General(ref cases) => {
|
||||
@ -345,7 +466,7 @@ fn build_const_struct(ccx: @CrateContext, st: &Struct, vals: &[ValueRef])
|
||||
#[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 {
|
||||
@ -356,13 +477,14 @@ pub fn const_get_discrim(ccx: @CrateContext, r: &Repr, val: ValueRef)
|
||||
}
|
||||
}
|
||||
|
||||
/// Access a field of a constant value.
|
||||
pub fn const_get_element(ccx: @CrateContext, r: &Repr, val: ValueRef,
|
||||
_discr: int, ix: uint) -> ValueRef {
|
||||
// Not to be confused with common::const_get_elt.
|
||||
match *r {
|
||||
Unit(*) | CEnum(*) => ccx.sess.bug(~"element access in C-like enum \
|
||||
const"),
|
||||
Univariant(_, NoDtor) => const_struct_field(ccx, val, ix),
|
||||
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,
|
||||
@ -395,8 +517,8 @@ fn const_struct_field(ccx: @CrateContext, val: ValueRef, ix: uint)
|
||||
/// 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, DtorAbsent)
|
||||
| Univariant(ref st, NoDtor) => st.fields.len() == 1,
|
||||
Univariant(ref st, StructWithoutDtor)
|
||||
| Univariant(ref st, NonStruct) => st.fields.len() == 1,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user