Skip to content

Commit 5cef124

Browse files
committed
auto merge of #15242 : pcwalton/rust/self-in-trait-methods, r=alexcrichton
I can't believe this worked! I believe that the way the ABI and immediates work mean that this Just Works. Closes #10672. r? @alexcrichton
2 parents 90ab2f8 + 68ead46 commit 5cef124

File tree

7 files changed

+309
-21
lines changed

7 files changed

+309
-21
lines changed

src/librustc/middle/trans/base.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1220,7 +1220,7 @@ pub fn init_function<'a>(fcx: &'a FunctionContext<'a>,
12201220
// - new_fn_ctxt
12211221
// - trans_args
12221222

1223-
fn arg_kind(cx: &FunctionContext, t: ty::t) -> datum::Rvalue {
1223+
pub fn arg_kind(cx: &FunctionContext, t: ty::t) -> datum::Rvalue {
12241224
use middle::trans::datum::{ByRef, ByValue};
12251225

12261226
datum::Rvalue {

src/librustc/middle/trans/builder.rs

+8
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ impl<'a> Builder<'a> {
159159
attributes: &[(uint, u64)])
160160
-> ValueRef {
161161
self.count_insn("invoke");
162+
163+
debug!("Invoke {} with args ({})",
164+
self.ccx.tn.val_to_str(llfn),
165+
args.iter()
166+
.map(|&v| self.ccx.tn.val_to_str(v))
167+
.collect::<Vec<String>>()
168+
.connect(", "));
169+
162170
unsafe {
163171
let v = llvm::LLVMBuildInvoke(self.llbuilder,
164172
llfn,

src/librustc/middle/trans/callee.rs

+134-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
* closure.
1717
*/
1818

19+
use arena::TypedArena;
1920
use back::abi;
21+
use back::link;
2022
use driver::session;
2123
use lib::llvm::ValueRef;
2224
use lib::llvm::llvm;
@@ -33,28 +35,26 @@ use middle::trans::cleanup::CleanupMethods;
3335
use middle::trans::common;
3436
use middle::trans::common::*;
3537
use middle::trans::datum::*;
36-
use middle::trans::datum::Datum;
38+
use middle::trans::datum::{Datum, KindOps};
3739
use middle::trans::expr;
3840
use middle::trans::glue;
3941
use middle::trans::inline;
42+
use middle::trans::foreign;
4043
use middle::trans::meth;
4144
use middle::trans::monomorphize;
45+
use middle::trans::type_::Type;
4246
use middle::trans::type_of;
43-
use middle::trans::foreign;
4447
use middle::ty;
4548
use middle::typeck;
4649
use middle::typeck::coherence::make_substs_for_receiver_types;
4750
use middle::typeck::MethodCall;
4851
use util::ppaux::Repr;
4952

50-
use middle::trans::type_::Type;
51-
53+
use std::gc::Gc;
5254
use syntax::ast;
5355
use synabi = syntax::abi;
5456
use syntax::ast_map;
5557

56-
use std::gc::Gc;
57-
5858
pub struct MethodData {
5959
pub llfn: ValueRef,
6060
pub llself: ValueRef,
@@ -224,6 +224,134 @@ fn resolve_default_method_vtables(bcx: &Block,
224224
param_vtables
225225
}
226226

227+
/// Translates the adapter that deconstructs a `Box<Trait>` object into
228+
/// `Trait` so that a by-value self method can be called.
229+
pub fn trans_unboxing_shim(bcx: &Block,
230+
llshimmedfn: ValueRef,
231+
method: &ty::Method,
232+
method_id: ast::DefId,
233+
substs: subst::Substs)
234+
-> ValueRef {
235+
let _icx = push_ctxt("trans_unboxing_shim");
236+
let ccx = bcx.ccx();
237+
let tcx = bcx.tcx();
238+
239+
// Transform the self type to `Box<self_type>`.
240+
let self_type = *method.fty.sig.inputs.get(0);
241+
let boxed_self_type = ty::mk_uniq(tcx, self_type);
242+
let boxed_function_type = ty::FnSig {
243+
binder_id: method.fty.sig.binder_id,
244+
inputs: method.fty.sig.inputs.iter().enumerate().map(|(i, typ)| {
245+
if i == 0 {
246+
boxed_self_type
247+
} else {
248+
*typ
249+
}
250+
}).collect(),
251+
output: method.fty.sig.output,
252+
variadic: false,
253+
};
254+
let boxed_function_type = ty::BareFnTy {
255+
fn_style: method.fty.fn_style,
256+
abi: method.fty.abi,
257+
sig: boxed_function_type,
258+
};
259+
let boxed_function_type =
260+
ty::mk_bare_fn(tcx, boxed_function_type).subst(tcx, &substs);
261+
let function_type =
262+
ty::mk_bare_fn(tcx, method.fty.clone()).subst(tcx, &substs);
263+
264+
let function_name = tcx.map.with_path(method_id.node, |path| {
265+
link::mangle_internal_name_by_path_and_seq(path, "unboxing_shim")
266+
});
267+
let llfn = decl_internal_rust_fn(ccx,
268+
boxed_function_type,
269+
function_name.as_slice());
270+
271+
let block_arena = TypedArena::new();
272+
let empty_param_substs = param_substs::empty();
273+
let return_type = ty::ty_fn_ret(boxed_function_type);
274+
let fcx = new_fn_ctxt(ccx,
275+
llfn,
276+
-1,
277+
false,
278+
return_type,
279+
&empty_param_substs,
280+
None,
281+
&block_arena);
282+
init_function(&fcx, false, return_type);
283+
284+
// Create the substituted versions of the self type.
285+
let mut bcx = fcx.entry_bcx.borrow().clone().unwrap();
286+
let arg_scope = fcx.push_custom_cleanup_scope();
287+
let arg_scope_id = cleanup::CustomScope(arg_scope);
288+
let boxed_arg_types = ty::ty_fn_args(boxed_function_type);
289+
let boxed_self_type = *boxed_arg_types.get(0);
290+
let arg_types = ty::ty_fn_args(function_type);
291+
let self_type = *arg_types.get(0);
292+
let boxed_self_kind = arg_kind(&fcx, boxed_self_type);
293+
294+
// Create a datum for self.
295+
let llboxedself = unsafe {
296+
llvm::LLVMGetParam(fcx.llfn, fcx.arg_pos(0) as u32)
297+
};
298+
let llboxedself = Datum::new(llboxedself,
299+
boxed_self_type,
300+
boxed_self_kind);
301+
let boxed_self =
302+
unpack_datum!(bcx,
303+
llboxedself.to_lvalue_datum_in_scope(bcx,
304+
"boxedself",
305+
arg_scope_id));
306+
307+
// This `Load` is needed because lvalue data are always by-ref.
308+
let llboxedself = Load(bcx, boxed_self.val);
309+
310+
let llself = if type_is_immediate(ccx, self_type) {
311+
let llboxedself = Load(bcx, llboxedself);
312+
immediate_rvalue(llboxedself, self_type)
313+
} else {
314+
let llself = rvalue_scratch_datum(bcx, self_type, "self");
315+
memcpy_ty(bcx, llself.val, llboxedself, self_type);
316+
llself
317+
};
318+
319+
// Make sure we don't free the box twice!
320+
boxed_self.kind.post_store(bcx, boxed_self.val, boxed_self_type);
321+
322+
// Schedule a cleanup to free the box.
323+
fcx.schedule_free_value(arg_scope_id,
324+
llboxedself,
325+
cleanup::HeapExchange,
326+
self_type);
327+
328+
// Now call the function.
329+
let mut llshimmedargs = vec!(llself.val);
330+
for i in range(1, arg_types.len()) {
331+
llshimmedargs.push(unsafe {
332+
llvm::LLVMGetParam(fcx.llfn, fcx.arg_pos(i) as u32)
333+
});
334+
}
335+
bcx = trans_call_inner(bcx,
336+
None,
337+
function_type,
338+
|bcx, _| {
339+
Callee {
340+
bcx: bcx,
341+
data: Fn(llshimmedfn),
342+
}
343+
},
344+
ArgVals(llshimmedargs.as_slice()),
345+
match fcx.llretptr.get() {
346+
None => None,
347+
Some(llretptr) => Some(expr::SaveIn(llretptr)),
348+
}).bcx;
349+
350+
bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope);
351+
finish_fn(&fcx, bcx);
352+
353+
llfn
354+
}
227355

228356
pub fn trans_fn_ref_with_vtables(
229357
bcx: &Block, //

src/librustc/middle/trans/meth.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -492,14 +492,24 @@ fn emit_vtable_methods(bcx: &Block,
492492
m.repr(tcx),
493493
substs.repr(tcx));
494494
if m.generics.has_type_params(subst::FnSpace) ||
495-
ty::type_has_self(ty::mk_bare_fn(tcx, m.fty.clone()))
496-
{
495+
ty::type_has_self(ty::mk_bare_fn(tcx, m.fty.clone())) {
497496
debug!("(making impl vtable) method has self or type params: {}",
498497
token::get_ident(ident));
499498
C_null(Type::nil(ccx).ptr_to())
500499
} else {
501-
trans_fn_ref_with_vtables(bcx, m_id, ExprId(0),
502-
substs.clone(), vtables.clone())
500+
let mut fn_ref = trans_fn_ref_with_vtables(bcx,
501+
m_id,
502+
ExprId(0),
503+
substs.clone(),
504+
vtables.clone());
505+
if m.explicit_self == ast::SelfValue {
506+
fn_ref = trans_unboxing_shim(bcx,
507+
fn_ref,
508+
&*m,
509+
m_id,
510+
substs.clone());
511+
}
512+
fn_ref
503513
}
504514
}).collect()
505515
}

src/librustc/middle/typeck/check/method.rs

+24-10
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,9 @@ fn construct_transformed_self_ty_for_object(
271271
tcx.sess.span_bug(span, "static method for object type receiver");
272272
}
273273
ast::SelfValue => {
274-
ty::mk_err() // error reported in `enforce_object_limitations()`
274+
let tr = ty::mk_trait(tcx, trait_def_id, obj_substs,
275+
ty::empty_builtin_bounds());
276+
ty::mk_uniq(tcx, tr)
275277
}
276278
ast::SelfRegion(..) | ast::SelfUniq => {
277279
let transformed_self_ty = *method_ty.fty.sig.inputs.get(0);
@@ -1225,14 +1227,7 @@ impl<'a> LookupContext<'a> {
12251227
through an object");
12261228
}
12271229

1228-
ast::SelfValue => { // reason (a) above
1229-
self.tcx().sess.span_err(
1230-
self.span,
1231-
"cannot call a method with a by-value receiver \
1232-
through an object");
1233-
}
1234-
1235-
ast::SelfRegion(..) | ast::SelfUniq => {}
1230+
ast::SelfValue | ast::SelfRegion(..) | ast::SelfUniq => {}
12361231
}
12371232

12381233
// reason (a) above
@@ -1302,7 +1297,26 @@ impl<'a> LookupContext<'a> {
13021297
}
13031298

13041299
SelfValue => {
1305-
rcvr_matches_ty(self.fcx, rcvr_ty, candidate)
1300+
debug!("(is relevant?) explicit self is by-value");
1301+
match ty::get(rcvr_ty).sty {
1302+
ty::ty_uniq(typ) => {
1303+
match ty::get(typ).sty {
1304+
ty::ty_trait(box ty::TyTrait {
1305+
def_id: self_did,
1306+
..
1307+
}) => {
1308+
rcvr_matches_object(self_did, candidate) ||
1309+
rcvr_matches_ty(self.fcx,
1310+
rcvr_ty,
1311+
candidate)
1312+
}
1313+
_ => {
1314+
rcvr_matches_ty(self.fcx, rcvr_ty, candidate)
1315+
}
1316+
}
1317+
}
1318+
_ => rcvr_matches_ty(self.fcx, rcvr_ty, candidate)
1319+
}
13061320
}
13071321

13081322
SelfRegion(_, m) => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// error-pattern:explicit failure
12+
13+
trait Foo {
14+
fn foo(self, x: int);
15+
}
16+
17+
struct S {
18+
x: int,
19+
y: int,
20+
z: int,
21+
s: String,
22+
}
23+
24+
impl Foo for S {
25+
fn foo(self, x: int) {
26+
fail!()
27+
}
28+
}
29+
30+
impl Drop for S {
31+
fn drop(&mut self) {
32+
println!("bye 1!");
33+
}
34+
}
35+
36+
fn f() {
37+
let s = S {
38+
x: 2,
39+
y: 3,
40+
z: 4,
41+
s: "hello".to_string(),
42+
};
43+
let st = box s as Box<Foo>;
44+
st.foo(5);
45+
}
46+
47+
fn main() {
48+
f();
49+
}
50+
51+

0 commit comments

Comments
 (0)