Purge the hack that allows FnOnce to be used with a by-value self method. Besides being yucky, it will cause problems if we try to make all traits implement themselves, which would make a lot of things in life easier. Also, it was inextricably linked to Box, which was not the intention. We can work around its absence, so better to reimplement it later in a more thorough fashion.

This commit is contained in:
Niko Matsakis 2014-11-20 12:12:38 -05:00
parent d61338172f
commit 698db04a8d
9 changed files with 42 additions and 303 deletions

View file

@ -19,8 +19,7 @@ pub use self::CalleeData::*;
pub use self::CallArgs::*;
use arena::TypedArena;
use back::abi;
use back::link;
use back::{abi,link};
use session;
use llvm::{ValueRef, get_param};
use llvm;
@ -357,153 +356,6 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
llfn
}
/// 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<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
llshimmedfn: ValueRef,
fty: &ty::BareFnTy<'tcx>,
method_id: ast::DefId,
substs: &subst::Substs<'tcx>)
-> ValueRef {
let _icx = push_ctxt("trans_unboxing_shim");
let ccx = bcx.ccx();
let tcx = bcx.tcx();
let fty = fty.subst(tcx, substs);
// Transform the self type to `Box<self_type>`.
let self_type = fty.sig.inputs[0];
let boxed_self_type = ty::mk_uniq(tcx, self_type);
let boxed_function_type = ty::FnSig {
inputs: fty.sig.inputs.iter().enumerate().map(|(i, typ)| {
if i == 0 {
boxed_self_type
} else {
*typ
}
}).collect(),
output: fty.sig.output,
variadic: false,
};
let boxed_function_type = ty::BareFnTy {
fn_style: fty.fn_style,
abi: fty.abi,
sig: boxed_function_type,
};
let boxed_function_type = ty::mk_bare_fn(tcx, boxed_function_type);
let function_type = match fty.abi {
synabi::RustCall => {
// We're passing through to a RustCall ABI function, but
// because the shim will already perform untupling, we
// need to pretend the shimmed function does not use
// RustCall so the untupled arguments can be passed
// through verbatim. This is kind of ugly.
let fake_ty = ty::FnSig {
inputs: type_of::untuple_arguments_if_necessary(ccx,
fty.sig.inputs.as_slice(),
fty.abi),
output: fty.sig.output,
variadic: false,
};
let fake_ty = ty::BareFnTy {
fn_style: fty.fn_style,
abi: synabi::Rust,
sig: fake_ty,
};
ty::mk_bare_fn(tcx, fake_ty)
}
_ => {
ty::mk_bare_fn(tcx, fty)
}
};
let function_name = ty::with_path(tcx, method_id, |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 = Substs::trans_empty();
let return_type = ty::ty_fn_ret(boxed_function_type);
let fcx = new_fn_ctxt(ccx,
llfn,
ast::DUMMY_NODE_ID,
false,
return_type,
&empty_param_substs,
None,
&block_arena);
let mut bcx = init_function(&fcx, false, return_type);
// Create the substituted versions of the self type.
let arg_scope = fcx.push_custom_cleanup_scope();
let arg_scope_id = cleanup::CustomScope(arg_scope);
let boxed_self_type = ty::ty_fn_args(boxed_function_type)[0];
let arg_types = ty::ty_fn_args(function_type);
let self_type = arg_types[0];
let boxed_self_kind = arg_kind(&fcx, boxed_self_type);
// Create a datum for self.
let llboxedself = get_param(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(get_param(fcx.llfn, fcx.arg_pos(i) as u32));
}
assert!(!fcx.needs_ret_allocas);
let dest = fcx.llretslotptr.get().map(|_|
expr::SaveIn(fcx.get_ret_slot(bcx, return_type, "ret_slot"))
);
bcx = trans_call_inner(bcx,
None,
function_type,
|bcx, _| {
Callee {
bcx: bcx,
data: Fn(llshimmedfn),
}
},
ArgVals(llshimmedargs.as_slice()),
dest).bcx;
bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope);
finish_fn(&fcx, bcx, return_type);
llfn
}
/// Translates a reference to a fn/method item, monomorphizing and
/// inlining as it goes.
///

View file

