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