1
Fork 0

auto merge of #5778 : jld/rust/reflect-abstract-enum, r=graydon

This takes care of one of the last remnants of assumptions about enum layout.  A type visitor is now passed a function to read a value's discriminant, then accesses fields by being passed a byte offset for each one.  The latter may not be fully general, despite the constraints imposed on representations by borrowed pointers, but works for any representations currently planned and is relatively simple.

Closes #5652.
This commit is contained in:
bors 2013-04-15 09:57:55 -07:00
commit 3809a04bf7
5 changed files with 201 additions and 28 deletions

View file

@ -15,6 +15,7 @@ Runtime type reflection
*/
use intrinsic::{TyDesc, TyVisitor};
#[cfg(not(stage0))] use intrinsic::Opaque;
use libc::c_void;
use sys;
use vec;
@ -393,6 +394,7 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> {
true
}
#[cfg(stage0)]
fn visit_enter_enum(&self, n_variants: uint, sz: uint, align: uint)
-> bool {
self.align(align);
@ -402,11 +404,23 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> {
true
}
#[cfg(not(stage0))]
fn visit_enter_enum(&self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
sz: uint, align: uint)
-> bool {
self.align(align);
if ! self.inner.visit_enter_enum(n_variants, get_disr, sz, align) {
return false;
}
true
}
fn visit_enter_enum_variant(&self, variant: uint,
disr_val: int,
n_fields: uint,
name: &str) -> bool {
self.inner.push_ptr();
self.inner.push_ptr(); // NOTE remove after next snapshot
if ! self.inner.visit_enter_enum_variant(variant, disr_val,
n_fields, name) {
return false;
@ -414,6 +428,7 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> {
true
}
#[cfg(stage0)]
fn visit_enum_variant_field(&self, i: uint, inner: *TyDesc) -> bool {
unsafe { self.align((*inner).align); }
if ! self.inner.visit_enum_variant_field(i, inner) { return false; }
@ -421,6 +436,15 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> {
true
}
#[cfg(not(stage0))]
fn visit_enum_variant_field(&self, i: uint, offset: uint, inner: *TyDesc) -> bool {
self.inner.push_ptr();
self.bump(offset);
if ! self.inner.visit_enum_variant_field(i, offset, inner) { return false; }
self.inner.pop_ptr();
true
}
fn visit_leave_enum_variant(&self, variant: uint,
disr_val: int,
n_fields: uint,
@ -429,10 +453,11 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> {
n_fields, name) {
return false;
}
self.inner.pop_ptr();
self.inner.pop_ptr(); // NOTE remove after next snapshot
true
}
#[cfg(stage0)]
fn visit_leave_enum(&self, n_variants: uint, sz: uint, align: uint)
-> bool {
if ! self.inner.visit_leave_enum(n_variants, sz, align) {
@ -442,6 +467,17 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> {
true
}
#[cfg(not(stage0))]
fn visit_leave_enum(&self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
sz: uint, align: uint) -> bool {
if ! self.inner.visit_leave_enum(n_variants, get_disr, sz, align) {
return false;
}
self.bump(sz);
true
}
fn visit_trait(&self) -> bool {
self.align_to::<@TyVisitor>();
if ! self.inner.visit_trait() { return false; }

View file

@ -18,6 +18,7 @@ use cast::transmute;
use char;
use intrinsic;
use intrinsic::{TyDesc, TyVisitor, visit_tydesc};
#[cfg(not(stage0))] use intrinsic::Opaque;
use io::{Writer, WriterUtil};
use libc::c_void;
use managed;
@ -137,12 +138,20 @@ impl Repr for char {
// New implementation using reflect::MovePtr
#[cfg(stage0)]
enum VariantState {
Degenerate,
TagMatch,
TagMismatch,
}
#[cfg(not(stage0))]
enum VariantState {
SearchingFor(int),
Matched,
AlreadyFound
}
pub struct ReprVisitor {
mut ptr: *c_void,
mut ptr_stk: ~[*c_void],
@ -181,14 +190,14 @@ pub impl ReprVisitor {
true
}
#[inline(always)]
#[cfg(stage0)] #[inline(always)]
fn bump(&self, sz: uint) {
do self.move_ptr() |p| {
((p as uint) + sz) as *c_void
};
}
#[inline(always)]
#[cfg(stage0)] #[inline(always)]
fn bump_past<T>(&self) {
self.bump(sys::size_of::<T>());
}
@ -458,6 +467,7 @@ impl TyVisitor for ReprVisitor {
true
}
#[cfg(stage0)]
fn visit_enter_enum(&self, n_variants: uint,
_sz: uint, _align: uint) -> bool {
if n_variants == 1 {
@ -468,6 +478,16 @@ impl TyVisitor for ReprVisitor {
true
}
#[cfg(not(stage0))]
fn visit_enter_enum(&self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
_sz: uint, _align: uint) -> bool {
let disr = unsafe { get_disr(transmute(self.ptr)) };
self.var_stk.push(SearchingFor(disr));
true
}
#[cfg(stage0)]
fn visit_enter_enum_variant(&self, _variant: uint,
disr_val: int,
n_fields: uint,
@ -500,6 +520,36 @@ impl TyVisitor for ReprVisitor {
true
}
#[cfg(not(stage0))]
fn visit_enter_enum_variant(&self, _variant: uint,
disr_val: int,
n_fields: uint,
name: &str) -> bool {
let mut write = false;
match self.var_stk.pop() {
SearchingFor(sought) => {
if disr_val == sought {
self.var_stk.push(Matched);
write = true;
} else {
self.var_stk.push(SearchingFor(sought));
}
}
Matched | AlreadyFound => {
self.var_stk.push(AlreadyFound);
}
}
if write {
self.writer.write_str(name);
if n_fields > 0 {
self.writer.write_char('(');
}
}
true
}
#[cfg(stage0)]
fn visit_enum_variant_field(&self, i: uint, inner: *TyDesc) -> bool {
match self.var_stk[vec::uniq_len(&const self.var_stk) - 1] {
Degenerate | TagMatch => {
@ -515,6 +565,23 @@ impl TyVisitor for ReprVisitor {
true
}
#[cfg(not(stage0))]
fn visit_enum_variant_field(&self, i: uint, _offset: uint, inner: *TyDesc) -> bool {
match self.var_stk[vec::uniq_len(&const self.var_stk) - 1] {
Matched => {
if i != 0 {
self.writer.write_str(", ");
}
if ! self.visit_inner(inner) {
return false;
}
}
_ => ()
}
true
}
#[cfg(stage0)]
fn visit_leave_enum_variant(&self, _variant: uint,
_disr_val: int,
n_fields: uint,
@ -530,12 +597,39 @@ impl TyVisitor for ReprVisitor {
true
}
#[cfg(not(stage0))]
fn visit_leave_enum_variant(&self, _variant: uint,
_disr_val: int,
n_fields: uint,
_name: &str) -> bool {
match self.var_stk[vec::uniq_len(&const self.var_stk) - 1] {
Matched => {
if n_fields > 0 {
self.writer.write_char(')');
}
}
_ => ()
}
true
}
#[cfg(stage0)]
fn visit_leave_enum(&self, _n_variants: uint,
_sz: uint, _align: uint) -> bool {
self.var_stk.pop();
true
}
#[cfg(not(stage0))]
fn visit_leave_enum(&self, _n_variants: uint,
_get_disr: extern unsafe fn(ptr: *Opaque) -> int,
_sz: uint, _align: uint) -> bool {
match self.var_stk.pop() {
SearchingFor(*) => fail!(~"enum value matched no variant"),
_ => true
}
}
fn visit_enter_fn(&self, _purity: uint, _proto: uint,
_n_inputs: uint, _retstyle: uint) -> bool { true }
fn visit_fn_input(&self, _i: uint, _mode: uint, _inner: *TyDesc) -> bool {

View file

@ -28,6 +28,8 @@ pub mod intrinsic {
// Remaining fields not listed
}
pub enum Opaque { }
pub trait TyVisitor {
fn visit_bot(&self) -> bool;
fn visit_nil(&self) -> bool;
@ -91,17 +93,19 @@ pub mod intrinsic {
sz: uint, align: uint) -> bool;
fn visit_enter_enum(&self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
sz: uint, align: uint) -> bool;
fn visit_enter_enum_variant(&self, variant: uint,
disr_val: int,
n_fields: uint,
name: &str) -> bool;
fn visit_enum_variant_field(&self, i: uint, inner: *TyDesc) -> bool;
fn visit_enum_variant_field(&self, i: uint, offset: uint, inner: *TyDesc) -> bool;
fn visit_leave_enum_variant(&self, variant: uint,
disr_val: int,
n_fields: uint,
name: &str) -> bool;
fn visit_leave_enum(&self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
sz: uint, align: uint) -> bool;
fn visit_enter_fn(&self, purity: uint, proto: uint,

View file

@ -8,8 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use lib::llvm::{TypeRef, ValueRef};
use back::link::mangle_internal_name_by_path_and_seq;
use lib::llvm::{TypeRef, ValueRef, llvm};
use middle::trans::adt;
use middle::trans::base::*;
use middle::trans::build::*;
use middle::trans::callee::{ArgVals, DontAutorefArg};
@ -24,10 +25,13 @@ use middle::trans::type_of::*;
use middle::ty;
use util::ppaux::ty_to_str;
use core::libc::c_uint;
use core::option::None;
use core::vec;
use syntax::ast::def_id;
use syntax::ast;
use syntax::ast_map::path_name;
use syntax::parse::token::special_idents;
pub struct Reflector {
visitor_val: ValueRef,
@ -266,23 +270,52 @@ pub impl Reflector {
// variant?
ty::ty_enum(did, ref substs) => {
let bcx = self.bcx;
let tcx = bcx.ccx().tcx;
let variants = ty::substd_enum_variants(tcx, did, substs);
let ccx = bcx.ccx();
let repr = adt::represent_type(bcx.ccx(), t);
let variants = ty::substd_enum_variants(ccx.tcx, did, substs);
let llptrty = T_ptr(type_of(ccx, t));
let (_, opaquety) = *(ccx.tcx.intrinsic_defs.find(&ccx.sess.ident_of(~"Opaque"))
.expect("Failed to resolve intrinsic::Opaque"));
let opaqueptrty = ty::mk_ptr(ccx.tcx, ty::mt { ty: opaquety, mutbl: ast::m_imm });
let extra = ~[self.c_uint(vec::len(variants))]
let make_get_disr = || {
let sub_path = bcx.fcx.path + ~[path_name(special_idents::anon)];
let sym = mangle_internal_name_by_path_and_seq(ccx, sub_path, ~"get_disr");
let args = [ty::arg { mode: ast::expl(ast::by_copy),
ty: opaqueptrty }];
let llfty = type_of_fn(ccx, args, ty::mk_int(ccx.tcx));
let llfdecl = decl_internal_cdecl_fn(ccx.llmod, sym, llfty);
let arg = unsafe {
llvm::LLVMGetParam(llfdecl, first_real_arg as c_uint)
};
let fcx = new_fn_ctxt(ccx, ~[], llfdecl, None);
let bcx = top_scope_block(fcx, None);
let arg = BitCast(bcx, arg, llptrty);
let ret = adt::trans_get_discr(bcx, repr, arg);
Store(bcx, ret, fcx.llretptr);
cleanup_and_Br(bcx, bcx, fcx.llreturn);
finish_fn(fcx, bcx.llbb);
llfdecl
};
let enum_args = ~[self.c_uint(vec::len(variants)), make_get_disr()]
+ self.c_size_and_align(t);
do self.bracketed(~"enum", extra) |this| {
do self.bracketed(~"enum", enum_args) |this| {
for variants.eachi |i, v| {
let extra1 = ~[this.c_uint(i),
this.c_int(v.disr_val),
this.c_uint(vec::len(v.args)),
this.c_slice(
bcx.ccx().sess.str_of(v.name))];
do this.bracketed(~"enum_variant", extra1) |this| {
let variant_args = ~[this.c_uint(i),
this.c_int(v.disr_val),
this.c_uint(vec::len(v.args)),
this.c_slice(ccx.sess.str_of(v.name))];
do this.bracketed(~"enum_variant", variant_args) |this| {
for v.args.eachi |j, a| {
let extra = ~[this.c_uint(j),
this.c_tydesc(*a)];
this.visit(~"enum_variant_field", extra);
let bcx = this.bcx;
let null = C_null(llptrty);
let offset = p2i(ccx, adt::trans_field_ptr(bcx, repr, null,
v.disr_val, j));
let field_args = ~[this.c_uint(j),
offset,
this.c_tydesc(*a)];
this.visit(~"enum_variant_field", field_args);
}
}
}

View file

@ -13,7 +13,7 @@
use core::bool;
use core::libc::c_void;
use core::vec::UnboxedVecRepr;
use intrinsic::{TyDesc, get_tydesc, visit_tydesc, TyVisitor};
use intrinsic::{TyDesc, get_tydesc, visit_tydesc, TyVisitor, Opaque};
#[doc = "High-level interfaces to `intrinsic::visit_ty` reflection system."]
@ -376,10 +376,12 @@ impl<V:TyVisitor + movable_ptr> TyVisitor for ptr_visit_adaptor<V> {
true
}
fn visit_enter_enum(&self, n_variants: uint, sz: uint, align: uint)
fn visit_enter_enum(&self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
sz: uint, align: uint)
-> bool {
self.align(align);
if ! self.inner.visit_enter_enum(n_variants, sz, align) { return false; }
if ! self.inner.visit_enter_enum(n_variants, get_disr, sz, align) { return false; }
true
}
@ -394,8 +396,8 @@ impl<V:TyVisitor + movable_ptr> TyVisitor for ptr_visit_adaptor<V> {
true
}
fn visit_enum_variant_field(&self, i: uint, inner: *TyDesc) -> bool {
if ! self.inner.visit_enum_variant_field(i, inner) { return false; }
fn visit_enum_variant_field(&self, i: uint, offset: uint, inner: *TyDesc) -> bool {
if ! self.inner.visit_enum_variant_field(i, offset, inner) { return false; }
true
}
@ -410,9 +412,11 @@ impl<V:TyVisitor + movable_ptr> TyVisitor for ptr_visit_adaptor<V> {
true
}
fn visit_leave_enum(&self, n_variants: uint, sz: uint, align: uint)
fn visit_leave_enum(&self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
sz: uint, align: uint)
-> bool {
if ! self.inner.visit_leave_enum(n_variants, sz, align) { return false; }
if ! self.inner.visit_leave_enum(n_variants, get_disr, sz, align) { return false; }
true
}
@ -586,6 +590,7 @@ impl TyVisitor for my_visitor {
_sz: uint, _align: uint) -> bool { true }
fn visit_enter_enum(&self, _n_variants: uint,
_get_disr: extern unsafe fn(ptr: *Opaque) -> int,
_sz: uint, _align: uint) -> bool {
// FIXME (#3732): this needs to rewind between enum variants, or something.
true
@ -594,7 +599,7 @@ impl TyVisitor for my_visitor {
_disr_val: int,
_n_fields: uint,
_name: &str) -> bool { true }
fn visit_enum_variant_field(&self, _i: uint, inner: *TyDesc) -> bool {
fn visit_enum_variant_field(&self, _i: uint, _offset: uint, inner: *TyDesc) -> bool {
self.visit_inner(inner)
}
fn visit_leave_enum_variant(&self, _variant: uint,
@ -602,6 +607,7 @@ impl TyVisitor for my_visitor {
_n_fields: uint,
_name: &str) -> bool { true }
fn visit_leave_enum(&self, _n_variants: uint,
_get_disr: extern unsafe fn(ptr: *Opaque) -> int,
_sz: uint, _align: uint) -> bool { true }
fn visit_enter_fn(&self, _purity: uint, _proto: uint,