From 68ead460f9029fd8de508a46fe944afb83e99da3 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sat, 28 Jun 2014 12:55:17 -0700 Subject: [PATCH] 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. --- src/librustc/middle/trans/base.rs | 2 +- src/librustc/middle/trans/builder.rs | 8 + src/librustc/middle/trans/callee.rs | 140 +++++++++++++++++- src/librustc/middle/trans/meth.rs | 18 ++- src/librustc/middle/typeck/check/method.rs | 34 +++-- .../run-fail/by-value-self-objects-fail.rs | 51 +++++++ src/test/run-pass/by-value-self-objects.rs | 77 ++++++++++ 7 files changed, 309 insertions(+), 21 deletions(-) create mode 100644 src/test/run-fail/by-value-self-objects-fail.rs create mode 100644 src/test/run-pass/by-value-self-objects.rs diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 210de1946c9..75271804b79 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1220,7 +1220,7 @@ pub fn init_function<'a>(fcx: &'a FunctionContext<'a>, // - new_fn_ctxt // - 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}; datum::Rvalue { diff --git a/src/librustc/middle/trans/builder.rs b/src/librustc/middle/trans/builder.rs index c64f4cfa877..a9c1adac3d7 100644 --- a/src/librustc/middle/trans/builder.rs +++ b/src/librustc/middle/trans/builder.rs @@ -159,6 +159,14 @@ impl<'a> Builder<'a> { attributes: &[(uint, u64)]) -> ValueRef { 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::>() + .connect(", ")); + unsafe { let v = llvm::LLVMBuildInvoke(self.llbuilder, llfn, diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 8b484e90898..116b2e6b421 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -16,7 +16,9 @@ * closure. */ +use arena::TypedArena; use back::abi; +use back::link; use driver::session; use lib::llvm::ValueRef; use lib::llvm::llvm; @@ -33,28 +35,26 @@ use middle::trans::cleanup::CleanupMethods; use middle::trans::common; use middle::trans::common::*; use middle::trans::datum::*; -use middle::trans::datum::Datum; +use middle::trans::datum::{Datum, KindOps}; use middle::trans::expr; use middle::trans::glue; use middle::trans::inline; +use middle::trans::foreign; use middle::trans::meth; use middle::trans::monomorphize; +use middle::trans::type_::Type; use middle::trans::type_of; -use middle::trans::foreign; use middle::ty; use middle::typeck; use middle::typeck::coherence::make_substs_for_receiver_types; use middle::typeck::MethodCall; use util::ppaux::Repr; -use middle::trans::type_::Type; - +use std::gc::Gc; use syntax::ast; use synabi = syntax::abi; use syntax::ast_map; -use std::gc::Gc; - pub struct MethodData { pub llfn: ValueRef, pub llself: ValueRef, @@ -224,6 +224,134 @@ fn resolve_default_method_vtables(bcx: &Block, param_vtables } +/// Translates the adapter that deconstructs a `Box` 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`. + 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( bcx: &Block, // diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 85660cd2eb5..e1d43c52400 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -492,14 +492,24 @@ fn emit_vtable_methods(bcx: &Block, m.repr(tcx), substs.repr(tcx)); 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: {}", token::get_ident(ident)); C_null(Type::nil(ccx).ptr_to()) } else { - trans_fn_ref_with_vtables(bcx, m_id, ExprId(0), - substs.clone(), vtables.clone()) + let mut fn_ref = trans_fn_ref_with_vtables(bcx, + 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() } diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 4270ff1e795..c3b2756bdbf 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -271,7 +271,9 @@ fn construct_transformed_self_ty_for_object( tcx.sess.span_bug(span, "static method for object type receiver"); } 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 => { let transformed_self_ty = *method_ty.fty.sig.inputs.get(0); @@ -1225,14 +1227,7 @@ impl<'a> LookupContext<'a> { through an object"); } - ast::SelfValue => { // reason (a) above - self.tcx().sess.span_err( - self.span, - "cannot call a method with a by-value receiver \ - through an object"); - } - - ast::SelfRegion(..) | ast::SelfUniq => {} + ast::SelfValue | ast::SelfRegion(..) | ast::SelfUniq => {} } // reason (a) above @@ -1302,7 +1297,26 @@ impl<'a> LookupContext<'a> { } 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) => { diff --git a/src/test/run-fail/by-value-self-objects-fail.rs b/src/test/run-fail/by-value-self-objects-fail.rs new file mode 100644 index 00000000000..74889263cc8 --- /dev/null +++ b/src/test/run-fail/by-value-self-objects-fail.rs @@ -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 or the MIT license +// , 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; + st.foo(5); +} + +fn main() { + f(); +} + + diff --git a/src/test/run-pass/by-value-self-objects.rs b/src/test/run-pass/by-value-self-objects.rs new file mode 100644 index 00000000000..3a588367a97 --- /dev/null +++ b/src/test/run-pass/by-value-self-objects.rs @@ -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 or the MIT license +// , 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; + st.foo(5); + println!("bye 2!"); +} + +fn g() { + let s = 2i; + let st = box s as Box; + st.foo(3); + println!("bye 3!"); +} + +fn main() { + f(); + + unsafe { + assert!(destructor_count == 1); + } + + g(); +} +