1 // Copyright 2012 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.
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.
11 use arena
::TypedArena
;
13 use llvm
::{ValueRef, get_params}
;
14 use middle
::def_id
::DefId
;
16 use middle
::subst
::{Subst, Substs}
;
28 use trans
::debuginfo
::DebugLoc
;
33 use trans
::monomorphize
;
34 use trans
::type_
::Type
;
35 use trans
::type_of
::*;
36 use middle
::ty
::{self, Ty}
;
37 use middle
::ty
::MethodCall
;
41 use syntax
::codemap
::DUMMY_SP
;
45 // drop_glue pointer, size, align.
46 const VTABLE_OFFSET
: usize = 3;
48 /// The main "translation" pass for methods. Generates code
49 /// for non-monomorphized methods only. Other methods will
50 /// be generated once they are invoked with specific type parameters,
51 /// see `trans::base::lval_static_fn()` or `trans::base::monomorphic_fn()`.
52 pub fn trans_impl(ccx
: &CrateContext
,
54 impl_items
: &[hir
::ImplItem
],
55 generics
: &hir
::Generics
,
57 let _icx
= push_ctxt("meth::trans_impl");
60 debug
!("trans_impl(name={}, id={})", name
, id
);
62 // Both here and below with generic methods, be sure to recurse and look for
63 // items that we need to translate.
64 if !generics
.ty_params
.is_empty() {
68 for impl_item
in impl_items
{
69 match impl_item
.node
{
70 hir
::ImplItemKind
::Method(ref sig
, ref body
) => {
71 if sig
.generics
.ty_params
.is_empty() {
72 let trans_everywhere
= attr
::requests_inline(&impl_item
.attrs
);
73 for (ref ccx
, is_origin
) in ccx
.maybe_iter(trans_everywhere
) {
74 let llfn
= get_item_val(ccx
, impl_item
.id
);
75 let empty_substs
= tcx
.mk_substs(Substs
::trans_empty());
86 if is_origin { OriginalTranslation }
else { InlinedCopy }
);
95 pub fn trans_method_callee
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
96 method_call
: MethodCall
,
97 self_expr
: Option
<&hir
::Expr
>,
98 arg_cleanup_scope
: cleanup
::ScopeId
)
99 -> Callee
<'blk
, 'tcx
> {
100 let _icx
= push_ctxt("meth::trans_method_callee");
102 let method
= bcx
.tcx().tables
.borrow().method_map
[&method_call
];
104 match bcx
.tcx().impl_or_trait_item(method
.def_id
).container() {
105 ty
::ImplContainer(_
) => {
106 debug
!("trans_method_callee: static, {:?}", method
.def_id
);
107 let datum
= callee
::trans_fn_ref(bcx
.ccx(),
109 MethodCallKey(method_call
),
110 bcx
.fcx
.param_substs
);
118 ty
::TraitContainer(trait_def_id
) => {
119 let trait_ref
= method
.substs
.to_trait_ref(bcx
.tcx(), trait_def_id
);
120 let trait_ref
= ty
::Binder(bcx
.monomorphize(&trait_ref
));
121 let span
= bcx
.tcx().map
.span(method_call
.expr_id
);
122 debug
!("method_call={:?} trait_ref={:?} trait_ref id={:?} substs={:?}",
127 let origin
= fulfill_obligation(bcx
.ccx(), span
, trait_ref
);
128 debug
!("origin = {:?}", origin
);
129 trans_monomorphized_callee(bcx
,
141 pub fn trans_static_method_callee
<'a
, 'tcx
>(ccx
: &CrateContext
<'a
, 'tcx
>,
144 expr_id
: ast
::NodeId
,
145 param_substs
: &'tcx subst
::Substs
<'tcx
>)
146 -> Datum
<'tcx
, Rvalue
>
148 let _icx
= push_ctxt("meth::trans_static_method_callee");
151 debug
!("trans_static_method_callee(method_id={:?}, trait_id={}, \
154 tcx
.item_path_str(trait_id
),
157 let mname
= tcx
.item_name(method_id
);
159 debug
!("trans_static_method_callee: method_id={:?}, expr_id={}, \
160 name={}", method_id
, expr_id
, mname
);
162 // Find the substitutions for the fn itself. This includes
163 // type parameters that belong to the trait but also some that
164 // belong to the method:
165 let rcvr_substs
= node_id_substs(ccx
, ExprId(expr_id
), param_substs
);
166 debug
!("rcvr_substs={:?}", rcvr_substs
);
167 let trait_ref
= ty
::Binder(rcvr_substs
.to_trait_ref(tcx
, trait_id
));
168 let vtbl
= fulfill_obligation(ccx
, DUMMY_SP
, trait_ref
);
170 // Now that we know which impl is being used, we can dispatch to
171 // the actual function:
173 traits
::VtableImpl(traits
::VtableImplData
{
174 impl_def_id
: impl_did
,
178 let callee_substs
= impl_substs
.with_method_from(&rcvr_substs
);
179 let mth
= tcx
.get_impl_method(impl_did
, callee_substs
, mname
);
180 trans_fn_ref_with_substs(ccx
, mth
.method
.def_id
, ExprId(expr_id
),
184 traits
::VtableObject(ref data
) => {
185 let idx
= traits
::get_vtable_index_of_object_method(tcx
, data
, method_id
);
186 trans_object_shim(ccx
,
187 data
.upcast_trait_ref
.clone(),
192 // FIXME(#20847): handle at least VtableFnPointer
193 tcx
.sess
.bug(&format
!("static call to invalid vtable: {:?}",
199 fn trans_monomorphized_callee
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
200 method_call
: MethodCall
,
201 self_expr
: Option
<&hir
::Expr
>,
205 vtable
: traits
::Vtable
<'tcx
, ()>,
206 arg_cleanup_scope
: cleanup
::ScopeId
)
207 -> Callee
<'blk
, 'tcx
> {
208 let _icx
= push_ctxt("meth::trans_monomorphized_callee");
210 traits
::VtableImpl(vtable_impl
) => {
212 let impl_did
= vtable_impl
.impl_def_id
;
213 let mname
= match ccx
.tcx().impl_or_trait_item(method_id
) {
214 ty
::MethodTraitItem(method
) => method
.name
,
216 bcx
.tcx().sess
.bug("can't monomorphize a non-method trait \
220 // create a concatenated set of substitutions which includes
221 // those from the impl and those from the method:
222 let meth_substs
= node_id_substs(ccx
,
223 MethodCallKey(method_call
),
224 bcx
.fcx
.param_substs
);
225 let impl_substs
= vtable_impl
.substs
.with_method_from(&meth_substs
);
226 let mth
= bcx
.tcx().get_impl_method(impl_did
, impl_substs
, mname
);
227 // translate the function
228 let datum
= trans_fn_ref_with_substs(bcx
.ccx(),
230 MethodCallKey(method_call
),
231 bcx
.fcx
.param_substs
,
234 Callee { bcx: bcx, data: Fn(datum.val), ty: datum.ty }
236 traits
::VtableClosure(vtable_closure
) => {
237 // The substitutions should have no type parameters remaining
238 // after passing through fulfill_obligation
239 let trait_closure_kind
= bcx
.tcx().lang_items
.fn_trait_kind(trait_id
).unwrap();
240 let llfn
= closure
::trans_closure_method(bcx
.ccx(),
241 vtable_closure
.closure_def_id
,
242 vtable_closure
.substs
,
247 ty
: monomorphize_type(bcx
, method_ty
)
250 traits
::VtableFnPointer(fn_ty
) => {
251 let trait_closure_kind
= bcx
.tcx().lang_items
.fn_trait_kind(trait_id
).unwrap();
252 let llfn
= trans_fn_pointer_shim(bcx
.ccx(), trait_closure_kind
, fn_ty
);
256 ty
: monomorphize_type(bcx
, method_ty
)
259 traits
::VtableObject(ref data
) => {
260 let idx
= traits
::get_vtable_index_of_object_method(bcx
.tcx(), data
, method_id
);
261 if let Some(self_expr
) = self_expr
{
262 if let ty
::TyBareFn(_
, ref fty
) = monomorphize_type(bcx
, method_ty
).sty
{
263 let ty
= bcx
.tcx().mk_fn(None
, opaque_method_ty(bcx
.tcx(), fty
));
264 return trans_trait_callee(bcx
, ty
, idx
, self_expr
, arg_cleanup_scope
);
267 let datum
= trans_object_shim(bcx
.ccx(),
268 data
.upcast_trait_ref
.clone(),
271 Callee { bcx: bcx, data: Fn(datum.val), ty: datum.ty }
273 traits
::VtableBuiltin(..) |
274 traits
::VtableDefaultImpl(..) |
275 traits
::VtableParam(..) => {
277 &format
!("resolved vtable bad vtable {:?} in trans",
283 /// Create a method callee where the method is coming from a trait object (e.g., Box<Trait> type).
284 /// In this case, we must pull the fn pointer out of the vtable that is packaged up with the
285 /// object. Objects are represented as a pair, so we first evaluate the self expression and then
286 /// extract the self data and vtable out of the pair.
287 fn trans_trait_callee
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
288 opaque_fn_ty
: Ty
<'tcx
>,
290 self_expr
: &hir
::Expr
,
291 arg_cleanup_scope
: cleanup
::ScopeId
)
292 -> Callee
<'blk
, 'tcx
> {
293 let _icx
= push_ctxt("meth::trans_trait_callee");
296 // Translate self_datum and take ownership of the value by
297 // converting to an rvalue.
298 let self_datum
= unpack_datum
!(
299 bcx
, expr
::trans(bcx
, self_expr
));
301 let llval
= if bcx
.fcx
.type_needs_drop(self_datum
.ty
) {
302 let self_datum
= unpack_datum
!(
303 bcx
, self_datum
.to_rvalue_datum(bcx
, "trait_callee"));
305 // Convert to by-ref since `trans_trait_callee_from_llval` wants it
307 let self_datum
= unpack_datum
!(
308 bcx
, self_datum
.to_ref_datum(bcx
));
310 // Arrange cleanup in case something should go wrong before the
311 // actual call occurs.
312 self_datum
.add_clean(bcx
.fcx
, arg_cleanup_scope
)
314 // We don't have to do anything about cleanups for &Trait and &mut Trait.
315 assert
!(self_datum
.kind
.is_by_ref());
319 let llself
= Load(bcx
, expr
::get_dataptr(bcx
, llval
));
320 let llvtable
= Load(bcx
, expr
::get_meta(bcx
, llval
));
321 trans_trait_callee_from_llval(bcx
, opaque_fn_ty
, vtable_index
, llself
, llvtable
)
324 /// Same as `trans_trait_callee()` above, except that it is given a by-ref pointer to the object
326 fn trans_trait_callee_from_llval
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
327 opaque_fn_ty
: Ty
<'tcx
>,
331 -> Callee
<'blk
, 'tcx
> {
332 let _icx
= push_ctxt("meth::trans_trait_callee");
335 // Load the data pointer from the object.
336 debug
!("trans_trait_callee_from_llval(callee_ty={}, vtable_index={}, llself={}, llvtable={})",
339 bcx
.val_to_string(llself
),
340 bcx
.val_to_string(llvtable
));
342 // Replace the self type (&Self or Box<Self>) with an opaque pointer.
343 let mptr
= Load(bcx
, GEPi(bcx
, llvtable
, &[vtable_index
+ VTABLE_OFFSET
]));
344 let llcallee_ty
= type_of_fn_from_ty(ccx
, opaque_fn_ty
);
348 data
: TraitItem(MethodData
{
349 llfn
: PointerCast(bcx
, mptr
, llcallee_ty
.ptr_to()),
350 llself
: PointerCast(bcx
, llself
, Type
::i8p(ccx
)),
356 /// Generate a shim function that allows an object type like `SomeTrait` to
357 /// implement the type `SomeTrait`. Imagine a trait definition:
359 /// trait SomeTrait { fn get(&self) -> i32; ... }
361 /// And a generic bit of code:
363 /// fn foo<T:SomeTrait>(t: &T) {
364 /// let x = SomeTrait::get;
368 /// What is the value of `x` when `foo` is invoked with `T=SomeTrait`?
369 /// The answer is that it is a shim function generated by this routine:
371 /// fn shim(t: &SomeTrait) -> i32 {
372 /// // ... call t.get() virtually ...
375 /// In fact, all virtual calls can be thought of as normal trait calls
376 /// that go through this shim function.
377 pub fn trans_object_shim
<'a
, 'tcx
>(
378 ccx
: &'a CrateContext
<'a
, 'tcx
>,
379 upcast_trait_ref
: ty
::PolyTraitRef
<'tcx
>,
382 -> Datum
<'tcx
, Rvalue
>
384 let _icx
= push_ctxt("trans_object_shim");
387 debug
!("trans_object_shim(upcast_trait_ref={:?}, method_id={:?})",
391 // Upcast to the trait in question and extract out the substitutions.
392 let upcast_trait_ref
= tcx
.erase_late_bound_regions(&upcast_trait_ref
);
393 let object_substs
= upcast_trait_ref
.substs
.clone().erase_regions();
394 debug
!("trans_object_shim: object_substs={:?}", object_substs
);
396 // Lookup the type of this method as declared in the trait and apply substitutions.
397 let method_ty
= match tcx
.impl_or_trait_item(method_id
) {
398 ty
::MethodTraitItem(method
) => method
,
400 tcx
.sess
.bug("can't create a method shim for a non-method item")
403 let fty
= monomorphize
::apply_param_substs(tcx
, &object_substs
, &method_ty
.fty
);
404 let fty
= tcx
.mk_bare_fn(fty
);
405 let method_ty
= opaque_method_ty(tcx
, fty
);
406 debug
!("trans_object_shim: fty={:?} method_ty={:?}", fty
, method_ty
);
409 let shim_fn_ty
= tcx
.mk_fn(None
, fty
);
410 let method_bare_fn_ty
= tcx
.mk_fn(None
, method_ty
);
411 let function_name
= link
::mangle_internal_name_by_type_and_seq(ccx
, shim_fn_ty
, "object_shim");
412 let llfn
= declare
::define_internal_rust_fn(ccx
, &function_name
, shim_fn_ty
);
414 let sig
= ccx
.tcx().erase_late_bound_regions(&fty
.sig
);
415 let sig
= infer
::normalize_associated_type(ccx
.tcx(), &sig
);
417 let empty_substs
= tcx
.mk_substs(Substs
::trans_empty());
418 let (block_arena
, fcx
): (TypedArena
<_
>, FunctionContext
);
419 block_arena
= TypedArena
::new();
420 fcx
= new_fn_ctxt(ccx
,
428 let mut bcx
= init_function(&fcx
, false, sig
.output
);
430 let llargs
= get_params(fcx
.llfn
);
432 let self_idx
= fcx
.arg_offset();
433 let llself
= llargs
[self_idx
];
434 let llvtable
= llargs
[self_idx
+ 1];
436 debug
!("trans_object_shim: llself={}, llvtable={}",
437 bcx
.val_to_string(llself
), bcx
.val_to_string(llvtable
));
439 assert
!(!fcx
.needs_ret_allocas
);
442 fcx
.llretslotptr
.get().map(
443 |_
| expr
::SaveIn(fcx
.get_ret_slot(bcx
, sig
.output
, "ret_slot")));
445 debug
!("trans_object_shim: method_offset_in_vtable={}",
448 bcx
= trans_call_inner(bcx
,
450 |bcx
, _
| trans_trait_callee_from_llval(bcx
,
454 ArgVals(&llargs
[(self_idx
+ 2)..]),
457 finish_fn(&fcx
, bcx
, sig
.output
, DebugLoc
::None
);
459 immediate_rvalue(llfn
, shim_fn_ty
)
462 /// Creates a returns a dynamic vtable for the given type and vtable origin.
463 /// This is used only for objects.
465 /// The `trait_ref` encodes the erased self type. Hence if we are
466 /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
467 /// `trait_ref` would map `T:Trait`.
468 pub fn get_vtable
<'a
, 'tcx
>(ccx
: &CrateContext
<'a
, 'tcx
>,
469 trait_ref
: ty
::PolyTraitRef
<'tcx
>,
470 param_substs
: &'tcx subst
::Substs
<'tcx
>)
474 let _icx
= push_ctxt("meth::get_vtable");
476 debug
!("get_vtable(trait_ref={:?})", trait_ref
);
479 match ccx
.vtables().borrow().get(&trait_ref
) {
480 Some(&val
) => { return val }
484 // Not in the cache. Build it.
485 let methods
= traits
::supertraits(tcx
, trait_ref
.clone()).flat_map(|trait_ref
| {
486 let vtable
= fulfill_obligation(ccx
, DUMMY_SP
, trait_ref
.clone());
488 // Should default trait error here?
489 traits
::VtableDefaultImpl(_
) |
490 traits
::VtableBuiltin(_
) => {
491 Vec
::new().into_iter()
494 traits
::VtableImplData
{
498 let nullptr
= C_null(Type
::nil(ccx
).ptr_to());
499 get_vtable_methods(ccx
, id
, substs
)
504 trans_fn_ref_with_substs(ccx
,
516 traits
::VtableClosure(
517 traits
::VtableClosureData
{
521 let trait_closure_kind
= tcx
.lang_items
.fn_trait_kind(trait_ref
.def_id()).unwrap();
522 let llfn
= closure
::trans_closure_method(ccx
,
526 vec
![llfn
].into_iter()
528 traits
::VtableFnPointer(bare_fn_ty
) => {
529 let trait_closure_kind
= tcx
.lang_items
.fn_trait_kind(trait_ref
.def_id()).unwrap();
530 vec
![trans_fn_pointer_shim(ccx
, trait_closure_kind
, bare_fn_ty
)].into_iter()
532 traits
::VtableObject(ref data
) => {
533 // this would imply that the Self type being erased is
534 // an object type; this cannot happen because we
535 // cannot cast an unsized type into a trait object
537 &format
!("cannot get vtable for an object type: {:?}",
540 traits
::VtableParam(..) => {
542 &format
!("resolved vtable for {:?} to bad vtable {:?} in trans",
549 let size_ty
= sizing_type_of(ccx
, trait_ref
.self_ty());
550 let size
= machine
::llsize_of_alloc(ccx
, size_ty
);
551 let align
= align_of(ccx
, trait_ref
.self_ty());
553 let components
: Vec
<_
> = vec
![
554 // Generate a destructor for the vtable.
555 glue
::get_drop_glue(ccx
, trait_ref
.self_ty()),
558 ].into_iter().chain(methods
).collect();
560 let vtable_const
= C_struct(ccx
, &components
, false);
561 let align
= machine
::llalign_of_pref(ccx
, val_ty(vtable_const
));
562 let vtable
= consts
::addr_of(ccx
, vtable_const
, align
, "vtable");
564 ccx
.vtables().borrow_mut().insert(trait_ref
, vtable
);
568 pub fn get_vtable_methods
<'a
, 'tcx
>(ccx
: &CrateContext
<'a
, 'tcx
>,
570 substs
: subst
::Substs
<'tcx
>)
571 -> Vec
<Option
<ty
::util
::ImplMethod
<'tcx
>>>
575 debug
!("get_vtable_methods(impl_id={:?}, substs={:?}", impl_id
, substs
);
577 let trt_id
= match tcx
.impl_trait_ref(impl_id
) {
578 Some(t_id
) => t_id
.def_id
,
579 None
=> ccx
.sess().bug("make_impl_vtable: don't know how to \
580 make a vtable for a type impl!")
583 tcx
.populate_implementations_for_trait_if_necessary(trt_id
);
585 let trait_item_def_ids
= tcx
.trait_item_def_ids(trt_id
);
589 // Filter out non-method items.
590 .filter_map(|item_def_id
| {
592 ty
::MethodTraitItemId(def_id
) => Some(def_id
),
597 // Now produce pointers for each remaining method. If the
598 // method could never be called from this object, just supply
600 .map(|trait_method_def_id
| {
601 debug
!("get_vtable_methods: trait_method_def_id={:?}",
602 trait_method_def_id
);
604 let trait_method_type
= match tcx
.impl_or_trait_item(trait_method_def_id
) {
605 ty
::MethodTraitItem(m
) => m
,
606 _
=> ccx
.sess().bug("should be a method, not other assoc item"),
608 let name
= trait_method_type
.name
;
610 // Some methods cannot be called on an object; skip those.
611 if !traits
::is_vtable_safe_method(tcx
, trt_id
, &trait_method_type
) {
612 debug
!("get_vtable_methods: not vtable safe");
616 debug
!("get_vtable_methods: trait_method_type={:?}",
619 // The substitutions we have are on the impl, so we grab
620 // the method type from the impl to substitute into.
621 let mth
= tcx
.get_impl_method(impl_id
, substs
.clone(), name
);
623 debug
!("get_vtable_methods: mth={:?}", mth
);
625 // If this is a default method, it's possible that it
626 // relies on where clauses that do not hold for this
627 // particular set of type parameters. Note that this
628 // method could then never be called, so we do not want to
629 // try and trans it, in that case. Issue #23435.
631 let predicates
= mth
.method
.predicates
.predicates
.subst(tcx
, &mth
.substs
);
632 if !normalize_and_test_predicates(ccx
, predicates
.into_vec()) {
633 debug
!("get_vtable_methods: predicates do not hold");
643 /// Replace the self type (&Self or Box<Self>) with an opaque pointer.
644 fn opaque_method_ty
<'tcx
>(tcx
: &ty
::ctxt
<'tcx
>, method_ty
: &ty
::BareFnTy
<'tcx
>)
645 -> &'tcx ty
::BareFnTy
<'tcx
> {
646 let mut inputs
= method_ty
.sig
.0.inputs
.clone();
647 inputs
[0] = tcx
.mk_mut_ptr(tcx
.mk_mach_int(ast
::IntTy
::I8
));
649 tcx
.mk_bare_fn(ty
::BareFnTy
{
650 unsafety
: method_ty
.unsafety
,
652 sig
: ty
::Binder(ty
::FnSig
{
654 output
: method_ty
.sig
.0.output
,
655 variadic
: method_ty
.sig
.0.variadic
,