]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/typeck/check/writeback.rs
ec230761d350fc22a995d33a9490e9f1d547dc69
[rust.git] / src / librustc / middle / typeck / check / writeback.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 // Type resolution: the phase that finds all the types in the AST with
12 // unresolved type variables and replaces "ty_var" types with their
13 // substitutions.
14
15
16 use middle::pat_util;
17 use middle::ty;
18 use middle::typeck::astconv::AstConv;
19 use middle::typeck::check::FnCtxt;
20 use middle::typeck::infer::{force_all, resolve_all, resolve_region};
21 use middle::typeck::infer::resolve_type;
22 use middle::typeck::infer;
23 use middle::typeck::MethodCallee;
24 use middle::typeck::{vtable_res, vtable_origin};
25 use middle::typeck::{vtable_static, vtable_param};
26 use middle::typeck::write_substs_to_tcx;
27 use middle::typeck::write_ty_to_tcx;
28 use util::ppaux;
29 use util::ppaux::Repr;
30
31 use syntax::ast;
32 use syntax::codemap::Span;
33 use syntax::print::pprust::pat_to_str;
34 use syntax::visit;
35 use syntax::visit::Visitor;
36
37 fn resolve_type_vars_in_type(fcx: @FnCtxt, sp: Span, typ: ty::t)
38                           -> Option<ty::t> {
39     if !ty::type_needs_infer(typ) { return Some(typ); }
40     match resolve_type(fcx.infcx(), typ, resolve_all | force_all) {
41         Ok(new_type) => return Some(new_type),
42         Err(e) => {
43             if !fcx.ccx.tcx.sess.has_errors() {
44                 fcx.ccx.tcx.sess.span_err(
45                     sp,
46                     format!("cannot determine a type \
47                           for this expression: {}",
48                          infer::fixup_err_to_str(e)))
49             }
50             return None;
51         }
52     }
53 }
54
55 fn resolve_type_vars_in_types(fcx: @FnCtxt, sp: Span, tys: &[ty::t])
56                           -> ~[ty::t] {
57     tys.map(|t| {
58         match resolve_type_vars_in_type(fcx, sp, *t) {
59             Some(t1) => t1,
60             None => ty::mk_err()
61         }
62     })
63 }
64
65 fn resolve_method_map_entry(wbcx: &mut WbCtxt, sp: Span, id: ast::NodeId) {
66     let fcx = wbcx.fcx;
67     let tcx = fcx.ccx.tcx;
68
69     // Resolve any method map entry
70     match fcx.inh.method_map.borrow().get().find(&id) {
71         Some(method) => {
72             debug!("writeback::resolve_method_map_entry(id={:?}, entry={:?})",
73                    id, method.repr(tcx));
74             let method_ty = match resolve_type_vars_in_type(fcx, sp, method.ty) {
75                 Some(t) => t,
76                 None => {
77                     wbcx.success = false;
78                     return;
79                 }
80             };
81             let mut new_tps = ~[];
82             for &subst in method.substs.tps.iter() {
83                 match resolve_type_vars_in_type(fcx, sp, subst) {
84                     Some(t) => new_tps.push(t),
85                     None => { wbcx.success = false; return; }
86                 }
87             }
88             let new_method = MethodCallee {
89                 origin: method.origin,
90                 ty: method_ty,
91                 substs: ty::substs {
92                     tps: new_tps,
93                     regions: ty::ErasedRegions,
94                     self_ty: None
95                 }
96             };
97             fcx.ccx.method_map.borrow_mut().get().insert(id, new_method);
98         }
99         None => {}
100     }
101 }
102
103 fn resolve_vtable_map_entry(fcx: @FnCtxt, sp: Span, id: ast::NodeId) {
104     // Resolve any vtable map entry
105     match fcx.inh.vtable_map.borrow().get().find_copy(&id) {
106         Some(origins) => {
107             let r_origins = resolve_origins(fcx, sp, origins);
108             fcx.ccx.vtable_map.borrow_mut().get().insert(id, r_origins);
109             debug!("writeback::resolve_vtable_map_entry(id={}, vtables={:?})",
110                     id, r_origins.repr(fcx.tcx()));
111         }
112         None => {}
113     }
114
115     fn resolve_origins(fcx: @FnCtxt, sp: Span,
116                        vtbls: vtable_res) -> vtable_res {
117         @vtbls.map(|os| @os.map(|o| resolve_origin(fcx, sp, o)))
118     }
119
120     fn resolve_origin(fcx: @FnCtxt,
121                       sp: Span,
122                       origin: &vtable_origin) -> vtable_origin {
123         match origin {
124             &vtable_static(def_id, ref tys, origins) => {
125                 let r_tys = resolve_type_vars_in_types(fcx, sp, *tys);
126                 let r_origins = resolve_origins(fcx, sp, origins);
127                 vtable_static(def_id, r_tys, r_origins)
128             }
129             &vtable_param(n, b) => {
130                 vtable_param(n, b)
131             }
132         }
133     }
134 }
135
136 fn resolve_type_vars_for_node(wbcx: &mut WbCtxt, sp: Span, id: ast::NodeId)
137                            -> Option<ty::t> {
138     let fcx = wbcx.fcx;
139     let tcx = fcx.ccx.tcx;
140
141     // Resolve any borrowings for the node with id `id`
142     let adjustment = {
143         let adjustments = fcx.inh.adjustments.borrow();
144         adjustments.get().find_copy(&id)
145     };
146     match adjustment {
147         None => (),
148
149         Some(adjustment) => {
150             match *adjustment {
151                 ty::AutoAddEnv(r, s) => {
152                     match resolve_region(fcx.infcx(),
153                                          r,
154                                          resolve_all | force_all) {
155                         Err(e) => {
156                             // This should not, I think, happen:
157                             tcx.sess.span_err(
158                                 sp,
159                                 format!("cannot resolve bound for closure: \
160                                          {}",
161                                         infer::fixup_err_to_str(e)));
162                         }
163                         Ok(r1) => {
164                             // FIXME(eddyb) #2190 Allow only statically resolved
165                             // bare functions to coerce to a closure to avoid
166                             // constructing (slower) indirect call wrappers.
167                             {
168                                 let def_map = tcx.def_map.borrow();
169                                 match def_map.get().find(&id) {
170                                     Some(&ast::DefFn(..)) |
171                                     Some(&ast::DefStaticMethod(..)) |
172                                     Some(&ast::DefVariant(..)) |
173                                     Some(&ast::DefStruct(_)) => {}
174                                     _ => tcx.sess.span_err(sp,
175                                             "cannot coerce non-statically resolved bare fn")
176                                 }
177                             }
178
179                             let resolved_adj = @ty::AutoAddEnv(r1, s);
180                             debug!("Adjustments for node {}: {:?}",
181                                    id,
182                                    resolved_adj);
183                             let mut adjustments = tcx.adjustments
184                                                      .borrow_mut();
185                             adjustments.get().insert(id, resolved_adj);
186                         }
187                     }
188                 }
189
190                 ty::AutoDerefRef(adj) => {
191                     let fixup_region = |r| {
192                         match resolve_region(fcx.infcx(),
193                                              r,
194                                              resolve_all | force_all) {
195                             Ok(r1) => r1,
196                             Err(e) => {
197                                 // This should not, I think, happen.
198                                 tcx.sess.span_err(
199                                     sp,
200                                     format!("cannot resolve scope of borrow: \
201                                              {}",
202                                              infer::fixup_err_to_str(e)));
203                                 r
204                             }
205                         }
206                     };
207
208                     let resolved_autoref = match adj.autoref {
209                         None => None,
210                         Some(ref r) => Some(r.map_region(fixup_region))
211                     };
212
213                     let resolved_adj = @ty::AutoDerefRef(ty::AutoDerefRef {
214                         autoderefs: adj.autoderefs,
215                         autoref: resolved_autoref,
216                     });
217                     debug!("Adjustments for node {}: {:?}", id, resolved_adj);
218                     let mut adjustments = tcx.adjustments.borrow_mut();
219                     adjustments.get().insert(id, resolved_adj);
220                 }
221
222                 ty::AutoObject(..) => {
223                     debug!("Adjustments for node {}: {:?}", id, adjustment);
224                     let mut adjustments = tcx.adjustments.borrow_mut();
225                     adjustments.get().insert(id, adjustment);
226                 }
227             }
228         }
229     }
230
231     // Resolve the type of the node with id `id`
232     let n_ty = fcx.node_ty(id);
233     match resolve_type_vars_in_type(fcx, sp, n_ty) {
234       None => {
235         wbcx.success = false;
236         return None;
237       }
238
239       Some(t) => {
240         debug!("resolve_type_vars_for_node(id={}, n_ty={}, t={})",
241                id, ppaux::ty_to_str(tcx, n_ty), ppaux::ty_to_str(tcx, t));
242         write_ty_to_tcx(tcx, id, t);
243         let mut ret = Some(t);
244         fcx.opt_node_ty_substs(id, |substs| {
245           let mut new_tps = ~[];
246           for subst in substs.tps.iter() {
247               match resolve_type_vars_in_type(fcx, sp, *subst) {
248                 Some(t) => new_tps.push(t),
249                 None => { wbcx.success = false; ret = None; break }
250               }
251           }
252           write_substs_to_tcx(tcx, id, new_tps);
253           ret.is_some()
254         });
255         ret
256       }
257     }
258 }
259
260 struct WbCtxt {
261     fcx: @FnCtxt,
262
263     // As soon as we hit an error we have to stop resolving
264     // the entire function.
265     success: bool,
266 }
267
268 fn visit_stmt(s: &ast::Stmt, wbcx: &mut WbCtxt) {
269     if !wbcx.success { return; }
270     resolve_type_vars_for_node(wbcx, s.span, ty::stmt_node_id(s));
271     visit::walk_stmt(wbcx, s, ());
272 }
273
274 fn visit_expr(e: &ast::Expr, wbcx: &mut WbCtxt) {
275     if !wbcx.success {
276         return;
277     }
278
279     resolve_type_vars_for_node(wbcx, e.span, e.id);
280     resolve_method_map_entry(wbcx, e.span, e.id);
281     resolve_vtable_map_entry(wbcx.fcx, e.span, e.id);
282
283     match e.node {
284         ast::ExprFnBlock(ref decl, _) | ast::ExprProc(ref decl, _) => {
285             for input in decl.inputs.iter() {
286                 let _ = resolve_type_vars_for_node(wbcx, e.span, input.id);
287             }
288         }
289         _ => {}
290     }
291
292     visit::walk_expr(wbcx, e, ());
293 }
294
295 fn visit_block(b: &ast::Block, wbcx: &mut WbCtxt) {
296     if !wbcx.success {
297         return;
298     }
299
300     resolve_type_vars_for_node(wbcx, b.span, b.id);
301     visit::walk_block(wbcx, b, ());
302 }
303
304 fn visit_pat(p: &ast::Pat, wbcx: &mut WbCtxt) {
305     if !wbcx.success {
306         return;
307     }
308
309     resolve_type_vars_for_node(wbcx, p.span, p.id);
310     debug!("Type for pattern binding {} (id {}) resolved to {}",
311            pat_to_str(p), p.id,
312            wbcx.fcx.infcx().ty_to_str(
313                ty::node_id_to_type(wbcx.fcx.ccx.tcx,
314                                    p.id)));
315     visit::walk_pat(wbcx, p, ());
316 }
317
318 fn visit_local(l: &ast::Local, wbcx: &mut WbCtxt) {
319     if !wbcx.success { return; }
320     let var_ty = wbcx.fcx.local_ty(l.span, l.id);
321     match resolve_type(wbcx.fcx.infcx(), var_ty, resolve_all | force_all) {
322         Ok(lty) => {
323             debug!("Type for local {} (id {}) resolved to {}",
324                    pat_to_str(l.pat),
325                    l.id,
326                    wbcx.fcx.infcx().ty_to_str(lty));
327             write_ty_to_tcx(wbcx.fcx.ccx.tcx, l.id, lty);
328         }
329         Err(e) => {
330             wbcx.fcx.ccx.tcx.sess.span_err(
331                 l.span,
332                 format!("cannot determine a type \
333                       for this local variable: {}",
334                      infer::fixup_err_to_str(e)));
335             wbcx.success = false;
336         }
337     }
338     visit::walk_local(wbcx, l, ());
339 }
340 fn visit_item(_item: &ast::Item, _wbcx: &mut WbCtxt) {
341     // Ignore items
342 }
343
344 impl Visitor<()> for WbCtxt {
345     fn visit_item(&mut self, i: &ast::Item, _: ()) { visit_item(i, self); }
346     fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) { visit_stmt(s, self); }
347     fn visit_expr(&mut self, ex:&ast::Expr, _: ()) { visit_expr(ex, self); }
348     fn visit_block(&mut self, b: &ast::Block, _: ()) { visit_block(b, self); }
349     fn visit_pat(&mut self, p: &ast::Pat, _: ()) { visit_pat(p, self); }
350     fn visit_local(&mut self, l: &ast::Local, _: ()) { visit_local(l, self); }
351     // FIXME(#10894) should continue recursing
352     fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {}
353 }
354
355 fn resolve_upvar_borrow_map(wbcx: &mut WbCtxt) {
356     if !wbcx.success {
357         return;
358     }
359
360     let fcx = wbcx.fcx;
361     let tcx = fcx.tcx();
362     let upvar_borrow_map = fcx.inh.upvar_borrow_map.borrow();
363     for (upvar_id, upvar_borrow) in upvar_borrow_map.get().iter() {
364         let r = upvar_borrow.region;
365         match resolve_region(fcx.infcx(), r, resolve_all | force_all) {
366             Ok(r) => {
367                 let new_upvar_borrow = ty::UpvarBorrow {
368                     kind: upvar_borrow.kind,
369                     region: r
370                 };
371                 debug!("Upvar borrow for {} resolved to {}",
372                        upvar_id.repr(tcx), new_upvar_borrow.repr(tcx));
373                 let mut tcx_upvar_borrow_map = tcx.upvar_borrow_map.borrow_mut();
374                 tcx_upvar_borrow_map.get().insert(*upvar_id, new_upvar_borrow);
375             }
376             Err(e) => {
377                 let span = ty::expr_span(tcx, upvar_id.closure_expr_id);
378                 fcx.ccx.tcx.sess.span_err(
379                     span, format!("cannot resolve lifetime for \
380                                   captured variable `{}`: {}",
381                                   ty::local_var_name_str(tcx, upvar_id.var_id).get().to_str(),
382                                   infer::fixup_err_to_str(e)));
383                 wbcx.success = false;
384             }
385         };
386     }
387 }
388
389 pub fn resolve_type_vars_in_expr(fcx: @FnCtxt, e: &ast::Expr) -> bool {
390     let mut wbcx = WbCtxt { fcx: fcx, success: true };
391     let wbcx = &mut wbcx;
392     wbcx.visit_expr(e, ());
393     resolve_upvar_borrow_map(wbcx);
394     return wbcx.success;
395 }
396
397 pub fn resolve_type_vars_in_fn(fcx: @FnCtxt, decl: &ast::FnDecl,
398                                blk: &ast::Block) -> bool {
399     let mut wbcx = WbCtxt { fcx: fcx, success: true };
400     let wbcx = &mut wbcx;
401     wbcx.visit_block(blk, ());
402     for arg in decl.inputs.iter() {
403         wbcx.visit_pat(arg.pat, ());
404         // Privacy needs the type for the whole pattern, not just each binding
405         if !pat_util::pat_is_binding(fcx.tcx().def_map, arg.pat) {
406             resolve_type_vars_for_node(wbcx, arg.pat.span, arg.pat.id);
407         }
408     }
409     resolve_upvar_borrow_map(wbcx);
410     return wbcx.success;
411 }