1
Fork 0

Implement trait objects (cc #14)

This commit is contained in:
bjorn3 2018-09-08 18:00:06 +02:00
parent 62a0203a5a
commit 6196146ac4
9 changed files with 243 additions and 177 deletions

View file

@ -1,137 +0,0 @@
From 307aba455c6ee3227d7c522c07761cda19dc716c Mon Sep 17 00:00:00 2001
From: bjorn3 <bjorn3@users.noreply.github.com>
Date: Wed, 29 Aug 2018 14:29:05 +0200
Subject: [PATCH] Disable all trait object unsizing
---
src/libcore/alloc.rs | 2 +-
src/libcore/fmt/builders.rs | 13 ++++++++-----
src/libcore/fmt/mod.rs | 3 ++-
src/libcore/panic.rs | 5 +++--
src/libcore/slice/mod.rs | 2 +-
5 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs
index 35e4eea..28b6e2f 100644
--- a/src/libcore/alloc.rs
+++ b/src/libcore/alloc.rs
@@ -144,7 +144,7 @@ impl Layout {
#[stable(feature = "alloc_layout", since = "1.28.0")]
#[inline]
pub fn for_value<T: ?Sized>(t: &T) -> Self {
- let (size, align) = (mem::size_of_val(t), mem::align_of_val(t));
+ let (size, align) = panic!(); //(mem::size_of_val(t), mem::align_of_val(t));
// See rationale in `new` for why this us using an unsafe variant below
debug_assert!(Layout::from_size_align(size, align).is_ok());
unsafe {
diff --git a/src/libcore/fmt/builders.rs b/src/libcore/fmt/builders.rs
index 3c5f934..1427ab3 100644
--- a/src/libcore/fmt/builders.rs
+++ b/src/libcore/fmt/builders.rs
@@ -18,6 +18,7 @@ struct PadAdapter<'a> {
impl<'a> PadAdapter<'a> {
fn wrap<'b, 'c: 'a+'b>(fmt: &'c mut fmt::Formatter, slot: &'b mut Option<Self>)
-> fmt::Formatter<'b> {
+ /*
fmt.wrap_buf(move |buf| {
*slot = Some(PadAdapter {
buf,
@@ -25,6 +26,8 @@ impl<'a> PadAdapter<'a> {
});
slot.as_mut().unwrap()
})
+ */
+ panic!();
}
}
@@ -107,7 +110,7 @@ pub fn debug_struct_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>,
impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
/// Adds a new field to the generated struct output.
#[stable(feature = "debug_builders", since = "1.2.0")]
- pub fn field(&mut self, name: &str, value: &dyn fmt::Debug) -> &mut DebugStruct<'a, 'b> {
+ pub fn field(&mut self, name: &str, value: &impl fmt::Debug) -> &mut DebugStruct<'a, 'b> {
self.result = self.result.and_then(|_| {
let prefix = if self.has_fields {
","
@@ -204,7 +207,7 @@ pub fn debug_tuple_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>, name: &str) -> D
impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
/// Adds a new field to the generated tuple struct output.
#[stable(feature = "debug_builders", since = "1.2.0")]
- pub fn field(&mut self, value: &dyn fmt::Debug) -> &mut DebugTuple<'a, 'b> {
+ pub fn field(&mut self, value: &impl fmt::Debug) -> &mut DebugTuple<'a, 'b> {
self.result = self.result.and_then(|_| {
let (prefix, space) = if self.fields > 0 {
(",", " ")
@@ -258,7 +261,7 @@ struct DebugInner<'a, 'b: 'a> {
}
impl<'a, 'b: 'a> DebugInner<'a, 'b> {
- fn entry(&mut self, entry: &dyn fmt::Debug) {
+ fn entry(&mut self, entry: &impl fmt::Debug) {
self.result = self.result.and_then(|_| {
if self.is_pretty() {
let mut slot = None;
@@ -340,7 +343,7 @@ pub fn debug_set_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>) -> DebugSet<'a, 'b
impl<'a, 'b: 'a> DebugSet<'a, 'b> {
/// Adds a new entry to the set output.
#[stable(feature = "debug_builders", since = "1.2.0")]
- pub fn entry(&mut self, entry: &dyn fmt::Debug) -> &mut DebugSet<'a, 'b> {
+ pub fn entry(&mut self, entry: &impl fmt::Debug) -> &mut DebugSet<'a, 'b> {
self.inner.entry(entry);
self
}
@@ -411,7 +414,7 @@ pub fn debug_list_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>) -> DebugList<'a,
impl<'a, 'b: 'a> DebugList<'a, 'b> {
/// Adds a new entry to the list output.
#[stable(feature = "debug_builders", since = "1.2.0")]
- pub fn entry(&mut self, entry: &dyn fmt::Debug) -> &mut DebugList<'a, 'b> {
+ pub fn entry(&mut self, entry: &impl fmt::Debug) -> &mut DebugList<'a, 'b> {
self.inner.entry(entry);
self
}
diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs
index 928f95e..ad33906 100644
--- a/src/libcore/fmt/mod.rs
+++ b/src/libcore/fmt/mod.rs
@@ -224,7 +224,8 @@ pub trait Write {
}
}
- write(&mut Adapter(self), args)
+ //write(&mut Adapter(self), args)
+ panic!()
}
}
diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs
index 17cac5a..27b7dde 100644
--- a/src/libcore/panic.rs
+++ b/src/libcore/panic.rs
@@ -58,8 +58,9 @@ impl<'a> PanicInfo<'a> {
pub fn internal_constructor(message: Option<&'a fmt::Arguments<'a>>,
location: Location<'a>)
-> Self {
- struct NoPayload;
- PanicInfo { payload: &NoPayload, location, message }
+ //struct NoPayload;
+ //PanicInfo { payload: &NoPayload, location, message }
+ panic!();
}
#[doc(hidden)]
diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs
index 88fdd76..8537f0e 100644
--- a/src/libcore/slice/mod.rs
+++ b/src/libcore/slice/mod.rs
@@ -4003,7 +4003,7 @@ impl<A> SlicePartialEq<A> for [A]
return true;
}
unsafe {
- let size = mem::size_of_val(self);
+ let size = panic!(); //mem::size_of_val(self);
memcmp(self.as_ptr() as *const u8,
other.as_ptr() as *const u8, size) == 0
}
--
2.11.0

1
Cargo.lock generated
View file

@ -416,6 +416,7 @@ version = "0.1.0"
dependencies = [
"ar 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift 0.21.0 (git+https://github.com/CraneStation/cranelift.git)",
"cranelift-faerie 0.21.0 (git+https://github.com/CraneStation/cranelift.git)",
"cranelift-module 0.21.0 (git+https://github.com/CraneStation/cranelift.git)",

View file

@ -24,6 +24,7 @@ target-lexicon = "0.0.3"
faerie = "0.5.0"
ar = "0.6.0"
bitflags = "1.0.3"
byteorder = "1.2.6"
# Uncomment to use local checkout of cranelift
#[patch."https://github.com/CraneStation/cranelift.git"]

View file

@ -31,6 +31,18 @@ impl Termination for () {
}
}
trait SomeTrait {
fn object_safe(&self);
}
impl SomeTrait for &'static str {
fn object_safe(&self) {
unsafe {
puts(*self as *const str as *const u8);
}
}
}
#[lang = "start"]
fn start<T: Termination + 'static>(
main: fn() -> T,
@ -45,15 +57,28 @@ static NUM_REF: &'static u8 = unsafe { &NUM };
fn main() {
unsafe {
let slice: &[u8] = b"Hello\0" as &[u8; 6];
if intrinsics::size_of_val(slice) as u8 != 6 {
panic(&("eji", "frjio", 0, 0));
};
let ptr: *const u8 = slice as *const [u8] as *const u8;
let world = box "World!\0";
let hello: &[u8] = b"Hello\0" as &[u8; 6];
let ptr: *const u8 = hello as *const [u8] as *const u8;
puts(ptr);
puts(*world as *const str as *const u8);
}
//panic(&("panic msg", "abc.rs", 0, 43));
let world = box "World!\0";
puts(*world as *const str as *const u8);
if intrinsics::size_of_val(hello) as u8 != 6 {
panic(&("", "", 0, 0));
};
let chars = &['C', 'h', 'a', 'r', 's'];
let chars = chars as &[char];
if intrinsics::size_of_val(chars) as u8 != 4 * 5 {
panic(&("", "", 0, 0));
}
let a: &dyn SomeTrait = &"abc\0";
a.object_safe();
if intrinsics::size_of_val(a) as u8 != 16 {
panic(&("", "", 0, 0));
}
}
}

View file

@ -50,6 +50,18 @@ fn get_pass_mode<'a, 'tcx: 'a>(
}
}
fn adjust_arg_for_abi<'a, 'tcx: 'a>(
fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
sig: FnSig<'tcx>,
arg: CValue<'tcx>,
) -> Value {
match get_pass_mode(fx.tcx, sig.abi, arg.layout().ty, false) {
PassMode::NoPass => unimplemented!("pass mode nopass"),
PassMode::ByVal(_) => arg.load_value(fx),
PassMode::ByRef => arg.force_stack(fx),
}
}
pub fn cton_sig_from_fn_ty<'a, 'tcx: 'a>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
fn_ty: Ty<'tcx>,
@ -164,12 +176,16 @@ pub fn get_function_name_and_sig<'a, 'tcx>(
impl<'a, 'tcx: 'a, B: Backend + 'a> FunctionCx<'a, 'tcx, B> {
/// Instance must be monomorphized
pub fn get_function_ref(&mut self, inst: Instance<'tcx>) -> FuncRef {
pub fn get_function_id(&mut self, inst: Instance<'tcx>) -> FuncId {
let (name, sig) = get_function_name_and_sig(self.tcx, inst);
let func_id = self
.module
self.module
.declare_function(&name, Linkage::Import, &sig)
.unwrap();
.unwrap()
}
/// Instance must be monomorphized
pub fn get_function_ref(&mut self, inst: Instance<'tcx>) -> FuncRef {
let func_id = self.get_function_id(inst);
self.module
.declare_func_in_func(func_id, &mut self.bcx.func)
}
@ -468,29 +484,51 @@ pub fn codegen_call<'a, 'tcx: 'a>(
PassMode::ByVal(_) => None,
};
let instance = match fn_ty.sty {
ty::FnDef(def_id, substs) => {
Some(Instance::resolve(fx.tcx, ParamEnv::reveal_all(), def_id, substs).unwrap())
}
_ => None,
};
let func_ref: Option<Value>; // Indirect call target
let first_arg = {
if let Some(Instance {
def: InstanceDef::Virtual(_, idx),
..
}) = instance
{
let (ptr, method) = crate::vtable::get_ptr_and_method_ref(fx, args[0], idx);
func_ref = Some(method);
Some(ptr)
} else {
func_ref = if instance.is_none() {
let func = trans_operand(fx, func);
Some(func.load_value(fx))
} else {
None
};
args.get(0).map(|arg| adjust_arg_for_abi(fx, sig, *arg))
}.into_iter()
};
let call_args: Vec<Value> = return_ptr
.into_iter()
.chain(args.into_iter().map(|arg| {
match get_pass_mode(fx.tcx, sig.abi, arg.layout().ty, false) {
PassMode::NoPass => unimplemented!("pass mode nopass"),
PassMode::ByVal(_) => arg.load_value(fx),
PassMode::ByRef => arg.force_stack(fx),
}
})).collect::<Vec<_>>();
.chain(first_arg)
.chain(
args.into_iter()
.skip(1)
.map(|arg| adjust_arg_for_abi(fx, sig, arg)),
).collect::<Vec<_>>();
let call_inst = match fn_ty.sty {
ty::FnDef(def_id, substs) => {
let inst = Instance::resolve(fx.tcx, ParamEnv::reveal_all(), def_id, substs).unwrap();
let func_ref = fx.get_function_ref(inst);
fx.bcx.ins().call(func_ref, &call_args)
}
ty::FnPtr(_) => {
let func = trans_operand(fx, func);
let func = func.load_value(fx);
let sig = fx.bcx.import_signature(cton_sig_from_fn_ty(fx.tcx, fn_ty));
fx.bcx.ins().call_indirect(sig, func, &call_args)
}
_ => bug!("{:?}", fn_ty),
let sig = fx.bcx.import_signature(cton_sig_from_fn_ty(fx.tcx, fn_ty));
let call_inst = if let Some(func_ref) = func_ref {
fx.bcx.ins().call_indirect(sig, func_ref, &call_args)
} else {
let func_ref = fx.get_function_ref(instance.expect("non-indirect call on non-FnDef type"));
fx.bcx.ins().call(func_ref, &call_args)
};
match output_pass_mode {
@ -610,6 +648,7 @@ fn codegen_intrinsic_call<'a, 'tcx: 'a>(
let elem_size = fx.layout_of(elem).size.bytes();
fx.bcx.ins().imul_imm(len, elem_size as i64)
}
ty::Dynamic(..) => crate::vtable::size_of_obj(fx, args[0]),
ty => unimplemented!("size_of_val for {:?}", ty),
};
ret.write_cvalue(fx, CValue::ByVal(size, usize_layout));

View file

@ -12,7 +12,7 @@ impl<F: Fn() -> String> Drop for PrintOnPanic<F> {
pub fn trans_mono_item<'a, 'tcx: 'a>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
module: &mut Module<impl Backend>,
caches: &mut Caches,
caches: &mut Caches<'tcx>,
ccx: &mut crate::constant::ConstantCx,
mono_item: MonoItem<'tcx>,
) {
@ -59,7 +59,7 @@ fn trans_fn<'a, 'tcx: 'a>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
module: &mut Module<impl Backend>,
constants: &mut crate::constant::ConstantCx,
caches: &mut Caches,
caches: &mut Caches<'tcx>,
instance: Instance<'tcx>,
) {
// Step 1. Get mir

View file

@ -236,9 +236,13 @@ impl<'tcx> CValue<'tcx> {
}
_ => bug!("unsize non array {:?} to slice", ty),
},
ty::Dynamic(_, _) => match ty.sty {
ty::Dynamic(data, _) => match ty.sty {
ty::Dynamic(_, _) => self.load_value_pair(fx),
_ => unimpl!("unsize of type ... to {:?}", dest.layout().ty),
_ => {
let ptr = self.load_value(fx);
let vtable = crate::vtable::get_vtable(fx, ty, data.principal());
(ptr, vtable)
}
},
_ => bug!(
"unsize of type {:?} to {:?}",
@ -556,7 +560,7 @@ pub struct FunctionCx<'a, 'tcx: 'a, B: Backend + 'a> {
pub local_map: HashMap<Local, CPlace<'tcx>>,
pub comments: HashMap<Inst, String>,
pub constants: &'a mut crate::constant::ConstantCx,
pub caches: &'a mut Caches,
pub caches: &'a mut Caches<'tcx>,
/// add_global_comment inserts a comment here
pub top_nop: Option<Inst>,

View file

@ -1,6 +1,7 @@
#![feature(rustc_private, macro_at_most_once_rep)]
#![allow(intra_doc_link_resolution_failure)]
extern crate byteorder;
extern crate syntax;
#[macro_use]
extern crate rustc;
@ -53,6 +54,7 @@ mod common;
mod constant;
mod metadata;
mod pretty_clif;
mod vtable;
mod prelude {
pub use std::any::Any;
@ -100,14 +102,16 @@ mod prelude {
use crate::constant::ConstantCx;
use crate::prelude::*;
pub struct Caches {
pub struct Caches<'tcx> {
pub context: Context,
pub vtables: HashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), DataId>,
}
impl Caches {
impl<'tcx> Caches<'tcx> {
fn new() -> Self {
Caches {
context: Context::new(),
vtables: HashMap::new(),
}
}
}

129
src/vtable.rs Normal file
View file

@ -0,0 +1,129 @@
//! See librustc_codegen_llvm/meth.rs for reference
use crate::prelude::*;
const DROP_FN_INDEX: usize = 0;
const SIZE_INDEX: usize = 1;
const ALIGN_INDEX: usize = 2;
pub fn size_of_obj<'a, 'tcx: 'a>(
fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
val: CValue<'tcx>,
) -> Value {
let (_ptr, vtable) = val.load_value_pair(fx);
let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize;
fx.bcx.ins().load(
pointer_ty(fx.tcx),
MemFlags::new(),
vtable,
(SIZE_INDEX * usize_size) as i32,
)
}
pub fn get_ptr_and_method_ref<'a, 'tcx: 'a>(
fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
arg: CValue<'tcx>,
idx: usize,
) -> (Value, Value) {
let (ptr, vtable) = arg.load_value_pair(fx);
let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes();
let func_ref = fx.bcx.ins().load(
pointer_ty(fx.tcx),
MemFlags::new(),
vtable,
((idx + 3) * usize_size as usize) as i32,
);
(ptr, func_ref)
}
pub fn get_vtable<'a, 'tcx: 'a>(
fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
ty: Ty<'tcx>,
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
) -> Value {
let data_id = if let Some(data_id) = fx.caches.vtables.get(&(ty, trait_ref)) {
*data_id
} else {
let data_id = build_vtable(fx, ty, trait_ref);
fx.caches.vtables.insert((ty, trait_ref), data_id);
data_id
};
let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
fx.bcx
.ins()
.global_value(fx.module.pointer_type(), local_data_id)
}
fn build_vtable<'a, 'tcx: 'a>(
fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
ty: Ty<'tcx>,
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
) -> DataId {
let tcx = fx.tcx;
let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize;
let (size, align) = tcx
.layout_of(ParamEnv::reveal_all().and(ty))
.unwrap()
.size_and_align();
let drop_in_place_fn =
fx.get_function_id(::rustc_mir::monomorphize::resolve_drop_in_place(tcx, ty));
let mut components: Vec<_> = vec![Some(drop_in_place_fn), None, None];
if let Some(trait_ref) = trait_ref {
let trait_ref = trait_ref.with_self_ty(tcx, ty);
let methods = tcx.vtable_methods(trait_ref);
let methods = methods.iter().cloned().map(|opt_mth| {
opt_mth.map_or(None, |(def_id, substs)| {
Some(fx.get_function_id(
Instance::resolve(tcx, ParamEnv::reveal_all(), def_id, substs).unwrap(),
))
})
});
components.extend(methods);
}
let mut data_ctx = DataContext::new();
let mut data = ::std::iter::repeat(0u8)
.take(components.len() * usize_size)
.collect::<Vec<u8>>()
.into_boxed_slice();
write_usize(fx.tcx, &mut data, SIZE_INDEX, size.bytes());
write_usize(fx.tcx, &mut data, ALIGN_INDEX, align.abi());
data_ctx.define(data);
for (i, component) in components.into_iter().enumerate() {
if let Some(func_id) = component {
let func_ref = fx.module.declare_func_in_data(func_id, &mut data_ctx);
data_ctx.write_function_addr((i * usize_size) as u32, func_ref);
}
}
let data_id = fx
.module
.declare_data(
&format!("vtable.{:?}.for.{:?}", trait_ref, ty),
Linkage::Local,
false,
).unwrap();
fx.module.define_data(data_id, &data_ctx).unwrap();
data_id
}
fn write_usize(tcx: TyCtxt, buf: &mut [u8], idx: usize, num: u64) {
use byteorder::{BigEndian, LittleEndian, WriteBytesExt};
let usize_size = tcx
.layout_of(ParamEnv::reveal_all().and(tcx.types.usize))
.unwrap()
.size
.bytes() as usize;
let mut target = &mut buf[idx * usize_size..(idx + 1) * usize_size];
match tcx.data_layout.endian {
layout::Endian::Little => target.write_uint::<LittleEndian>(num, usize_size),
layout::Endian::Big => target.write_uint::<BigEndian>(num, usize_size),
}.unwrap()
}