]> git.lizzy.rs Git - rust.git/commitdiff
librustc: Permit by-value-self methods to be invoked on objects
authorPatrick Walton <pcwalton@mimiga.net>
Sat, 28 Jun 2014 19:55:17 +0000 (12:55 -0700)
committerPatrick Walton <pcwalton@mimiga.net>
Tue, 1 Jul 2014 01:43:31 +0000 (18:43 -0700)
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
src/librustc/middle/trans/builder.rs
src/librustc/middle/trans/callee.rs
src/librustc/middle/trans/meth.rs
src/librustc/middle/typeck/check/method.rs
src/test/run-fail/by-value-self-objects-fail.rs [new file with mode: 0644]
src/test/run-pass/by-value-self-objects.rs [new file with mode: 0644]

index 210de1946c9e6f9ec8deef4faf185821439b460c..75271804b7911b32be2380cbfffc9d8015e824dc 100644 (file)
@@ -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 {
index c64f4cfa8779887d8d341457866027e896379573..a9c1adac3d7cf01e07c3926d4706708e8a087367 100644 (file)
@@ -159,6 +159,14 @@ pub fn invoke(&self,
                   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::<Vec<String>>()
+                   .connect(", "));
+
         unsafe {
             let v = llvm::LLVMBuildInvoke(self.llbuilder,
                                           llfn,
index 8b484e90898c97d2846080b80ca88efaa123b879..116b2e6b421b81e41e1c5a94064853528c57897a 100644 (file)
@@ -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;
 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<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(
     bcx: &Block,                 //
index 85660cd2eb540c880d13f0ebedf74f7ca67d679a..e1d43c5240059ee03fd51e40f451269ec123f17d 100644 (file)
@@ -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()
 }
index 4270ff1e7952d450967f652e4d4f29f616789059..c3b2756bdbff2f7f165af2718ec6a1fe293a7452 100644 (file)
@@ -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 @@ fn enforce_object_limitations(&self, candidate: &Candidate) {
                      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 @@ fn is_relevant(&self, rcvr_ty: ty::t, candidate: &Candidate) -> bool {
             }
 
             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 (file)
index 0000000..7488926
--- /dev/null
@@ -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();
+}
+
+
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 (file)
index 0000000..3a58836
--- /dev/null
@@ -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();
+}
+