]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/trans/meth.rs
auto merge of #13967 : richo/rust/features/ICE-fails, r=alexcrichton
[rust.git] / src / librustc / middle / 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
12 use back::abi;
13 use lib::llvm::llvm;
14 use lib::llvm::ValueRef;
15 use lib;
16 use metadata::csearch;
17 use middle::trans::base::*;
18 use middle::trans::build::*;
19 use middle::trans::callee::*;
20 use middle::trans::callee;
21 use middle::trans::cleanup;
22 use middle::trans::common::*;
23 use middle::trans::datum::*;
24 use middle::trans::expr::{SaveIn, Ignore};
25 use middle::trans::expr;
26 use middle::trans::glue;
27 use middle::trans::monomorphize;
28 use middle::trans::type_::Type;
29 use middle::trans::type_of::*;
30 use middle::ty;
31 use middle::typeck;
32 use middle::typeck::MethodCall;
33 use util::common::indenter;
34 use util::ppaux::Repr;
35
36 use std::c_str::ToCStr;
37 use syntax::abi::Rust;
38 use syntax::parse::token;
39 use syntax::{ast, ast_map, visit};
40
41 /**
42 The main "translation" pass for methods.  Generates code
43 for non-monomorphized methods only.  Other methods will
44 be generated once they are invoked with specific type parameters,
45 see `trans::base::lval_static_fn()` or `trans::base::monomorphic_fn()`.
46 */
47 pub fn trans_impl(ccx: &CrateContext,
48                   name: ast::Ident,
49                   methods: &[@ast::Method],
50                   generics: &ast::Generics,
51                   id: ast::NodeId) {
52     let _icx = push_ctxt("meth::trans_impl");
53     let tcx = ccx.tcx();
54
55     debug!("trans_impl(name={}, id={:?})", name.repr(tcx), id);
56
57     // Both here and below with generic methods, be sure to recurse and look for
58     // items that we need to translate.
59     if !generics.ty_params.is_empty() {
60         let mut v = TransItemVisitor{ ccx: ccx };
61         for method in methods.iter() {
62             visit::walk_method_helper(&mut v, *method, ());
63         }
64         return;
65     }
66     for method in methods.iter() {
67         if method.generics.ty_params.len() == 0u {
68             let llfn = get_item_val(ccx, method.id);
69             trans_fn(ccx, method.decl, method.body,
70                      llfn, None, method.id, []);
71         } else {
72             let mut v = TransItemVisitor{ ccx: ccx };
73             visit::walk_method_helper(&mut v, *method, ());
74         }
75     }
76 }
77
78 pub fn trans_method_callee<'a>(
79                            bcx: &'a Block<'a>,
80                            method_call: MethodCall,
81                            self_expr: Option<&ast::Expr>,
82                            arg_cleanup_scope: cleanup::ScopeId)
83                            -> Callee<'a> {
84     let _icx = push_ctxt("meth::trans_method_callee");
85
86     let (origin, method_ty) = match bcx.tcx().method_map
87                                        .borrow().find(&method_call) {
88         Some(method) => {
89             debug!("trans_method_callee({:?}, method={})",
90                    method_call, method.repr(bcx.tcx()));
91             (method.origin, method.ty)
92         }
93         None => {
94             bcx.sess().span_bug(bcx.tcx().map.span(method_call.expr_id),
95                                 "method call expr wasn't in method map")
96         }
97     };
98
99     match origin {
100         typeck::MethodStatic(did) => {
101             Callee {
102                 bcx: bcx,
103                 data: Fn(callee::trans_fn_ref(bcx, did, MethodCall(method_call)))
104             }
105         }
106         typeck::MethodParam(typeck::MethodParam {
107             trait_id: trait_id,
108             method_num: off,
109             param_num: p,
110             bound_num: b
111         }) => {
112             match bcx.fcx.param_substs {
113                 Some(substs) => {
114                     ty::populate_implementations_for_trait_if_necessary(
115                         bcx.tcx(),
116                         trait_id);
117
118                     let vtbl = find_vtable(bcx.tcx(), substs, p, b);
119                     trans_monomorphized_callee(bcx, method_call,
120                                                trait_id, off, vtbl)
121                 }
122                 // how to get rid of this?
123                 None => fail!("trans_method_callee: missing param_substs")
124             }
125         }
126
127         typeck::MethodObject(ref mt) => {
128             let self_expr = match self_expr {
129                 Some(self_expr) => self_expr,
130                 None => {
131                     bcx.sess().span_bug(bcx.tcx().map.span(method_call.expr_id),
132                                         "self expr wasn't provided for trait object \
133                                          callee (trying to call overloaded op?)")
134                 }
135             };
136             trans_trait_callee(bcx,
137                                monomorphize_type(bcx, method_ty),
138                                mt.real_index,
139                                self_expr,
140                                arg_cleanup_scope)
141         }
142     }
143 }
144
145 pub fn trans_static_method_callee(bcx: &Block,
146                                   method_id: ast::DefId,
147                                   trait_id: ast::DefId,
148                                   expr_id: ast::NodeId)
149                                   -> ValueRef {
150     let _icx = push_ctxt("meth::trans_static_method_callee");
151     let ccx = bcx.ccx();
152
153     debug!("trans_static_method_callee(method_id={:?}, trait_id={}, \
154             expr_id={:?})",
155            method_id,
156            ty::item_path_str(bcx.tcx(), trait_id),
157            expr_id);
158     let _indenter = indenter();
159
160     ty::populate_implementations_for_trait_if_necessary(bcx.tcx(), trait_id);
161
162     // When we translate a static fn defined in a trait like:
163     //
164     //   trait<T1...Tn> Trait {
165     //       fn foo<M1...Mn>(...) {...}
166     //   }
167     //
168     // this winds up being translated as something like:
169     //
170     //   fn foo<T1...Tn,self: Trait<T1...Tn>,M1...Mn>(...) {...}
171     //
172     // So when we see a call to this function foo, we have to figure
173     // out which impl the `Trait<T1...Tn>` bound on the type `self` was
174     // bound to.
175     let bound_index = ty::lookup_trait_def(bcx.tcx(), trait_id).
176         generics.type_param_defs().len();
177
178     let mname = if method_id.krate == ast::LOCAL_CRATE {
179         match bcx.tcx().map.get(method_id.node) {
180             ast_map::NodeTraitMethod(method) => {
181                 let ident = match *method {
182                     ast::Required(ref m) => m.ident,
183                     ast::Provided(ref m) => m.ident
184                 };
185                 ident.name
186             }
187             _ => fail!("callee is not a trait method")
188         }
189     } else {
190         csearch::get_item_path(bcx.tcx(), method_id).last().unwrap().name()
191     };
192     debug!("trans_static_method_callee: method_id={:?}, expr_id={:?}, \
193             name={}", method_id, expr_id, token::get_name(mname));
194
195     let vtable_key = MethodCall::expr(expr_id);
196     let vtbls = resolve_vtables_in_fn_ctxt(bcx.fcx, ccx.tcx.vtable_map.borrow()
197                                                        .get(&vtable_key).as_slice());
198
199     match vtbls.move_iter().nth(bound_index).unwrap().move_iter().nth(0).unwrap() {
200         typeck::vtable_static(impl_did, rcvr_substs, rcvr_origins) => {
201             assert!(rcvr_substs.iter().all(|t| !ty::type_needs_infer(*t)));
202
203             let mth_id = method_with_name(ccx, impl_did, mname);
204             let (callee_substs, callee_origins) =
205                 combine_impl_and_methods_tps(
206                     bcx, mth_id, ExprId(expr_id),
207                     rcvr_substs, rcvr_origins);
208
209             let llfn = trans_fn_ref_with_vtables(bcx, mth_id, ExprId(expr_id),
210                                                  callee_substs,
211                                                  Some(callee_origins));
212
213             let callee_ty = node_id_type(bcx, expr_id);
214             let llty = type_of_fn_from_ty(ccx, callee_ty).ptr_to();
215             PointerCast(bcx, llfn, llty)
216         }
217         _ => {
218             fail!("vtable_param left in monomorphized \
219                    function's vtable substs");
220         }
221     }
222 }
223
224 fn method_with_name(ccx: &CrateContext,
225                     impl_id: ast::DefId,
226                     name: ast::Name) -> ast::DefId {
227     match ccx.impl_method_cache.borrow().find_copy(&(impl_id, name)) {
228         Some(m) => return m,
229         None => {}
230     }
231
232     let methods = ccx.tcx.impl_methods.borrow();
233     let methods = methods.find(&impl_id)
234                          .expect("could not find impl while translating");
235     let meth_did = methods.iter().find(|&did| ty::method(&ccx.tcx, *did).ident.name == name)
236                                  .expect("could not find method while translating");
237
238     ccx.impl_method_cache.borrow_mut().insert((impl_id, name), *meth_did);
239     *meth_did
240 }
241
242 fn trans_monomorphized_callee<'a>(bcx: &'a Block<'a>,
243                                   method_call: MethodCall,
244                                   trait_id: ast::DefId,
245                                   n_method: uint,
246                                   vtbl: typeck::vtable_origin)
247                                   -> Callee<'a> {
248     let _icx = push_ctxt("meth::trans_monomorphized_callee");
249     match vtbl {
250       typeck::vtable_static(impl_did, rcvr_substs, rcvr_origins) => {
251           let ccx = bcx.ccx();
252           let mname = ty::trait_method(ccx.tcx(), trait_id, n_method).ident;
253           let mth_id = method_with_name(bcx.ccx(), impl_did, mname.name);
254
255           // create a concatenated set of substitutions which includes
256           // those from the impl and those from the method:
257           let (callee_substs, callee_origins) =
258               combine_impl_and_methods_tps(
259                   bcx, mth_id,  MethodCall(method_call),
260                   rcvr_substs, rcvr_origins);
261
262           // translate the function
263           let llfn = trans_fn_ref_with_vtables(bcx,
264                                                mth_id,
265                                                MethodCall(method_call),
266                                                callee_substs,
267                                                Some(callee_origins));
268
269           Callee { bcx: bcx, data: Fn(llfn) }
270       }
271       typeck::vtable_param(..) => {
272           fail!("vtable_param left in monomorphized function's vtable substs");
273       }
274     }
275 }
276
277 fn combine_impl_and_methods_tps(bcx: &Block,
278                                 mth_did: ast::DefId,
279                                 node: ExprOrMethodCall,
280                                 rcvr_substs: Vec<ty::t>,
281                                 rcvr_origins: typeck::vtable_res)
282                                 -> (Vec<ty::t>, typeck::vtable_res) {
283     /*!
284     *
285     * Creates a concatenated set of substitutions which includes
286     * those from the impl and those from the method.  This are
287     * some subtle complications here.  Statically, we have a list
288     * of type parameters like `[T0, T1, T2, M1, M2, M3]` where
289     * `Tn` are type parameters that appear on the receiver.  For
290     * example, if the receiver is a method parameter `A` with a
291     * bound like `trait<B,C,D>` then `Tn` would be `[B,C,D]`.
292     *
293     * The weird part is that the type `A` might now be bound to
294     * any other type, such as `foo<X>`.  In that case, the vector
295     * we want is: `[X, M1, M2, M3]`.  Therefore, what we do now is
296     * to slice off the method type parameters and append them to
297     * the type parameters from the type that the receiver is
298     * mapped to. */
299
300     let ccx = bcx.ccx();
301     let method = ty::method(ccx.tcx(), mth_did);
302     let n_m_tps = method.generics.type_param_defs().len();
303     let node_substs = node_id_type_params(bcx, node);
304     debug!("rcvr_substs={:?}", rcvr_substs.repr(ccx.tcx()));
305     debug!("node_substs={:?}", node_substs.repr(ccx.tcx()));
306     let mut ty_substs = rcvr_substs;
307     {
308         let start = node_substs.len() - n_m_tps;
309         ty_substs.extend(node_substs.move_iter().skip(start));
310     }
311     debug!("n_m_tps={:?}", n_m_tps);
312     debug!("ty_substs={:?}", ty_substs.repr(ccx.tcx()));
313
314
315     // Now, do the same work for the vtables.  The vtables might not
316     // exist, in which case we need to make them.
317     let vtable_key = match node {
318         ExprId(id) => MethodCall::expr(id),
319         MethodCall(method_call) => method_call
320     };
321     let mut vtables = rcvr_origins;
322     match node_vtables(bcx, vtable_key) {
323         Some(vt) => {
324             let start = vt.len() - n_m_tps;
325             vtables.extend(vt.move_iter().skip(start));
326         }
327         None => {
328             vtables.extend(range(0, n_m_tps).map(
329                 |_| -> typeck::vtable_param_res {
330                     Vec::new()
331                 }
332             ));
333         }
334     }
335
336     (ty_substs, vtables)
337 }
338
339 fn trans_trait_callee<'a>(bcx: &'a Block<'a>,
340                           method_ty: ty::t,
341                           n_method: uint,
342                           self_expr: &ast::Expr,
343                           arg_cleanup_scope: cleanup::ScopeId)
344                           -> Callee<'a> {
345     /*!
346      * Create a method callee where the method is coming from a trait
347      * object (e.g., Box<Trait> type).  In this case, we must pull the fn
348      * pointer out of the vtable that is packaged up with the object.
349      * Objects are represented as a pair, so we first evaluate the self
350      * expression and then extract the self data and vtable out of the
351      * pair.
352      */
353
354     let _icx = push_ctxt("meth::trans_trait_callee");
355     let mut bcx = bcx;
356
357     // Translate self_datum and take ownership of the value by
358     // converting to an rvalue.
359     let self_datum = unpack_datum!(
360         bcx, expr::trans(bcx, self_expr));
361
362     let llval = if ty::type_needs_drop(bcx.tcx(), self_datum.ty) {
363         let self_datum = unpack_datum!(
364             bcx, self_datum.to_rvalue_datum(bcx, "trait_callee"));
365
366         // Convert to by-ref since `trans_trait_callee_from_llval` wants it
367         // that way.
368         let self_datum = unpack_datum!(
369             bcx, self_datum.to_ref_datum(bcx));
370
371         // Arrange cleanup in case something should go wrong before the
372         // actual call occurs.
373         self_datum.add_clean(bcx.fcx, arg_cleanup_scope)
374     } else {
375         // We don't have to do anything about cleanups for &Trait and &mut Trait.
376         assert!(self_datum.kind.is_by_ref());
377         self_datum.val
378     };
379
380     trans_trait_callee_from_llval(bcx, method_ty, n_method, llval)
381 }
382
383 pub fn trans_trait_callee_from_llval<'a>(bcx: &'a Block<'a>,
384                                          callee_ty: ty::t,
385                                          n_method: uint,
386                                          llpair: ValueRef)
387                                          -> Callee<'a> {
388     /*!
389      * Same as `trans_trait_callee()` above, except that it is given
390      * a by-ref pointer to the object pair.
391      */
392
393     let _icx = push_ctxt("meth::trans_trait_callee");
394     let ccx = bcx.ccx();
395
396     // Load the data pointer from the object.
397     debug!("(translating trait callee) loading second index from pair");
398     let llboxptr = GEPi(bcx, llpair, [0u, abi::trt_field_box]);
399     let llbox = Load(bcx, llboxptr);
400     let llself = PointerCast(bcx, llbox, Type::i8p(ccx));
401
402     // Load the function from the vtable and cast it to the expected type.
403     debug!("(translating trait callee) loading method");
404     // Replace the self type (&Self or Box<Self>) with an opaque pointer.
405     let llcallee_ty = match ty::get(callee_ty).sty {
406         ty::ty_bare_fn(ref f) if f.abi == Rust => {
407             type_of_rust_fn(ccx, true, f.sig.inputs.slice_from(1), f.sig.output)
408         }
409         _ => {
410             ccx.sess().bug("meth::trans_trait_callee given non-bare-rust-fn");
411         }
412     };
413     let llvtable = Load(bcx,
414                         PointerCast(bcx,
415                                     GEPi(bcx, llpair,
416                                          [0u, abi::trt_field_vtable]),
417                                     Type::vtable(ccx).ptr_to().ptr_to()));
418     let mptr = Load(bcx, GEPi(bcx, llvtable, [0u, n_method + 1]));
419     let mptr = PointerCast(bcx, mptr, llcallee_ty.ptr_to());
420
421     return Callee {
422         bcx: bcx,
423         data: TraitMethod(MethodData {
424             llfn: mptr,
425             llself: llself,
426         })
427     };
428 }
429
430 /// Creates a returns a dynamic vtable for the given type and vtable origin.
431 /// This is used only for objects.
432 fn get_vtable(bcx: &Block,
433               self_ty: ty::t,
434               origins: typeck::vtable_param_res)
435               -> ValueRef {
436     let ccx = bcx.ccx();
437     let _icx = push_ctxt("meth::get_vtable");
438
439     // Check the cache.
440     let hash_id = (self_ty, monomorphize::make_vtable_id(ccx, origins.get(0)));
441     match ccx.vtables.borrow().find(&hash_id) {
442         Some(&val) => { return val }
443         None => { }
444     }
445
446     // Not in the cache. Actually build it.
447     let methods = origins.move_iter().flat_map(|origin| {
448         match origin {
449             typeck::vtable_static(id, substs, sub_vtables) => {
450                 emit_vtable_methods(bcx, id, substs, sub_vtables).move_iter()
451             }
452             _ => ccx.sess().bug("get_vtable: expected a static origin"),
453         }
454     });
455
456     // Generate a destructor for the vtable.
457     let drop_glue = glue::get_drop_glue(ccx, self_ty);
458     let vtable = make_vtable(ccx, drop_glue, methods);
459
460     ccx.vtables.borrow_mut().insert(hash_id, vtable);
461     vtable
462 }
463
464 /// Helper function to declare and initialize the vtable.
465 pub fn make_vtable<I: Iterator<ValueRef>>(ccx: &CrateContext,
466                                           drop_glue: ValueRef,
467                                           ptrs: I)
468                                           -> ValueRef {
469     let _icx = push_ctxt("meth::make_vtable");
470
471     let components: Vec<_> = Some(drop_glue).move_iter().chain(ptrs).collect();
472
473     unsafe {
474         let tbl = C_struct(ccx, components.as_slice(), false);
475         let sym = token::gensym("vtable");
476         let vt_gvar = format!("vtable{}", sym).with_c_str(|buf| {
477             llvm::LLVMAddGlobal(ccx.llmod, val_ty(tbl).to_ref(), buf)
478         });
479         llvm::LLVMSetInitializer(vt_gvar, tbl);
480         llvm::LLVMSetGlobalConstant(vt_gvar, lib::llvm::True);
481         lib::llvm::SetLinkage(vt_gvar, lib::llvm::InternalLinkage);
482         vt_gvar
483     }
484 }
485
486 fn emit_vtable_methods(bcx: &Block,
487                        impl_id: ast::DefId,
488                        substs: Vec<ty::t>,
489                        vtables: typeck::vtable_res)
490                        -> Vec<ValueRef> {
491     let ccx = bcx.ccx();
492     let tcx = ccx.tcx();
493
494     let trt_id = match ty::impl_trait_ref(tcx, impl_id) {
495         Some(t_id) => t_id.def_id,
496         None       => ccx.sess().bug("make_impl_vtable: don't know how to \
497                                       make a vtable for a type impl!")
498     };
499
500     ty::populate_implementations_for_trait_if_necessary(bcx.tcx(), trt_id);
501
502     let trait_method_def_ids = ty::trait_method_def_ids(tcx, trt_id);
503     trait_method_def_ids.iter().map(|method_def_id| {
504         let ident = ty::method(tcx, *method_def_id).ident;
505         // The substitutions we have are on the impl, so we grab
506         // the method type from the impl to substitute into.
507         let m_id = method_with_name(ccx, impl_id, ident.name);
508         let m = ty::method(tcx, m_id);
509         debug!("(making impl vtable) emitting method {} at subst {}",
510                m.repr(tcx),
511                substs.repr(tcx));
512         if m.generics.has_type_params() ||
513            ty::type_has_self(ty::mk_bare_fn(tcx, m.fty.clone())) {
514             debug!("(making impl vtable) method has self or type params: {}",
515                    token::get_ident(ident));
516             C_null(Type::nil(ccx).ptr_to())
517         } else {
518             trans_fn_ref_with_vtables(bcx, m_id, ExprId(0),
519                                       substs.clone(), Some(vtables.clone()))
520         }
521     }).collect()
522 }
523
524 pub fn trans_trait_cast<'a>(bcx: &'a Block<'a>,
525                             datum: Datum<Expr>,
526                             id: ast::NodeId,
527                             dest: expr::Dest)
528                             -> &'a Block<'a> {
529     /*!
530      * Generates the code to convert from a pointer (`Box<T>`, `&T`, etc)
531      * into an object (`Box<Trait>`, `&Trait`, etc). This means creating a
532      * pair where the first word is the vtable and the second word is
533      * the pointer.
534      */
535
536     let mut bcx = bcx;
537     let _icx = push_ctxt("meth::trans_cast");
538
539     let lldest = match dest {
540         Ignore => {
541             return datum.clean(bcx, "trait_cast", id);
542         }
543         SaveIn(dest) => dest
544     };
545
546     let ccx = bcx.ccx();
547     let v_ty = datum.ty;
548     let llbox_ty = type_of(bcx.ccx(), datum.ty);
549
550     // Store the pointer into the first half of pair.
551     let mut llboxdest = GEPi(bcx, lldest, [0u, abi::trt_field_box]);
552     llboxdest = PointerCast(bcx, llboxdest, llbox_ty.ptr_to());
553     bcx = datum.store_to(bcx, llboxdest);
554
555     // Store the vtable into the second half of pair.
556     let origins = {
557         let vtable_map = ccx.tcx.vtable_map.borrow();
558         resolve_param_vtables_under_param_substs(ccx.tcx(),
559             bcx.fcx.param_substs,
560             vtable_map.get(&MethodCall::expr(id)).get(0).as_slice())
561     };
562     let vtable = get_vtable(bcx, v_ty, origins);
563     let llvtabledest = GEPi(bcx, lldest, [0u, abi::trt_field_vtable]);
564     let llvtabledest = PointerCast(bcx, llvtabledest, val_ty(vtable).ptr_to());
565     Store(bcx, vtable, llvtabledest);
566
567     bcx
568 }