]> git.lizzy.rs Git - rust.git/blob - src/comp/middle/trans_impl.rs
First barely-working version of casting to iface
[rust.git] / src / comp / middle / trans_impl.rs
1 import trans::*;
2 import trans_common::*;
3 import trans_build::*;
4 import option::{some, none};
5 import syntax::{ast, ast_util};
6 import metadata::csearch;
7 import back::{link, abi};
8 import lib::llvm::llvm;
9 import llvm::{ValueRef, TypeRef, LLVMGetParam};
10
11 // Translation functionality related to impls and ifaces
12 //
13 // Terminology:
14 //  vtable:  a table of function pointers pointing to method wrappers
15 //           of an impl that implements an iface
16 //  dict:    a record containing a vtable pointer along with pointers to
17 //           all tydescs and other dicts needed to run methods in this vtable
18 //           (i.e. corresponding to the type parameters of the impl)
19 //  wrapper: a function that takes a dict as first argument, along
20 //           with the method-specific tydescs for a method (and all
21 //           other args the method expects), which fetches the extra
22 //           tydescs and dicts from the dict, splices them into the
23 //           arglist, and calls through to the actual method
24 //
25 // Generic functions take, along with their normal arguments, a number
26 // of extra tydesc and dict arguments -- one tydesc for each type
27 // parameter, one dict (following the tydesc in the arg order) for
28 // each interface bound on a type parameter.
29 //
30 // Most dicts are completely static, and are allocated and filled at
31 // compile time. Dicts that depend on run-time values (tydescs or
32 // dicts for type parameter types) are built at run-time, and interned
33 // through upcall_intern_dict in the runtime. This means that dict
34 // pointers are self-contained things that do not need to be cleaned
35 // up.
36 //
37 // The trans_constants pass in trans.rs outputs the vtables. Typeck
38 // annotates notes with information about the methods and dicts that
39 // are referenced (ccx.method_map and ccx.dict_map).
40
41 fn trans_impl(cx: @local_ctxt, name: ast::ident, methods: [@ast::method],
42               id: ast::node_id, tps: [ast::ty_param]) {
43     let sub_cx = extend_path(cx, name);
44     for m in methods {
45         alt cx.ccx.item_ids.find(m.id) {
46           some(llfn) {
47             trans_fn(extend_path(sub_cx, m.ident), m.span, m.decl, m.body,
48                      llfn, impl_self(ty::node_id_to_monotype(cx.ccx.tcx, id)),
49                      tps + m.tps, m.id);
50           }
51         }
52     }
53 }
54
55 fn trans_self_arg(bcx: @block_ctxt, base: @ast::expr) -> result {
56     let tz = [], tr = [];
57     let basety = ty::expr_ty(bcx_tcx(bcx), base);
58     let {bcx, val} = trans_arg_expr(bcx, {mode: ast::by_ref, ty: basety},
59                                     T_ptr(type_of_or_i8(bcx, basety)), tz,
60                                     tr, base);
61     rslt(bcx, PointerCast(bcx, val, T_opaque_cbox_ptr(bcx_ccx(bcx))))
62 }
63
64 // Method callee where the method is statically known
65 fn trans_static_callee(bcx: @block_ctxt, e: @ast::expr, base: @ast::expr,
66                        did: ast::def_id) -> lval_maybe_callee {
67     let {bcx, val} = trans_self_arg(bcx, base);
68     {env: obj_env(val) with lval_static_fn(bcx, did, e.id)}
69 }
70
71 fn trans_vtable_callee(bcx: @block_ctxt, self: ValueRef, dict: ValueRef,
72                        fld_expr: @ast::expr, iface_id: ast::def_id,
73                        n_method: uint) -> lval_maybe_callee {
74     let bcx = bcx, ccx = bcx_ccx(bcx), tcx = ccx.tcx;
75     let method = ty::iface_methods(tcx, iface_id)[n_method];
76     let fty = ty::expr_ty(tcx, fld_expr);
77     let bare_fn_ty = type_of_fn_from_ty(ccx, ast_util::dummy_sp(),
78                                         fty, *method.tps);
79     let {inputs: bare_inputs, output} = llfn_arg_tys(bare_fn_ty);
80     let fn_ty = T_fn([val_ty(dict)] + bare_inputs, output);
81     let vtable = PointerCast(bcx, Load(bcx, GEPi(bcx, dict, [0, 0])),
82                              T_ptr(T_array(T_ptr(fn_ty), n_method + 1u)));
83     let mptr = Load(bcx, GEPi(bcx, vtable, [0, n_method as int]));
84     let generic = none;
85     if vec::len(*method.tps) > 0u {
86         let tydescs = [], tis = [];
87         let tptys = ty::node_id_to_type_params(tcx, fld_expr.id);
88         for t in vec::tail_n(tptys, vec::len(tptys) - vec::len(*method.tps)) {
89             let ti = none;
90             let td = get_tydesc(bcx, t, true, tps_normal, ti).result;
91             tis += [ti];
92             tydescs += [td.val];
93             bcx = td.bcx;
94         }
95         generic = some({item_type: fty,
96                         static_tis: tis,
97                         tydescs: tydescs,
98                         param_bounds: method.tps,
99                         origins: bcx_ccx(bcx).dict_map.find(fld_expr.id)});
100     }
101     {bcx: bcx, val: mptr, kind: owned,
102      env: dict_env(dict, self),
103      generic: generic}
104 }
105
106 // Method callee where the dict comes from a type param
107 fn trans_param_callee(bcx: @block_ctxt, fld_expr: @ast::expr,
108                       base: @ast::expr, iface_id: ast::def_id, n_method: uint,
109                       n_param: uint, n_bound: uint) -> lval_maybe_callee {
110     let {bcx, val} = trans_self_arg(bcx, base);
111     let dict = option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound];
112     trans_vtable_callee(bcx, val, dict, fld_expr, iface_id, n_method)
113 }
114
115 // Method callee where the dict comes from a boxed iface
116 fn trans_iface_callee(bcx: @block_ctxt, fld_expr: @ast::expr, base: @ast::expr,
117                       n_method: uint)
118     -> lval_maybe_callee {
119     let tcx = bcx_tcx(bcx);
120     let {bcx, val} = trans_temp_expr(bcx, base);
121     let box_body = GEPi(bcx, val, [0, abi::box_rc_field_body]);
122     let dict = Load(bcx, PointerCast(bcx, GEPi(bcx, box_body, [0, 1]),
123                                      T_ptr(T_ptr(T_dict()))));
124     // FIXME[impl] I doubt this is alignment-safe
125     let self = PointerCast(bcx, GEPi(bcx, box_body, [0, 2]),
126                            T_opaque_cbox_ptr(bcx_ccx(bcx)));
127     let iface_id = alt ty::struct(tcx, ty::expr_ty(tcx, base)) {
128         ty::ty_iface(did, _) { did }
129     };
130     trans_vtable_callee(bcx, self, dict, fld_expr, iface_id, n_method)
131 }
132
133 fn llfn_arg_tys(ft: TypeRef) -> {inputs: [TypeRef], output: TypeRef} {
134     let out_ty = llvm::LLVMGetReturnType(ft);
135     let n_args = llvm::LLVMCountParamTypes(ft);
136     let args = vec::init_elt(0 as TypeRef, n_args);
137     unsafe { llvm::LLVMGetParamTypes(ft, vec::to_ptr(args)); }
138     {inputs: args, output: out_ty}
139 }
140
141 fn trans_wrapper(ccx: @crate_ctxt, pt: [ast::ident],
142                  extra_tps: [ty::param_bounds], m: @ast::method) -> ValueRef {
143     let real_fn = ccx.item_ids.get(m.id);
144     let {inputs: real_args, output: real_ret} =
145         llfn_arg_tys(llvm::LLVMGetElementType(val_ty(real_fn)));
146     let extra_ptrs = [];
147     for tp in extra_tps {
148         extra_ptrs += [T_ptr(ccx.tydesc_type)];
149         for bound in *tp {
150             alt bound {
151               ty::bound_iface(_) { extra_ptrs += [T_ptr(T_dict())]; }
152               _ {}
153             }
154         }
155     }
156     let env_ty = T_ptr(T_struct([T_ptr(T_i8())] + extra_ptrs));
157     let n_extra_ptrs = vec::len(extra_ptrs);
158
159     let wrap_args = [T_ptr(T_dict())] + vec::slice(real_args, 0u, 2u) +
160         vec::slice(real_args, 2u + vec::len(extra_ptrs), vec::len(real_args));
161     let llfn_ty = T_fn(wrap_args, real_ret);
162
163     let lcx = @{path: pt + ["wrapper", m.ident], module_path: [],
164                 obj_typarams: [], obj_fields: [], ccx: ccx};
165     let name = link::mangle_internal_name_by_path_and_seq(ccx, pt, m.ident);
166     let llfn = decl_internal_cdecl_fn(ccx.llmod, name, llfn_ty);
167     let fcx = new_fn_ctxt(lcx, ast_util::dummy_sp(), llfn);
168     let bcx = new_top_block_ctxt(fcx), lltop = bcx.llbb;
169
170     let dict = PointerCast(bcx, LLVMGetParam(llfn, 0u), env_ty);
171     // retptr, self
172     let args = [LLVMGetParam(llfn, 1u), LLVMGetParam(llfn, 2u)], i = 0u;
173     // saved tydescs/dicts
174     while i < n_extra_ptrs {
175         i += 1u;
176         args += [load_inbounds(bcx, dict, [0, i as int])];
177     }
178     // the rest of the parameters
179     let i = 3u, params_total = llvm::LLVMCountParamTypes(llfn_ty);
180     while i < params_total {
181         args += [LLVMGetParam(llfn, i)];
182         i += 1u;
183     }
184     Call(bcx, ccx.item_ids.get(m.id), args);
185     build_return(bcx);
186     finish_fn(fcx, lltop);
187     ret llfn;
188 }
189
190 fn dict_is_static(tcx: ty::ctxt, origin: typeck::dict_origin) -> bool {
191     alt origin {
192       typeck::dict_static(_, ts, origs) {
193         vec::all(ts, {|t| !ty::type_contains_params(tcx, t)}) &&
194         vec::all(*origs, {|o| dict_is_static(tcx, o)})
195       }
196       typeck::dict_param(_, _) { false }
197     }
198 }
199
200 fn get_dict(bcx: @block_ctxt, origin: typeck::dict_origin) -> result {
201     let ccx = bcx_ccx(bcx);
202     alt origin {
203       typeck::dict_static(impl_did, tys, sub_origins) {
204         if dict_is_static(ccx.tcx, origin) {
205             ret rslt(bcx, get_static_dict(bcx, origin));
206         }
207         let {bcx, ptrs} = get_dict_ptrs(bcx, origin);
208         let pty = T_ptr(T_i8()), dict_ty = T_array(pty, vec::len(ptrs));
209         let dict = alloca(bcx, dict_ty), i = 0;
210         for ptr in ptrs {
211             Store(bcx, PointerCast(bcx, ptr, pty), GEPi(bcx, dict, [0, i]));
212             i += 1;
213         }
214         dict = Call(bcx, ccx.upcalls.intern_dict,
215                     [C_uint(ccx, vec::len(ptrs)),
216                      PointerCast(bcx, dict, T_ptr(T_dict()))]);
217         rslt(bcx, dict)
218       }
219       typeck::dict_param(n_param, n_bound) {
220         rslt(bcx, option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound])
221       }
222     }
223 }
224
225 fn dict_id(tcx: ty::ctxt, origin: typeck::dict_origin) -> dict_id {
226     alt origin {
227       typeck::dict_static(did, ts, origs) {
228         let d_params = [], orig = 0u;
229         if vec::len(ts) == 0u { ret @{impl_def: did, params: d_params}; }
230         let impl_params = ty::lookup_item_type(tcx, did).bounds;
231         vec::iter2(ts, *impl_params) {|t, bounds|
232             d_params += [dict_param_ty(t)];
233             for bound in *bounds {
234                 alt bound {
235                   ty::bound_iface(_) {
236                     d_params += [dict_param_dict(dict_id(tcx, origs[orig]))];
237                     orig += 1u;
238                   }
239                 }
240             }
241         }
242         @{impl_def: did, params: d_params}
243       }
244     }
245 }
246
247 fn get_static_dict(bcx: @block_ctxt, origin: typeck::dict_origin)
248     -> ValueRef {
249     let ccx = bcx_ccx(bcx);
250     let id = dict_id(ccx.tcx, origin);
251     alt ccx.dicts.find(id) {
252       some(d) { ret d; }
253       none. {}
254     }
255     let ptrs = C_struct(get_dict_ptrs(bcx, origin).ptrs);
256     let name = ccx.names.next("dict");
257     let gvar = str::as_buf(name, {|buf|
258         llvm::LLVMAddGlobal(ccx.llmod, val_ty(ptrs), buf)
259     });
260     llvm::LLVMSetGlobalConstant(gvar, lib::llvm::True);
261     llvm::LLVMSetInitializer(gvar, ptrs);
262     llvm::LLVMSetLinkage(gvar,
263                          lib::llvm::LLVMInternalLinkage as llvm::Linkage);
264     let cast = llvm::LLVMConstPointerCast(gvar, T_ptr(T_dict()));
265     ccx.dicts.insert(id, cast);
266     cast
267 }
268
269 fn get_dict_ptrs(bcx: @block_ctxt, origin: typeck::dict_origin)
270     -> {bcx: @block_ctxt, ptrs: [ValueRef]} {
271     let ccx = bcx_ccx(bcx);
272     alt origin {
273       typeck::dict_static(impl_did, tys, sub_origins) {
274         let vtable = if impl_did.crate == ast::local_crate {
275             ccx.item_ids.get(impl_did.node)
276         } else {
277             let name = csearch::get_symbol(ccx.sess.get_cstore(), impl_did);
278             get_extern_const(ccx.externs, ccx.llmod, name, T_ptr(T_i8()))
279         };
280         let impl_params = ty::lookup_item_type(ccx.tcx, impl_did).bounds;
281         let ptrs = [vtable], origin = 0u, ti = none, bcx = bcx;
282         vec::iter2(*impl_params, tys) {|param, ty|
283             let rslt = get_tydesc(bcx, ty, true, tps_normal, ti).result;
284             ptrs += [rslt.val];
285             bcx = rslt.bcx;
286             for bound in *param {
287                 alt bound {
288                   ty::bound_iface(_) {
289                     let res = get_dict(bcx, sub_origins[origin]);
290                     ptrs += [res.val];
291                     bcx = res.bcx;
292                     origin += 1u;
293                   }
294                   _ {}
295                 }
296             }
297         }
298         {bcx: bcx, ptrs: ptrs}
299       }
300     }
301 }
302
303 fn trans_cast(bcx: @block_ctxt, val: @ast::expr, id: ast::node_id, dest: dest)
304     -> @block_ctxt {
305     let ccx = bcx_ccx(bcx), tcx = ccx.tcx;
306     let val_ty = ty::expr_ty(tcx, val);
307     let {bcx, val: dict} = get_dict(bcx, ccx.dict_map.get(id)[0]);
308     let body_ty = ty::mk_tup(tcx, [ty::mk_type(tcx), ty::mk_type(tcx),
309                                    val_ty]);
310     let ti = none;
311     let {bcx, val: tydesc} = get_tydesc(bcx, body_ty, true,
312                                         tps_normal, ti).result;
313     lazily_emit_all_tydesc_glue(bcx, ti);
314     let {bcx, box, body: box_body} = trans_malloc_boxed(bcx, body_ty);
315     Store(bcx, tydesc, GEPi(bcx, box_body, [0, 0]));
316     Store(bcx, PointerCast(bcx, dict, T_ptr(ccx.tydesc_type)),
317           GEPi(bcx, box_body, [0, 1]));
318     bcx = trans_expr_save_in(bcx, val, GEPi(bcx, box_body, [0, 2]));
319     store_in_dest(bcx, PointerCast(bcx, box, T_opaque_iface_ptr(ccx)), dest)
320 }