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