]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/trans/meth.rs
Refactor core specialization and subst translation code to avoid
[rust.git] / src / librustc_trans / trans / meth.rs
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.
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 use std::rc::Rc;
12
13 use arena::TypedArena;
14 use back::link;
15 use llvm::{ValueRef, get_params};
16 use middle::def_id::DefId;
17 use middle::infer;
18 use middle::subst::{Subst, Substs};
19 use middle::subst;
20 use middle::traits;
21 use trans::base::*;
22 use trans::build::*;
23 use trans::callee::{Callee, Virtual, ArgVals,
24                     trans_fn_pointer_shim, trans_fn_ref_with_substs};
25 use trans::closure;
26 use trans::common::*;
27 use trans::consts;
28 use trans::datum::*;
29 use trans::debuginfo::DebugLoc;
30 use trans::declare;
31 use trans::expr;
32 use trans::glue;
33 use trans::machine;
34 use trans::type_::Type;
35 use trans::type_of::*;
36 use middle::ty::{self, Ty, TyCtxt, TypeFoldable};
37 use middle::ty::MethodCall;
38
39 use syntax::ast::{self, Name};
40 use syntax::attr;
41 use syntax::codemap::DUMMY_SP;
42
43 use rustc_front::hir;
44
45 // drop_glue pointer, size, align.
46 const VTABLE_OFFSET: usize = 3;
47
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,
53                   name: ast::Name,
54                   impl_items: &[hir::ImplItem],
55                   generics: &hir::Generics,
56                   id: ast::NodeId) {
57     let _icx = push_ctxt("meth::trans_impl");
58     let tcx = ccx.tcx();
59
60     debug!("trans_impl(name={}, id={})", name, id);
61
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() {
65         return;
66     }
67
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());
76                         trans_fn(ccx,
77                                  &sig.decl,
78                                  body,
79                                  llfn,
80                                  empty_substs,
81                                  impl_item.id,
82                                  &impl_item.attrs);
83                         update_linkage(ccx,
84                                        llfn,
85                                        Some(impl_item.id),
86                                        if is_origin { OriginalTranslation } else { InlinedCopy });
87                     }
88                 }
89             }
90             _ => {}
91         }
92     }
93 }
94
95 /// Compute the appropriate callee, give na method's ID, trait ID,
96 /// substitutions and a Vtable for that trait.
97 pub fn callee_for_trait_impl<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
98                                        method_id: DefId,
99                                        substs: &'tcx subst::Substs<'tcx>,
100                                        trait_id: DefId,
101                                        method_ty: Ty<'tcx>,
102                                        vtable: traits::Vtable<'tcx, ()>)
103                                        -> Callee<'tcx> {
104     let _icx = push_ctxt("meth::callee_for_trait_impl");
105     match vtable {
106         traits::VtableImpl(vtable_impl) => {
107             let impl_did = vtable_impl.impl_def_id;
108             let mname = ccx.tcx().item_name(method_id);
109             // create a concatenated set of substitutions which includes
110             // those from the impl and those from the method:
111             let impl_substs = vtable_impl.substs.with_method_from(&substs);
112             let substs = ccx.tcx().mk_substs(impl_substs);
113             let mth = get_impl_method(ccx.tcx(), impl_did, impl_substs, mname);
114
115             // Translate the function, bypassing Callee::def.
116             // That is because default methods have the same ID as the
117             // trait method used to look up the impl method that ended
118             // up here, so calling Callee::def would infinitely recurse.
119             Callee::ptr(trans_fn_ref_with_substs(ccx, mth.method.def_id,
120                                                  Some(method_ty), mth.substs))
121         }
122         traits::VtableClosure(vtable_closure) => {
123             // The substitutions should have no type parameters remaining
124             // after passing through fulfill_obligation
125             let trait_closure_kind = ccx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
126             let llfn = closure::trans_closure_method(ccx,
127                                                      vtable_closure.closure_def_id,
128                                                      vtable_closure.substs,
129                                                      trait_closure_kind);
130             let fn_ptr_ty = match method_ty.sty {
131                 ty::TyFnDef(_, _, fty) => ccx.tcx().mk_ty(ty::TyFnPtr(fty)),
132                 _ => unreachable!("expected fn item type, found {}",
133                                   method_ty)
134             };
135             Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
136         }
137         traits::VtableFnPointer(fn_ty) => {
138             let trait_closure_kind = ccx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
139             let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty);
140             let fn_ptr_ty = match method_ty.sty {
141                 ty::TyFnDef(_, _, fty) => ccx.tcx().mk_ty(ty::TyFnPtr(fty)),
142                 _ => unreachable!("expected fn item type, found {}",
143                                   method_ty)
144             };
145             Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
146         }
147         traits::VtableObject(ref data) => {
148             Callee {
149                 data: Virtual(traits::get_vtable_index_of_object_method(
150                     ccx.tcx(), data, method_id)),
151                 ty: method_ty
152             }
153         }
154         traits::VtableBuiltin(..) |
155         traits::VtableDefaultImpl(..) |
156         traits::VtableParam(..) => {
157             ccx.sess().bug(
158                 &format!("resolved vtable bad vtable {:?} in trans",
159                         vtable));
160         }
161     }
162 }
163
164 /// Extracts a method from a trait object's vtable, at the
165 /// specified index, and casts it to the given type.
166 pub fn get_virtual_method<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
167                                       llvtable: ValueRef,
168                                       vtable_index: usize,
169                                       method_ty: Ty<'tcx>)
170                                       -> Datum<'tcx, Rvalue> {
171     let _icx = push_ctxt("meth::get_virtual_method");
172     let ccx = bcx.ccx();
173
174     // Load the data pointer from the object.
175     debug!("get_virtual_method(callee_ty={}, vtable_index={}, llvtable={})",
176            method_ty,
177            vtable_index,
178            bcx.val_to_string(llvtable));
179
180     let mptr = Load(bcx, GEPi(bcx, llvtable, &[vtable_index + VTABLE_OFFSET]));
181
182     // Replace the self type (&Self or Box<Self>) with an opaque pointer.
183     if let ty::TyFnDef(_, _, fty) = method_ty.sty {
184         let opaque_ty = opaque_method_ty(ccx.tcx(), fty);
185         immediate_rvalue(PointerCast(bcx, mptr, type_of(ccx, opaque_ty)), opaque_ty)
186     } else {
187         immediate_rvalue(mptr, method_ty)
188     }
189 }
190
191 /// Generate a shim function that allows an object type like `SomeTrait` to
192 /// implement the type `SomeTrait`. Imagine a trait definition:
193 ///
194 ///    trait SomeTrait { fn get(&self) -> i32; ... }
195 ///
196 /// And a generic bit of code:
197 ///
198 ///    fn foo<T:SomeTrait>(t: &T) {
199 ///        let x = SomeTrait::get;
200 ///        x(t)
201 ///    }
202 ///
203 /// What is the value of `x` when `foo` is invoked with `T=SomeTrait`?
204 /// The answer is that it is a shim function generated by this routine:
205 ///
206 ///    fn shim(t: &SomeTrait) -> i32 {
207 ///        // ... call t.get() virtually ...
208 ///    }
209 ///
210 /// In fact, all virtual calls can be thought of as normal trait calls
211 /// that go through this shim function.
212 pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
213                                    method_ty: Ty<'tcx>,
214                                    vtable_index: usize)
215                                    -> Datum<'tcx, Rvalue> {
216     let _icx = push_ctxt("trans_object_shim");
217     let tcx = ccx.tcx();
218
219     debug!("trans_object_shim(vtable_index={}, method_ty={:?})",
220            vtable_index,
221            method_ty);
222
223     let ret_ty = tcx.erase_late_bound_regions(&method_ty.fn_ret());
224     let ret_ty = infer::normalize_associated_type(tcx, &ret_ty);
225
226     let shim_fn_ty = match method_ty.sty {
227         ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)),
228         _ => unreachable!("expected fn item type, found {}", method_ty)
229     };
230
231     //
232     let function_name = link::mangle_internal_name_by_type_and_seq(ccx, shim_fn_ty, "object_shim");
233     let llfn = declare::define_internal_rust_fn(ccx, &function_name, shim_fn_ty);
234
235     let empty_substs = tcx.mk_substs(Substs::trans_empty());
236     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
237     block_arena = TypedArena::new();
238     fcx = new_fn_ctxt(ccx,
239                       llfn,
240                       ast::DUMMY_NODE_ID,
241                       false,
242                       ret_ty,
243                       empty_substs,
244                       None,
245                       &block_arena);
246     let mut bcx = init_function(&fcx, false, ret_ty);
247
248     let llargs = get_params(fcx.llfn);
249
250     let self_idx = fcx.arg_offset();
251     let llself = llargs[self_idx];
252     let llvtable = llargs[self_idx + 1];
253
254     debug!("trans_object_shim: llself={}, llvtable={}",
255            bcx.val_to_string(llself), bcx.val_to_string(llvtable));
256
257     assert!(!fcx.needs_ret_allocas);
258
259     let dest =
260         fcx.llretslotptr.get().map(
261             |_| expr::SaveIn(fcx.get_ret_slot(bcx, ret_ty, "ret_slot")));
262
263     debug!("trans_object_shim: method_offset_in_vtable={}",
264            vtable_index);
265
266     let callee = Callee {
267         data: Virtual(vtable_index),
268         ty: method_ty
269     };
270     bcx = callee.call(bcx, DebugLoc::None, ArgVals(&llargs[self_idx..]), dest).bcx;
271
272     finish_fn(&fcx, bcx, ret_ty, DebugLoc::None);
273
274     immediate_rvalue(llfn, shim_fn_ty)
275 }
276
277 /// Creates a returns a dynamic vtable for the given type and vtable origin.
278 /// This is used only for objects.
279 ///
280 /// The `trait_ref` encodes the erased self type. Hence if we are
281 /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
282 /// `trait_ref` would map `T:Trait`.
283 pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
284                             trait_ref: ty::PolyTraitRef<'tcx>)
285                             -> ValueRef
286 {
287     let tcx = ccx.tcx();
288     let _icx = push_ctxt("meth::get_vtable");
289
290     debug!("get_vtable(trait_ref={:?})", trait_ref);
291
292     // Check the cache.
293     match ccx.vtables().borrow().get(&trait_ref) {
294         Some(&val) => { return val }
295         None => { }
296     }
297
298     // Not in the cache. Build it.
299     let methods = traits::supertraits(tcx, trait_ref.clone()).flat_map(|trait_ref| {
300         let vtable = fulfill_obligation(ccx, DUMMY_SP, trait_ref.clone());
301         match vtable {
302             // Should default trait error here?
303             traits::VtableDefaultImpl(_) |
304             traits::VtableBuiltin(_) => {
305                 Vec::new().into_iter()
306             }
307             traits::VtableImpl(
308                 traits::VtableImplData {
309                     impl_def_id: id,
310                     substs,
311                     nested: _ }) => {
312                 let nullptr = C_null(Type::nil(ccx).ptr_to());
313                 get_vtable_methods(ccx, id, substs)
314                     .into_iter()
315                     .map(|opt_mth| {
316                         match opt_mth {
317                             Some(mth) => {
318                                 trans_fn_ref_with_substs(ccx,
319                                                          mth.method.def_id,
320                                                          None,
321                                                          mth.substs).val
322                             }
323                             None => nullptr
324                         }
325                     })
326                     .collect::<Vec<_>>()
327                     .into_iter()
328             }
329             traits::VtableClosure(
330                 traits::VtableClosureData {
331                     closure_def_id,
332                     substs,
333                     nested: _ }) => {
334                 let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap();
335                 let llfn = closure::trans_closure_method(ccx,
336                                                          closure_def_id,
337                                                          substs,
338                                                          trait_closure_kind);
339                 vec![llfn].into_iter()
340             }
341             traits::VtableFnPointer(bare_fn_ty) => {
342                 let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap();
343                 vec![trans_fn_pointer_shim(ccx, trait_closure_kind, bare_fn_ty)].into_iter()
344             }
345             traits::VtableObject(ref data) => {
346                 // this would imply that the Self type being erased is
347                 // an object type; this cannot happen because we
348                 // cannot cast an unsized type into a trait object
349                 tcx.sess.bug(
350                     &format!("cannot get vtable for an object type: {:?}",
351                             data));
352             }
353             traits::VtableParam(..) => {
354                 tcx.sess.bug(
355                     &format!("resolved vtable for {:?} to bad vtable {:?} in trans",
356                             trait_ref,
357                             vtable));
358             }
359         }
360     });
361
362     let size_ty = sizing_type_of(ccx, trait_ref.self_ty());
363     let size = machine::llsize_of_alloc(ccx, size_ty);
364     let align = align_of(ccx, trait_ref.self_ty());
365
366     let components: Vec<_> = vec![
367         // Generate a destructor for the vtable.
368         glue::get_drop_glue(ccx, trait_ref.self_ty()),
369         C_uint(ccx, size),
370         C_uint(ccx, align)
371     ].into_iter().chain(methods).collect();
372
373     let vtable_const = C_struct(ccx, &components, false);
374     let align = machine::llalign_of_pref(ccx, val_ty(vtable_const));
375     let vtable = consts::addr_of(ccx, vtable_const, align, "vtable");
376
377     ccx.vtables().borrow_mut().insert(trait_ref, vtable);
378     vtable
379 }
380
381 pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
382                                     impl_id: DefId,
383                                     substs: &'tcx subst::Substs<'tcx>)
384                                     -> Vec<Option<ImplMethod<'tcx>>>
385 {
386     let tcx = ccx.tcx();
387
388     debug!("get_vtable_methods(impl_id={:?}, substs={:?}", impl_id, substs);
389
390     let trt_id = match tcx.impl_trait_ref(impl_id) {
391         Some(t_id) => t_id.def_id,
392         None       => ccx.sess().bug("make_impl_vtable: don't know how to \
393                                       make a vtable for a type impl!")
394     };
395
396     tcx.populate_implementations_for_trait_if_necessary(trt_id);
397
398     let trait_item_def_ids = tcx.trait_item_def_ids(trt_id);
399     trait_item_def_ids
400         .iter()
401
402         // Filter out non-method items.
403         .filter_map(|item_def_id| {
404             match *item_def_id {
405                 ty::MethodTraitItemId(def_id) => Some(def_id),
406                 _ => None,
407             }
408         })
409
410         // Now produce pointers for each remaining method. If the
411         // method could never be called from this object, just supply
412         // null.
413         .map(|trait_method_def_id| {
414             debug!("get_vtable_methods: trait_method_def_id={:?}",
415                    trait_method_def_id);
416
417             let trait_method_type = match tcx.impl_or_trait_item(trait_method_def_id) {
418                 ty::MethodTraitItem(m) => m,
419                 _ => ccx.sess().bug("should be a method, not other assoc item"),
420             };
421             let name = trait_method_type.name;
422
423             // Some methods cannot be called on an object; skip those.
424             if !traits::is_vtable_safe_method(tcx, trt_id, &trait_method_type) {
425                 debug!("get_vtable_methods: not vtable safe");
426                 return None;
427             }
428
429             debug!("get_vtable_methods: trait_method_type={:?}",
430                    trait_method_type);
431
432             // The substitutions we have are on the impl, so we grab
433             // the method type from the impl to substitute into.
434             let mth = get_impl_method(tcx, impl_id, substs.clone(), name);
435
436             debug!("get_vtable_methods: mth={:?}", mth);
437
438             // If this is a default method, it's possible that it
439             // relies on where clauses that do not hold for this
440             // particular set of type parameters. Note that this
441             // method could then never be called, so we do not want to
442             // try and trans it, in that case. Issue #23435.
443             if mth.is_provided {
444                 let predicates = mth.method.predicates.predicates.subst(tcx, mth.substs);
445                 if !normalize_and_test_predicates(ccx, predicates.into_vec()) {
446                     debug!("get_vtable_methods: predicates do not hold");
447                     return None;
448                 }
449             }
450
451             Some(mth)
452         })
453         .collect()
454 }
455
456 /// Replace the self type (&Self or Box<Self>) with an opaque pointer.
457 fn opaque_method_ty<'tcx>(tcx: &TyCtxt<'tcx>, method_ty: &ty::BareFnTy<'tcx>)
458                           -> Ty<'tcx> {
459     let mut inputs = method_ty.sig.0.inputs.clone();
460     inputs[0] = tcx.mk_mut_ptr(tcx.mk_mach_int(ast::IntTy::I8));
461
462     tcx.mk_fn_ptr(ty::BareFnTy {
463         unsafety: method_ty.unsafety,
464         abi: method_ty.abi,
465         sig: ty::Binder(ty::FnSig {
466             inputs: inputs,
467             output: method_ty.sig.0.output,
468             variadic: method_ty.sig.0.variadic,
469         }),
470     })
471 }
472
473 #[derive(Debug)]
474 pub struct ImplMethod<'tcx> {
475     pub method: Rc<ty::Method<'tcx>>,
476     pub substs: Substs<'tcx>,
477     pub is_provided: bool
478 }
479
480 /// Locates the applicable definition of a method, given its name.
481 pub fn get_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
482                              impl_def_id: DefId,
483                              substs: Substs<'tcx>,
484                              name: Name)
485                              -> ImplMethod<'tcx>
486 {
487     assert!(!substs.types.needs_infer());
488
489     let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
490     let trait_def = tcx.lookup_trait_def(trait_def_id);
491     let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables);
492
493     match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() {
494         Some(node_item) => {
495             ImplMethod {
496                 method: node_item.item,
497                 substs: traits::translate_substs(&infcx, impl_def_id, substs, node_item.node),
498                 is_provided: node_item.node.is_from_trait(),
499             }
500         }
501         None => {
502             tcx.sess.bug(&format!("method {:?} not found in {:?}", name, impl_def_id))
503         }
504     }
505 }