@ -550,68 +550,12 @@ pub fn get_vtable<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
emit_vtable_methods(bcx, id, substs).into_iter()
}
traits::VtableUnboxedClosure(closure_def_id, substs) => {
// Look up closure type
let self_ty = ty::node_id_to_type(bcx.tcx(), closure_def_id.node);
// Apply substitutions from closure param environment.
// The substitutions should have no type parameters
// remaining after passing through fulfill_obligation
let self_ty = self_ty.subst(bcx.tcx(), &substs);
let mut llfn = trans_fn_ref_with_substs(
let llfn = trans_fn_ref_with_substs(
bcx,
closure_def_id,
ExprId(0),
substs.clone());
{
let unboxed_closures = bcx.tcx()
.unboxed_closures
.borrow();
let closure_info =
unboxed_closures.get(&closure_def_id)
.expect("get_vtable(): didn't find \
unboxed closure");
if closure_info.kind == ty::FnOnceUnboxedClosureKind {
// Untuple the arguments and create an unboxing shim.
let (new_inputs, new_output) = match self_ty.sty {
ty::ty_unboxed_closure(_, _, ref substs) => {
let mut new_inputs = vec![self_ty.clone()];
match closure_info.closure_type.sig.inputs[0].sty {
ty::ty_tup(ref elements) => {
for element in elements.iter() {
new_inputs.push(element.subst(bcx.tcx(), substs));
}
}
_ => {
bcx.tcx().sess.bug("get_vtable(): closure \
type wasn't a tuple")
}
}
(new_inputs,
closure_info.closure_type.sig.output.subst(bcx.tcx(), substs))
},
_ => bcx.tcx().sess.bug("get_vtable(): def wasn't an unboxed closure")
};
let closure_type = ty::BareFnTy {
fn_style: closure_info.closure_type.fn_style,
abi: Rust,
sig: ty::FnSig {
inputs: new_inputs,
output: new_output,
variadic: false,
},
};
debug!("get_vtable(): closure type is {}",
closure_type.repr(bcx.tcx()));
llfn = trans_unboxing_shim(bcx,
llfn,
&closure_type,
closure_def_id,
&substs);
}
}
(vec!(llfn)).into_iter()
}
traits::VtableFnPointer(bare_fn_ty) => {
@ -701,18 +645,15 @@ fn emit_vtable_methods<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
token::get_name(name));
Some(C_null(Type::nil(ccx).ptr_to())).into_iter()
} else {
let mut fn_ref = trans_fn_ref_with_substs(
let fn_ref = trans_fn_ref_with_substs(
bcx,
m_id,
ExprId(0),
substs.clone());
if m.explicit_self == ty::ByValueExplicitSelfCategory {
fn_ref = trans_unboxing_shim(bcx,
fn_ref,
&m.fty,
m_id,
&substs);
}
// currently, at least, by-value self is not object safe
assert!(m.explicit_self != ty::ByValueExplicitSelfCategory);
Some(fn_ref).into_iter()
}
}

View file

@ -315,23 +315,6 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
get_method_index(tcx, &*new_trait_ref,
trait_ref.clone(), method_num);
// FIXME Hacky. By-value `self` methods in objects ought to be
// just a special case of passing ownership of a DST value
// as a parameter. *But* we currently hack them in and tie them to
// the particulars of the `Box` type. So basically for a `fn foo(self,...)`
// method invoked on an object, we don't want the receiver type to be
// `TheTrait`, but rather `Box<TheTrait>`. Yuck.
let mut m = m;
match m.explicit_self {
ty::ByValueExplicitSelfCategory => {
let mut n = (*m).clone();
let self_ty = n.fty.sig.inputs[0];
n.fty.sig.inputs[0] = ty::mk_uniq(tcx, self_ty);
m = Rc::new(n);
}
_ => { }
}
let xform_self_ty =
this.xform_self_ty(&m, &new_trait_ref.substs);

View file

