Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP) rustc: Implement nested C-like enums #17050

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 110 additions & 15 deletions src/librustc/middle/trans/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@

#![allow(unsigned_negate)]

use std::cmp;
use std::collections::Map;
use std::collections::hashmap::HashSet;
use libc::c_ulonglong;
use std::collections::Map;
use std::num::Int;
Expand Down Expand Up @@ -77,7 +80,7 @@ type Hint = attr::ReprAttr;
#[deriving(Eq, PartialEq)]
pub enum Repr {
/// C-like enums; basically an int.
CEnum(IntType, Disr, Disr), // discriminant range (signedness based on the IntType)
CEnum(IntType, Disr, Disr, uint), // discriminant range (signedness based on the IntType)
/**
* Single-case variants, and structs/tuples/records.
*
Expand Down Expand Up @@ -138,6 +141,13 @@ pub struct Struct {
pub fields: Vec<ty::t>
}

#[deriving(Show)]
struct CEnumRepr {
pub lo: Disr,
pub hi: Disr,
pub set: Option<HashSet<Disr>>
}

/**
* Convenience for `represent_type`. There should probably be more or
* these, for places in trans where the `ty::t` isn't directly
Expand Down Expand Up @@ -198,6 +208,7 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr {
return Univariant(mk_struct(cx, ftys.as_slice(), false), dtor);
}

// maybe check len <= 1 here:
if !dtor && cases.iter().all(|c| c.tys.len() == 0) {
// All bodies empty -> intlike
let discrs: Vec<u64> = cases.iter().map(|c| c.discr).collect();
Expand All @@ -207,7 +218,7 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr {
slo: discrs.iter().map(|n| *n as i64).min().unwrap(),
shi: discrs.iter().map(|n| *n as i64).max().unwrap()
};
return mk_cenum(cx, hint, &bounds);
return mk_cenum(cx, hint, &bounds, 0);
}

// Since there's at least one
Expand All @@ -229,6 +240,27 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr {
return Univariant(mk_struct(cx, ftys.as_slice(), false), dtor);
}

match hint {
attr::ReprInt(_, itype) => {
// attempt to flatten the enum
let (vnum, set) = collect_cases(cx, cases.as_slice(), hint);
let free_disrs = set.iter().max().unwrap() + 1 - set.len() as u64;
let additional = vnum - cmp::min(free_disrs, vnum);
let bounds = IntBounds {
ulo: if vnum == 0 { *set.iter().min().unwrap() } else { 0u64 },
uhi: *set.iter().max().unwrap() + additional,
slo: if vnum == 0 { set.iter().map(|n| *n as i64).min().unwrap() } else { 0i64 },
shi: set.iter().map(|n| *n as i64).max().unwrap() + additional as i64
};
debug!("nested C-like with hint {} bounds {} {} {} {}", itype, bounds.ulo,
bounds.uhi,
bounds.slo,
bounds.shi);
return mk_cenum(cx, hint, &bounds, 1);
}
attr::ReprAny | attr::ReprExtern | attr::ReprPacked => {}
}

if !dtor && cases.len() == 2 && hint == attr::ReprAny {
// Nullable pointer optimization
let mut discr = 0;
Expand Down Expand Up @@ -349,6 +381,59 @@ fn get_cases(tcx: &ty::ctxt, def_id: ast::DefId, substs: &subst::Substs) -> Vec<
}).collect()
}

fn collect_cases(cx: &CrateContext, cases: &[Case], hint: Hint) -> (u64, HashSet<Disr>) {
let other_cases = cases.iter().filter(|c| c.tys.len() == 0).count();

if other_cases == cases.len() {
// All bodies empty -> int-like
let discrs: HashSet<Disr> = cases.iter().map(|c| c.discr).collect();
return (0u64, discrs);
}

let mut other_cases = other_cases as u64;

match cases.iter().find(|c| c.tys.len() > 1) {
None => {}
Some(tuple_case) => {
let case_str: Vec<String> = tuple_case.tys.iter().map(|&t| ty_to_string(cx.tcx(), t))
.collect();
cx.sess().bug(format!("Nested enum can't be C-like, it has: {}",
case_str).as_slice());
}
}

let cenum_cases = cases.iter().flat_map(|c| c.tys.as_slice().iter());
let mut cenums = cenum_cases.map(|&t| {
match ty::get(t).sty {
ty::ty_enum(def_id, ref substs) => {
let cases = get_cases(cx.tcx(), def_id, substs);
let hints = ty::lookup_repr_hints(cx.tcx(), def_id); // no need?
collect_cases(cx, cases.as_slice(), hints[0])
}
_ => {
cx.sess().bug(format!("Nested enum can't be C-like, it has: {}",
ty_to_string(cx.tcx(), t)).as_slice())
}
}
});
// at least 2 cases, including one nonzero-sized
let (cnum, mut head) = cenums.next().unwrap();
other_cases += cnum;
for (cnum, cset) in cenums {
let initial_len = head.len();
for (i, &discr) in cset.iter().enumerate() {
head.insert(discr);
if head.len() != initial_len + i + 1 {
cx.sess().bug(format!("Nested C-like enum variants collide: {}",
discr).as_slice())
}
}
other_cases += cnum;
}

return (other_cases, head);
}

fn mk_struct(cx: &CrateContext, tys: &[ty::t], packed: bool) -> Struct {
if tys.iter().all(|&ty| ty::type_is_sized(cx.tcx(), ty)) {
let lltys = tys.iter().map(|&ty| type_of::sizing_type_of(cx, ty)).collect::<Vec<_>>();
Expand Down Expand Up @@ -382,11 +467,11 @@ struct IntBounds {
uhi: u64
}

fn mk_cenum(cx: &CrateContext, hint: Hint, bounds: &IntBounds) -> Repr {
fn mk_cenum(cx: &CrateContext, hint: Hint, bounds: &IntBounds, args: uint) -> Repr {
let it = range_to_inttype(cx, hint, bounds);
match it {
attr::SignedInt(_) => CEnum(it, bounds.slo as Disr, bounds.shi as Disr),
attr::UnsignedInt(_) => CEnum(it, bounds.ulo, bounds.uhi)
attr::SignedInt(_) => CEnum(it, bounds.slo as Disr, bounds.shi as Disr, args),
attr::UnsignedInt(_) => CEnum(it, bounds.ulo, bounds.uhi, args)
}
}

Expand Down Expand Up @@ -502,7 +587,7 @@ fn generic_type_of(cx: &CrateContext,
sizing: bool,
dst: bool) -> Type {
match *r {
CEnum(ity, _, _) => ll_inttype(cx, ity),
CEnum(ity, _, _, _) => ll_inttype(cx, ity),
RawNullablePointer { nnty, .. } => type_of::sizing_type_of(cx, nnty),
Univariant(ref st, _) | StructWrappedNullablePointer { nonnull: ref st, .. } => {
match name {
Expand Down Expand Up @@ -595,7 +680,7 @@ pub fn trans_get_discr(bcx: &Block, r: &Repr, scrutinee: ValueRef, cast_to: Opti
let signed;
let val;
match *r {
CEnum(ity, min, max) => {
CEnum(ity, min, max, _) => {
val = load_discr(bcx, ity, scrutinee, min, max);
signed = ity.is_signed();
}
Expand Down Expand Up @@ -669,7 +754,7 @@ fn load_discr(bcx: &Block, ity: IntType, ptr: ValueRef, min: Disr, max: Disr)
pub fn trans_case<'a>(bcx: &'a Block<'a>, r: &Repr, discr: Disr)
-> _match::OptResult<'a> {
match *r {
CEnum(ity, _, _) => {
CEnum(ity, _, _, _) => {
_match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity),
discr as u64, true)))
}
Expand All @@ -694,7 +779,10 @@ pub fn trans_case<'a>(bcx: &'a Block<'a>, r: &Repr, discr: Disr)
*/
pub fn trans_set_discr(bcx: &Block, r: &Repr, val: ValueRef, discr: Disr) {
match *r {
CEnum(ity, min, max) => {
CEnum(_, _, _, 1) => {
// identity function (noop)
}
CEnum(ity, min, max, _) => {
assert_discr_in_range(ity, min, max, discr);
Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true),
val)
Expand Down Expand Up @@ -751,7 +839,7 @@ fn assert_discr_in_range(ity: IntType, min: Disr, max: Disr, discr: Disr) {
*/
pub fn num_args(r: &Repr, discr: Disr) -> uint {
match *r {
CEnum(..) => 0,
CEnum(_, _, _, args) => args,
Univariant(ref st, dtor) => {
assert_eq!(discr, 0);
st.fields.len() - (if dtor { 1 } else { 0 })
Expand All @@ -777,7 +865,10 @@ pub fn trans_field_ptr(bcx: &Block, r: &Repr, val: ValueRef, discr: Disr,
// someday), it will need to return a possibly-new bcx as well.
match *r {
CEnum(..) => {
bcx.ccx().sess().bug("element access in C-like enum")
// C_integral(ll_inttype(ccx, ity), discr as u64, true)
debug!("trans_field_ptr ix {}", ix);
val
// bcx.ccx().sess().bug("element access in C-like enum")
}
Univariant(ref st, _dtor) => {
assert_eq!(discr, 0);
Expand Down Expand Up @@ -916,9 +1007,13 @@ pub fn trans_drop_flag_ptr<'b>(mut bcx: &'b Block<'b>, r: &Repr,
pub fn trans_const(ccx: &CrateContext, r: &Repr, discr: Disr,
vals: &[ValueRef]) -> ValueRef {
match *r {
CEnum(ity, min, max) => {
assert_eq!(vals.len(), 0);
assert_discr_in_range(ity, min, max, discr);
CEnum(ity, min, max, 1) if !vals.is_empty() => {
*vals.last().unwrap()
}
CEnum(ity, min, max, _) => {
// assert_eq!(vals.len(), 0);
// assert_discr_in_range(ity, min, max, discr);

C_integral(ll_inttype(ccx, ity), discr as u64, true)
}
General(ity, ref cases, _) => {
Expand Down Expand Up @@ -1039,7 +1134,7 @@ fn roundup(x: u64, a: u64) -> u64 { ((x + (a - 1)) / a) * a }
pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef)
-> Disr {
match *r {
CEnum(ity, _, _) => {
CEnum(ity, _, _, _) => {
match ity {
attr::SignedInt(..) => const_to_int(val) as Disr,
attr::UnsignedInt(..) => const_to_uint(val) as Disr
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/trans/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2383,7 +2383,7 @@ fn prepare_enum_metadata(cx: &CrateContext,
let type_rep = adt::represent_type(cx, enum_type);

let discriminant_type_metadata = match *type_rep {
adt::CEnum(inttype, _, _) => {
adt::CEnum(inttype, _, _, _) => {
return FinalMetadata(discriminant_type_metadata(inttype))
},
adt::RawNullablePointer { .. } |
Expand Down
5 changes: 5 additions & 0 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3013,6 +3013,11 @@ pub fn type_is_c_like_enum(cx: &ctxt, ty: t) -> bool {
false
} else {
variants.iter().all(|v| v.args.len() == 0)
|| lookup_repr_hints(cx, did).iter().any(|a|
match a {
&attr::ReprInt(..) => true,
_ => false
})
}
}
_ => false
Expand Down