mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-30 02:33:55 +00:00
librustc: Permit by-value-self methods to be invoked on objects
referenced by boxes. This is done by creating a shim function that handles the cleanup of the box properly. Closes #10672.
This commit is contained in:
parent
94343da1bd
commit
68ead460f9
@ -1220,7 +1220,7 @@ pub fn init_function<'a>(fcx: &'a FunctionContext<'a>,
|
|||||||
// - new_fn_ctxt
|
// - new_fn_ctxt
|
||||||
// - trans_args
|
// - trans_args
|
||||||
|
|
||||||
fn arg_kind(cx: &FunctionContext, t: ty::t) -> datum::Rvalue {
|
pub fn arg_kind(cx: &FunctionContext, t: ty::t) -> datum::Rvalue {
|
||||||
use middle::trans::datum::{ByRef, ByValue};
|
use middle::trans::datum::{ByRef, ByValue};
|
||||||
|
|
||||||
datum::Rvalue {
|
datum::Rvalue {
|
||||||
|
@ -159,6 +159,14 @@ impl<'a> Builder<'a> {
|
|||||||
attributes: &[(uint, u64)])
|
attributes: &[(uint, u64)])
|
||||||
-> ValueRef {
|
-> ValueRef {
|
||||||
self.count_insn("invoke");
|
self.count_insn("invoke");
|
||||||
|
|
||||||
|
debug!("Invoke {} with args ({})",
|
||||||
|
self.ccx.tn.val_to_str(llfn),
|
||||||
|
args.iter()
|
||||||
|
.map(|&v| self.ccx.tn.val_to_str(v))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.connect(", "));
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let v = llvm::LLVMBuildInvoke(self.llbuilder,
|
let v = llvm::LLVMBuildInvoke(self.llbuilder,
|
||||||
llfn,
|
llfn,
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
* closure.
|
* closure.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use arena::TypedArena;
|
||||||
use back::abi;
|
use back::abi;
|
||||||
|
use back::link;
|
||||||
use driver::session;
|
use driver::session;
|
||||||
use lib::llvm::ValueRef;
|
use lib::llvm::ValueRef;
|
||||||
use lib::llvm::llvm;
|
use lib::llvm::llvm;
|
||||||
@ -33,28 +35,26 @@ use middle::trans::cleanup::CleanupMethods;
|
|||||||
use middle::trans::common;
|
use middle::trans::common;
|
||||||
use middle::trans::common::*;
|
use middle::trans::common::*;
|
||||||
use middle::trans::datum::*;
|
use middle::trans::datum::*;
|
||||||
use middle::trans::datum::Datum;
|
use middle::trans::datum::{Datum, KindOps};
|
||||||
use middle::trans::expr;
|
use middle::trans::expr;
|
||||||
use middle::trans::glue;
|
use middle::trans::glue;
|
||||||
use middle::trans::inline;
|
use middle::trans::inline;
|
||||||
|
use middle::trans::foreign;
|
||||||
use middle::trans::meth;
|
use middle::trans::meth;
|
||||||
use middle::trans::monomorphize;
|
use middle::trans::monomorphize;
|
||||||
|
use middle::trans::type_::Type;
|
||||||
use middle::trans::type_of;
|
use middle::trans::type_of;
|
||||||
use middle::trans::foreign;
|
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::typeck;
|
use middle::typeck;
|
||||||
use middle::typeck::coherence::make_substs_for_receiver_types;
|
use middle::typeck::coherence::make_substs_for_receiver_types;
|
||||||
use middle::typeck::MethodCall;
|
use middle::typeck::MethodCall;
|
||||||
use util::ppaux::Repr;
|
use util::ppaux::Repr;
|
||||||
|
|
||||||
use middle::trans::type_::Type;
|
use std::gc::Gc;
|
||||||
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use synabi = syntax::abi;
|
use synabi = syntax::abi;
|
||||||
use syntax::ast_map;
|
use syntax::ast_map;
|
||||||
|
|
||||||
use std::gc::Gc;
|
|
||||||
|
|
||||||
pub struct MethodData {
|
pub struct MethodData {
|
||||||
pub llfn: ValueRef,
|
pub llfn: ValueRef,
|
||||||
pub llself: ValueRef,
|
pub llself: ValueRef,
|
||||||
@ -224,6 +224,134 @@ fn resolve_default_method_vtables(bcx: &Block,
|
|||||||
param_vtables
|
param_vtables
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Translates the adapter that deconstructs a `Box<Trait>` object into
|
||||||
|
/// `Trait` so that a by-value self method can be called.
|
||||||
|
pub fn trans_unboxing_shim(bcx: &Block,
|
||||||
|
llshimmedfn: ValueRef,
|
||||||
|
method: &ty::Method,
|
||||||
|
method_id: ast::DefId,
|
||||||
|
substs: subst::Substs)
|
||||||
|
-> ValueRef {
|
||||||
|
let _icx = push_ctxt("trans_unboxing_shim");
|
||||||
|
let ccx = bcx.ccx();
|
||||||
|
let tcx = bcx.tcx();
|
||||||
|
|
||||||
|
// Transform the self type to `Box<self_type>`.
|
||||||
|
let self_type = *method.fty.sig.inputs.get(0);
|
||||||
|
let boxed_self_type = ty::mk_uniq(tcx, self_type);
|
||||||
|
let boxed_function_type = ty::FnSig {
|
||||||
|
binder_id: method.fty.sig.binder_id,
|
||||||
|
inputs: method.fty.sig.inputs.iter().enumerate().map(|(i, typ)| {
|
||||||
|
if i == 0 {
|
||||||
|
boxed_self_type
|
||||||
|
} else {
|
||||||
|
*typ
|
||||||
|
}
|
||||||
|
}).collect(),
|
||||||
|
output: method.fty.sig.output,
|
||||||
|
variadic: false,
|
||||||
|
};
|
||||||
|
let boxed_function_type = ty::BareFnTy {
|
||||||
|
fn_style: method.fty.fn_style,
|
||||||
|
abi: method.fty.abi,
|
||||||
|
sig: boxed_function_type,
|
||||||
|
};
|
||||||
|
let boxed_function_type =
|
||||||
|
ty::mk_bare_fn(tcx, boxed_function_type).subst(tcx, &substs);
|
||||||
|
let function_type =
|
||||||
|
ty::mk_bare_fn(tcx, method.fty.clone()).subst(tcx, &substs);
|
||||||
|
|
||||||
|
let function_name = tcx.map.with_path(method_id.node, |path| {
|
||||||
|
link::mangle_internal_name_by_path_and_seq(path, "unboxing_shim")
|
||||||
|
});
|
||||||
|
let llfn = decl_internal_rust_fn(ccx,
|
||||||
|
boxed_function_type,
|
||||||
|
function_name.as_slice());
|
||||||
|
|
||||||
|
let block_arena = TypedArena::new();
|
||||||
|
let empty_param_substs = param_substs::empty();
|
||||||
|
let return_type = ty::ty_fn_ret(boxed_function_type);
|
||||||
|
let fcx = new_fn_ctxt(ccx,
|
||||||
|
llfn,
|
||||||
|
-1,
|
||||||
|
false,
|
||||||
|
return_type,
|
||||||
|
&empty_param_substs,
|
||||||
|
None,
|
||||||
|
&block_arena);
|
||||||
|
init_function(&fcx, false, return_type);
|
||||||
|
|
||||||
|
// Create the substituted versions of the self type.
|
||||||
|
let mut bcx = fcx.entry_bcx.borrow().clone().unwrap();
|
||||||
|
let arg_scope = fcx.push_custom_cleanup_scope();
|
||||||
|
let arg_scope_id = cleanup::CustomScope(arg_scope);
|
||||||
|
let boxed_arg_types = ty::ty_fn_args(boxed_function_type);
|
||||||
|
let boxed_self_type = *boxed_arg_types.get(0);
|
||||||
|
let arg_types = ty::ty_fn_args(function_type);
|
||||||
|
let self_type = *arg_types.get(0);
|
||||||
|
let boxed_self_kind = arg_kind(&fcx, boxed_self_type);
|
||||||
|
|
||||||
|
// Create a datum for self.
|
||||||
|
let llboxedself = unsafe {
|
||||||
|
llvm::LLVMGetParam(fcx.llfn, fcx.arg_pos(0) as u32)
|
||||||
|
};
|
||||||
|
let llboxedself = Datum::new(llboxedself,
|
||||||
|
boxed_self_type,
|
||||||
|
boxed_self_kind);
|
||||||
|
let boxed_self =
|
||||||
|
unpack_datum!(bcx,
|
||||||
|
llboxedself.to_lvalue_datum_in_scope(bcx,
|
||||||
|
"boxedself",
|
||||||
|
arg_scope_id));
|
||||||
|
|
||||||
|
// This `Load` is needed because lvalue data are always by-ref.
|
||||||
|
let llboxedself = Load(bcx, boxed_self.val);
|
||||||
|
|
||||||
|
let llself = if type_is_immediate(ccx, self_type) {
|
||||||
|
let llboxedself = Load(bcx, llboxedself);
|
||||||
|
immediate_rvalue(llboxedself, self_type)
|
||||||
|
} else {
|
||||||
|
let llself = rvalue_scratch_datum(bcx, self_type, "self");
|
||||||
|
memcpy_ty(bcx, llself.val, llboxedself, self_type);
|
||||||
|
llself
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure we don't free the box twice!
|
||||||
|
boxed_self.kind.post_store(bcx, boxed_self.val, boxed_self_type);
|
||||||
|
|
||||||
|
// Schedule a cleanup to free the box.
|
||||||
|
fcx.schedule_free_value(arg_scope_id,
|
||||||
|
llboxedself,
|
||||||
|
cleanup::HeapExchange,
|
||||||
|
self_type);
|
||||||
|
|
||||||
|
// Now call the function.
|
||||||
|
let mut llshimmedargs = vec!(llself.val);
|
||||||
|
for i in range(1, arg_types.len()) {
|
||||||
|
llshimmedargs.push(unsafe {
|
||||||
|
llvm::LLVMGetParam(fcx.llfn, fcx.arg_pos(i) as u32)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
bcx = trans_call_inner(bcx,
|
||||||
|
None,
|
||||||
|
function_type,
|
||||||
|
|bcx, _| {
|
||||||
|
Callee {
|
||||||
|
bcx: bcx,
|
||||||
|
data: Fn(llshimmedfn),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ArgVals(llshimmedargs.as_slice()),
|
||||||
|
match fcx.llretptr.get() {
|
||||||
|
None => None,
|
||||||
|
Some(llretptr) => Some(expr::SaveIn(llretptr)),
|
||||||
|
}).bcx;
|
||||||
|
|
||||||
|
bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope);
|
||||||
|
finish_fn(&fcx, bcx);
|
||||||
|
|
||||||
|
llfn
|
||||||
|
}
|
||||||
|
|
||||||
pub fn trans_fn_ref_with_vtables(
|
pub fn trans_fn_ref_with_vtables(
|
||||||
bcx: &Block, //
|
bcx: &Block, //
|
||||||
|
@ -492,14 +492,24 @@ fn emit_vtable_methods(bcx: &Block,
|
|||||||
m.repr(tcx),
|
m.repr(tcx),
|
||||||
substs.repr(tcx));
|
substs.repr(tcx));
|
||||||
if m.generics.has_type_params(subst::FnSpace) ||
|
if m.generics.has_type_params(subst::FnSpace) ||
|
||||||
ty::type_has_self(ty::mk_bare_fn(tcx, m.fty.clone()))
|
ty::type_has_self(ty::mk_bare_fn(tcx, m.fty.clone())) {
|
||||||
{
|
|
||||||
debug!("(making impl vtable) method has self or type params: {}",
|
debug!("(making impl vtable) method has self or type params: {}",
|
||||||
token::get_ident(ident));
|
token::get_ident(ident));
|
||||||
C_null(Type::nil(ccx).ptr_to())
|
C_null(Type::nil(ccx).ptr_to())
|
||||||
} else {
|
} else {
|
||||||
trans_fn_ref_with_vtables(bcx, m_id, ExprId(0),
|
let mut fn_ref = trans_fn_ref_with_vtables(bcx,
|
||||||
substs.clone(), vtables.clone())
|
m_id,
|
||||||
|
ExprId(0),
|
||||||
|
substs.clone(),
|
||||||
|
vtables.clone());
|
||||||
|
if m.explicit_self == ast::SelfValue {
|
||||||
|
fn_ref = trans_unboxing_shim(bcx,
|
||||||
|
fn_ref,
|
||||||
|
&*m,
|
||||||
|
m_id,
|
||||||
|
substs.clone());
|
||||||
|
}
|
||||||
|
fn_ref
|
||||||
}
|
}
|
||||||
}).collect()
|
}).collect()
|
||||||
}
|
}
|
||||||
|
@ -271,7 +271,9 @@ fn construct_transformed_self_ty_for_object(
|
|||||||
tcx.sess.span_bug(span, "static method for object type receiver");
|
tcx.sess.span_bug(span, "static method for object type receiver");
|
||||||
}
|
}
|
||||||
ast::SelfValue => {
|
ast::SelfValue => {
|
||||||
ty::mk_err() // error reported in `enforce_object_limitations()`
|
let tr = ty::mk_trait(tcx, trait_def_id, obj_substs,
|
||||||
|
ty::empty_builtin_bounds());
|
||||||
|
ty::mk_uniq(tcx, tr)
|
||||||
}
|
}
|
||||||
ast::SelfRegion(..) | ast::SelfUniq => {
|
ast::SelfRegion(..) | ast::SelfUniq => {
|
||||||
let transformed_self_ty = *method_ty.fty.sig.inputs.get(0);
|
let transformed_self_ty = *method_ty.fty.sig.inputs.get(0);
|
||||||
@ -1225,14 +1227,7 @@ impl<'a> LookupContext<'a> {
|
|||||||
through an object");
|
through an object");
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::SelfValue => { // reason (a) above
|
ast::SelfValue | ast::SelfRegion(..) | ast::SelfUniq => {}
|
||||||
self.tcx().sess.span_err(
|
|
||||||
self.span,
|
|
||||||
"cannot call a method with a by-value receiver \
|
|
||||||
through an object");
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::SelfRegion(..) | ast::SelfUniq => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reason (a) above
|
// reason (a) above
|
||||||
@ -1302,7 +1297,26 @@ impl<'a> LookupContext<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SelfValue => {
|
SelfValue => {
|
||||||
rcvr_matches_ty(self.fcx, rcvr_ty, candidate)
|
debug!("(is relevant?) explicit self is by-value");
|
||||||
|
match ty::get(rcvr_ty).sty {
|
||||||
|
ty::ty_uniq(typ) => {
|
||||||
|
match ty::get(typ).sty {
|
||||||
|
ty::ty_trait(box ty::TyTrait {
|
||||||
|
def_id: self_did,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
rcvr_matches_object(self_did, candidate) ||
|
||||||
|
rcvr_matches_ty(self.fcx,
|
||||||
|
rcvr_ty,
|
||||||
|
candidate)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
rcvr_matches_ty(self.fcx, rcvr_ty, candidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => rcvr_matches_ty(self.fcx, rcvr_ty, candidate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelfRegion(_, m) => {
|
SelfRegion(_, m) => {
|
||||||
|
51
src/test/run-fail/by-value-self-objects-fail.rs
Normal file
51
src/test/run-fail/by-value-self-objects-fail.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// error-pattern:explicit failure
|
||||||
|
|
||||||
|
trait Foo {
|
||||||
|
fn foo(self, x: int);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
z: int,
|
||||||
|
s: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Foo for S {
|
||||||
|
fn foo(self, x: int) {
|
||||||
|
fail!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for S {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("bye 1!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let s = S {
|
||||||
|
x: 2,
|
||||||
|
y: 3,
|
||||||
|
z: 4,
|
||||||
|
s: "hello".to_string(),
|
||||||
|
};
|
||||||
|
let st = box s as Box<Foo>;
|
||||||
|
st.foo(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
77
src/test/run-pass/by-value-self-objects.rs
Normal file
77
src/test/run-pass/by-value-self-objects.rs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
static mut destructor_count: uint = 0;
|
||||||
|
|
||||||
|
trait Foo {
|
||||||
|
fn foo(self, x: int);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
z: int,
|
||||||
|
s: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Foo for S {
|
||||||
|
fn foo(self, x: int) {
|
||||||
|
assert!(self.x == 2);
|
||||||
|
assert!(self.y == 3);
|
||||||
|
assert!(self.z == 4);
|
||||||
|
assert!(self.s.as_slice() == "hello");
|
||||||
|
assert!(x == 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for S {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("bye 1!");
|
||||||
|
unsafe {
|
||||||
|
destructor_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Foo for int {
|
||||||
|
fn foo(self, x: int) {
|
||||||
|
println!("{}", x * x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let s = S {
|
||||||
|
x: 2,
|
||||||
|
y: 3,
|
||||||
|
z: 4,
|
||||||
|
s: "hello".to_string(),
|
||||||
|
};
|
||||||
|
let st = box s as Box<Foo>;
|
||||||
|
st.foo(5);
|
||||||
|
println!("bye 2!");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn g() {
|
||||||
|
let s = 2i;
|
||||||
|
let st = box s as Box<Foo>;
|
||||||
|
st.foo(3);
|
||||||
|
println!("bye 3!");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
assert!(destructor_count == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
g();
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user