@ -149,14 +149,6 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
object_trait: &ty::TraitRef<'tcx>,
span: Span) {
// Skip the fn_once lang item trait since only the compiler should call
// `call_once` which is the method which takes self by value. What could go
// wrong?
match tcx.lang_items.fn_once_trait() {
Some(def_id) if def_id == object_trait.def_id => return,
_ => {}
}
let trait_items = ty::trait_items(tcx, object_trait.def_id);
let mut errors = Vec::new();

View file

@ -11,29 +11,29 @@
#![feature(unboxed_closures)]
// Test that unboxing shim for calling rust-call ABI methods through a
// trait box works and does not cause an ICE
// trait box works and does not cause an ICE.
struct Foo { foo: uint }
impl FnOnce<(), uint> for Foo {
extern "rust-call" fn call_once(self, _: ()) -> uint { self.foo }
impl FnMut<(), uint> for Foo {
extern "rust-call" fn call_mut(&mut self, _: ()) -> uint { self.foo }
}
impl FnOnce<(uint,), uint> for Foo {
extern "rust-call" fn call_once(self, (x,): (uint,)) -> uint { self.foo + x }
impl FnMut<(uint,), uint> for Foo {
extern "rust-call" fn call_mut(&mut self, (x,): (uint,)) -> uint { self.foo + x }
}
impl FnOnce<(uint, uint), uint> for Foo {
extern "rust-call" fn call_once(self, (x, y): (uint, uint)) -> uint { self.foo + x + y }
impl FnMut<(uint, uint), uint> for Foo {
extern "rust-call" fn call_mut(&mut self, (x, y): (uint, uint)) -> uint { self.foo + x + y }
}
fn main() {
let f = box Foo { foo: 42 } as Box<FnOnce<(), uint>>;
assert_eq!(f.call_once(()), 42);
let mut f = box Foo { foo: 42 } as Box<FnMut<(), uint>>;
assert_eq!(f.call_mut(()), 42);
let f = box Foo { foo: 40 } as Box<FnOnce<(uint,), uint>>;
assert_eq!(f.call_once((2,)), 42);
let mut f = box Foo { foo: 40 } as Box<FnMut<(uint,), uint>>;
assert_eq!(f.call_mut((2,)), 42);
let f = box Foo { foo: 40 } as Box<FnOnce<(uint, uint), uint>>;
assert_eq!(f.call_once((1, 1)), 42);
let mut f = box Foo { foo: 40 } as Box<FnMut<(uint, uint), uint>>;
assert_eq!(f.call_mut((1, 1)), 42);
}

View file

@ -1,19 +0,0 @@
// 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.
// Test that we don't ICE due to encountering unsubstituted type
// parameters when untupling FnOnce parameters during translation of
// an unboxing shim.
#![feature(unboxed_closures)]
fn main() {
let _: Box<FnOnce<(),()>> = box move |&mut:| {};
}

View file

@ -14,26 +14,26 @@
#![feature(unboxed_closures)]
fn main(){
fn bar<'a, T:'a> (t: T) -> Box<FnOnce<(),T> + 'a> {
box move |:| t
fn bar<'a, T:Clone+'a> (t: T) -> Box<FnMut<(),T> + 'a> {
box move |&mut:| t.clone()
}
let f = bar(42u);
assert_eq!(f.call_once(()), 42);
let mut f = bar(42u);
assert_eq!(f.call_mut(()), 42);
let f = bar("forty-two");
assert_eq!(f.call_once(()), "forty-two");
let mut f = bar("forty-two");
assert_eq!(f.call_mut(()), "forty-two");
let x = 42u;
let f = bar(&x);
assert_eq!(f.call_once(()), &x);
let mut f = bar(&x);
assert_eq!(f.call_mut(()), &x);
#[deriving(Show, PartialEq)]
#[deriving(Clone, Show, PartialEq)]
struct Foo(uint, &'static str);
impl Copy for Foo {}
let x = Foo(42, "forty-two");
let f = bar(x);
assert_eq!(f.call_once(()), x);
let mut f = bar(x);
assert_eq!(f.call_mut(()), x);
}

View file

@ -13,7 +13,16 @@
#![feature(unboxed_closures)]
fn main() {
let task: Box<FnOnce(int) -> int> = box |: x| x;
task.call_once((0i, ));
let task: Box<Fn(int) -> int> = box |&: x| x;
task.call((0i, ));
let mut task: Box<FnMut(int) -> int> = box |&mut: x| x;
task.call_mut((0i, ));
call(|:x| x, 22);
}
fn call<F:FnOnce(int) -> int>(f: F, x: int) -> int {
f.call_once((x,))
}

View file

@ -1,19 +0,0 @@
// 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.
#![feature(unboxed_closures)]
use std::ops::FnOnce;
fn main() {
let task: Box<FnOnce(int) -> int> = box |: x| x;
assert!(task.call_once((1234i,)) == 1234i);
}