]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/writeback.rs
move everything into the Rust tree
[rust.git] / src / librustc_typeck / check / writeback.rs
1 // Copyright 2014 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 use self::ResolveReason::*;
15
16 use astconv::AstConv;
17 use check::FnCtxt;
18 use middle::pat_util;
19 use middle::ty::{self, Ty, MethodCall, MethodCallee};
20 use middle::ty_fold::{TypeFolder,TypeFoldable};
21 use middle::infer;
22 use write_substs_to_tcx;
23 use write_ty_to_tcx;
24
25 use std::cell::Cell;
26
27 use syntax::ast;
28 use syntax::ast_util;
29 use syntax::codemap::{DUMMY_SP, Span};
30 use syntax::print::pprust::pat_to_string;
31 use syntax::visit;
32 use syntax::visit::Visitor;
33
34 ///////////////////////////////////////////////////////////////////////////
35 // Entry point functions
36
37 pub fn resolve_type_vars_in_expr(fcx: &FnCtxt, e: &ast::Expr) {
38     assert_eq!(fcx.writeback_errors.get(), false);
39     let mut wbcx = WritebackCx::new(fcx);
40     wbcx.visit_expr(e);
41     wbcx.visit_upvar_borrow_map();
42     wbcx.visit_closures();
43 }
44
45 pub fn resolve_type_vars_in_fn(fcx: &FnCtxt,
46                                decl: &ast::FnDecl,
47                                blk: &ast::Block) {
48     assert_eq!(fcx.writeback_errors.get(), false);
49     let mut wbcx = WritebackCx::new(fcx);
50     wbcx.visit_block(blk);
51     for arg in &decl.inputs {
52         wbcx.visit_node_id(ResolvingPattern(arg.pat.span), arg.id);
53         wbcx.visit_pat(&*arg.pat);
54
55         // Privacy needs the type for the whole pattern, not just each binding
56         if !pat_util::pat_is_binding(&fcx.tcx().def_map, &*arg.pat) {
57             wbcx.visit_node_id(ResolvingPattern(arg.pat.span),
58                                arg.pat.id);
59         }
60     }
61     wbcx.visit_upvar_borrow_map();
62     wbcx.visit_closures();
63 }
64
65 ///////////////////////////////////////////////////////////////////////////
66 // The Writerback context. This visitor walks the AST, checking the
67 // fn-specific tables to find references to types or regions. It
68 // resolves those regions to remove inference variables and writes the
69 // final result back into the master tables in the tcx. Here and
70 // there, it applies a few ad-hoc checks that were not convenient to
71 // do elsewhere.
72
73 struct WritebackCx<'cx, 'tcx: 'cx> {
74     fcx: &'cx FnCtxt<'cx, 'tcx>,
75 }
76
77 impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
78     fn new(fcx: &'cx FnCtxt<'cx, 'tcx>) -> WritebackCx<'cx, 'tcx> {
79         WritebackCx { fcx: fcx }
80     }
81
82     fn tcx(&self) -> &'cx ty::ctxt<'tcx> {
83         self.fcx.tcx()
84     }
85
86     // Hacky hack: During type-checking, we treat *all* operators
87     // as potentially overloaded. But then, during writeback, if
88     // we observe that something like `a+b` is (known to be)
89     // operating on scalars, we clear the overload.
90     fn fix_scalar_binary_expr(&mut self, e: &ast::Expr) {
91         if let ast::ExprBinary(ref op, ref lhs, ref rhs) = e.node {
92             let lhs_ty = self.fcx.node_ty(lhs.id);
93             let lhs_ty = self.fcx.infcx().resolve_type_vars_if_possible(&lhs_ty);
94
95             let rhs_ty = self.fcx.node_ty(rhs.id);
96             let rhs_ty = self.fcx.infcx().resolve_type_vars_if_possible(&rhs_ty);
97
98             if lhs_ty.is_scalar() && rhs_ty.is_scalar() {
99                 self.fcx.inh.tables.borrow_mut().method_map.remove(&MethodCall::expr(e.id));
100
101                 // weird but true: the by-ref binops put an
102                 // adjustment on the lhs but not the rhs; the
103                 // adjustment for rhs is kind of baked into the
104                 // system.
105                 if !ast_util::is_by_value_binop(op.node) {
106                     self.fcx.inh.tables.borrow_mut().adjustments.remove(&lhs.id);
107                 }
108             }
109         }
110     }
111 }
112
113 ///////////////////////////////////////////////////////////////////////////
114 // Impl of Visitor for Resolver
115 //
116 // This is the master code which walks the AST. It delegates most of
117 // the heavy lifting to the generic visit and resolve functions
118 // below. In general, a function is made into a `visitor` if it must
119 // traffic in node-ids or update tables in the type context etc.
120
121 impl<'cx, 'tcx, 'v> Visitor<'v> for WritebackCx<'cx, 'tcx> {
122     fn visit_item(&mut self, _: &ast::Item) {
123         // Ignore items
124     }
125
126     fn visit_stmt(&mut self, s: &ast::Stmt) {
127         if self.fcx.writeback_errors.get() {
128             return;
129         }
130
131         self.visit_node_id(ResolvingExpr(s.span), ast_util::stmt_id(s));
132         visit::walk_stmt(self, s);
133     }
134
135     fn visit_expr(&mut self, e: &ast::Expr) {
136         if self.fcx.writeback_errors.get() {
137             return;
138         }
139
140         self.fix_scalar_binary_expr(e);
141
142         self.visit_node_id(ResolvingExpr(e.span), e.id);
143         self.visit_method_map_entry(ResolvingExpr(e.span),
144                                     MethodCall::expr(e.id));
145
146         if let ast::ExprClosure(_, ref decl, _) = e.node {
147             for input in &decl.inputs {
148                 self.visit_node_id(ResolvingExpr(e.span), input.id);
149             }
150         }
151
152         visit::walk_expr(self, e);
153     }
154
155     fn visit_block(&mut self, b: &ast::Block) {
156         if self.fcx.writeback_errors.get() {
157             return;
158         }
159
160         self.visit_node_id(ResolvingExpr(b.span), b.id);
161         visit::walk_block(self, b);
162     }
163
164     fn visit_pat(&mut self, p: &ast::Pat) {
165         if self.fcx.writeback_errors.get() {
166             return;
167         }
168
169         self.visit_node_id(ResolvingPattern(p.span), p.id);
170
171         debug!("Type for pattern binding {} (id {}) resolved to {:?}",
172                pat_to_string(p),
173                p.id,
174                self.tcx().node_id_to_type(p.id));
175
176         visit::walk_pat(self, p);
177     }
178
179     fn visit_local(&mut self, l: &ast::Local) {
180         if self.fcx.writeback_errors.get() {
181             return;
182         }
183
184         let var_ty = self.fcx.local_ty(l.span, l.id);
185         let var_ty = self.resolve(&var_ty, ResolvingLocal(l.span));
186         write_ty_to_tcx(self.tcx(), l.id, var_ty);
187         visit::walk_local(self, l);
188     }
189
190     fn visit_ty(&mut self, t: &ast::Ty) {
191         match t.node {
192             ast::TyFixedLengthVec(ref ty, ref count_expr) => {
193                 self.visit_ty(&**ty);
194                 write_ty_to_tcx(self.tcx(), count_expr.id, self.tcx().types.usize);
195             }
196             _ => visit::walk_ty(self, t)
197         }
198     }
199 }
200
201 impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
202     fn visit_upvar_borrow_map(&self) {
203         if self.fcx.writeback_errors.get() {
204             return;
205         }
206
207         for (upvar_id, upvar_capture) in self.fcx.inh.tables.borrow().upvar_capture_map.iter() {
208             let new_upvar_capture = match *upvar_capture {
209                 ty::UpvarCapture::ByValue => ty::UpvarCapture::ByValue,
210                 ty::UpvarCapture::ByRef(ref upvar_borrow) => {
211                     let r = upvar_borrow.region;
212                     let r = self.resolve(&r, ResolvingUpvar(*upvar_id));
213                     ty::UpvarCapture::ByRef(
214                         ty::UpvarBorrow { kind: upvar_borrow.kind, region: r })
215                 }
216             };
217             debug!("Upvar capture for {:?} resolved to {:?}",
218                    upvar_id,
219                    new_upvar_capture);
220             self.fcx.tcx()
221                     .tables
222                     .borrow_mut()
223                     .upvar_capture_map
224                     .insert(*upvar_id, new_upvar_capture);
225         }
226     }
227
228     fn visit_closures(&self) {
229         if self.fcx.writeback_errors.get() {
230             return
231         }
232
233         for (def_id, closure_ty) in self.fcx.inh.tables.borrow().closure_tys.iter() {
234             let closure_ty = self.resolve(closure_ty, ResolvingClosure(*def_id));
235             self.fcx.tcx().tables.borrow_mut().closure_tys.insert(*def_id, closure_ty);
236         }
237
238         for (def_id, &closure_kind) in self.fcx.inh.tables.borrow().closure_kinds.iter() {
239             self.fcx.tcx().tables.borrow_mut().closure_kinds.insert(*def_id, closure_kind);
240         }
241     }
242
243     fn visit_node_id(&self, reason: ResolveReason, id: ast::NodeId) {
244         // Resolve any borrowings for the node with id `id`
245         self.visit_adjustments(reason, id);
246
247         // Resolve the type of the node with id `id`
248         let n_ty = self.fcx.node_ty(id);
249         let n_ty = self.resolve(&n_ty, reason);
250         write_ty_to_tcx(self.tcx(), id, n_ty);
251         debug!("Node {} has type {:?}", id, n_ty);
252
253         // Resolve any substitutions
254         self.fcx.opt_node_ty_substs(id, |item_substs| {
255             write_substs_to_tcx(self.tcx(), id,
256                                 self.resolve(item_substs, reason));
257         });
258     }
259
260     fn visit_adjustments(&self, reason: ResolveReason, id: ast::NodeId) {
261         let adjustments = self.fcx.inh.tables.borrow_mut().adjustments.remove(&id);
262         match adjustments {
263             None => {
264                 debug!("No adjustments for node {}", id);
265             }
266
267             Some(adjustment) => {
268                 let resolved_adjustment = match adjustment {
269                     ty::AdjustReifyFnPointer => ty::AdjustReifyFnPointer,
270
271                     ty::AdjustUnsafeFnPointer => {
272                         ty::AdjustUnsafeFnPointer
273                     }
274
275                     ty::AdjustDerefRef(adj) => {
276                         for autoderef in 0..adj.autoderefs {
277                             let method_call = MethodCall::autoderef(id, autoderef as u32);
278                             self.visit_method_map_entry(reason, method_call);
279                         }
280
281                         ty::AdjustDerefRef(ty::AutoDerefRef {
282                             autoderefs: adj.autoderefs,
283                             autoref: self.resolve(&adj.autoref, reason),
284                             unsize: self.resolve(&adj.unsize, reason),
285                         })
286                     }
287                 };
288                 debug!("Adjustments for node {}: {:?}", id, resolved_adjustment);
289                 self.tcx().tables.borrow_mut().adjustments.insert(
290                     id, resolved_adjustment);
291             }
292         }
293     }
294
295     fn visit_method_map_entry(&self,
296                               reason: ResolveReason,
297                               method_call: MethodCall) {
298         // Resolve any method map entry
299         let new_method = match self.fcx.inh.tables.borrow_mut().method_map.remove(&method_call) {
300             Some(method) => {
301                 debug!("writeback::resolve_method_map_entry(call={:?}, entry={:?})",
302                        method_call,
303                        method);
304                 let new_method = MethodCallee {
305                     def_id: method.def_id,
306                     ty: self.resolve(&method.ty, reason),
307                     substs: self.tcx().mk_substs(self.resolve(method.substs, reason)),
308                 };
309
310                 Some(new_method)
311             }
312             None => None
313         };
314
315         //NB(jroesch): We need to match twice to avoid a double borrow which would cause an ICE
316         match new_method {
317             Some(method) => {
318                 self.tcx().tables.borrow_mut().method_map.insert(
319                     method_call,
320                     method);
321             }
322             None => {}
323         }
324     }
325
326     fn resolve<T:TypeFoldable<'tcx>>(&self, t: &T, reason: ResolveReason) -> T {
327         t.fold_with(&mut Resolver::new(self.fcx, reason))
328     }
329 }
330
331 ///////////////////////////////////////////////////////////////////////////
332 // Resolution reason.
333
334 #[derive(Copy, Clone)]
335 enum ResolveReason {
336     ResolvingExpr(Span),
337     ResolvingLocal(Span),
338     ResolvingPattern(Span),
339     ResolvingUpvar(ty::UpvarId),
340     ResolvingClosure(ast::DefId),
341 }
342
343 impl ResolveReason {
344     fn span(&self, tcx: &ty::ctxt) -> Span {
345         match *self {
346             ResolvingExpr(s) => s,
347             ResolvingLocal(s) => s,
348             ResolvingPattern(s) => s,
349             ResolvingUpvar(upvar_id) => {
350                 tcx.expr_span(upvar_id.closure_expr_id)
351             }
352             ResolvingClosure(did) => {
353                 if did.krate == ast::LOCAL_CRATE {
354                     tcx.expr_span(did.node)
355                 } else {
356                     DUMMY_SP
357                 }
358             }
359         }
360     }
361 }
362
363 ///////////////////////////////////////////////////////////////////////////
364 // The Resolver. This is the type folding engine that detects
365 // unresolved types and so forth.
366
367 struct Resolver<'cx, 'tcx: 'cx> {
368     tcx: &'cx ty::ctxt<'tcx>,
369     infcx: &'cx infer::InferCtxt<'cx, 'tcx>,
370     writeback_errors: &'cx Cell<bool>,
371     reason: ResolveReason,
372 }
373
374 impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
375     fn new(fcx: &'cx FnCtxt<'cx, 'tcx>,
376            reason: ResolveReason)
377            -> Resolver<'cx, 'tcx>
378     {
379         Resolver::from_infcx(fcx.infcx(), &fcx.writeback_errors, reason)
380     }
381
382     fn from_infcx(infcx: &'cx infer::InferCtxt<'cx, 'tcx>,
383                   writeback_errors: &'cx Cell<bool>,
384                   reason: ResolveReason)
385                   -> Resolver<'cx, 'tcx>
386     {
387         Resolver { infcx: infcx,
388                    tcx: infcx.tcx,
389                    writeback_errors: writeback_errors,
390                    reason: reason }
391     }
392
393     fn report_error(&self, e: infer::FixupError) {
394         self.writeback_errors.set(true);
395         if !self.tcx.sess.has_errors() {
396             match self.reason {
397                 ResolvingExpr(span) => {
398                     span_err!(self.tcx.sess, span, E0101,
399                         "cannot determine a type for this expression: {}",
400                         infer::fixup_err_to_string(e));
401                 }
402
403                 ResolvingLocal(span) => {
404                     span_err!(self.tcx.sess, span, E0102,
405                         "cannot determine a type for this local variable: {}",
406                         infer::fixup_err_to_string(e));
407                 }
408
409                 ResolvingPattern(span) => {
410                     span_err!(self.tcx.sess, span, E0103,
411                         "cannot determine a type for this pattern binding: {}",
412                         infer::fixup_err_to_string(e));
413                 }
414
415                 ResolvingUpvar(upvar_id) => {
416                     let span = self.reason.span(self.tcx);
417                     span_err!(self.tcx.sess, span, E0104,
418                         "cannot resolve lifetime for captured variable `{}`: {}",
419                         self.tcx.local_var_name_str(upvar_id.var_id).to_string(),
420                         infer::fixup_err_to_string(e));
421                 }
422
423                 ResolvingClosure(_) => {
424                     let span = self.reason.span(self.tcx);
425                     span_err!(self.tcx.sess, span, E0196,
426                               "cannot determine a type for this closure")
427                 }
428             }
429         }
430     }
431 }
432
433 impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> {
434     fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> {
435         self.tcx
436     }
437
438     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
439         match self.infcx.fully_resolve(&t) {
440             Ok(t) => t,
441             Err(e) => {
442                 debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable",
443                        t);
444                 self.report_error(e);
445                 self.tcx().types.err
446             }
447         }
448     }
449
450     fn fold_region(&mut self, r: ty::Region) -> ty::Region {
451         match self.infcx.fully_resolve(&r) {
452             Ok(r) => r,
453             Err(e) => {
454                 self.report_error(e);
455                 ty::ReStatic
456             }
457         }
458     }
459 }
460
461 ///////////////////////////////////////////////////////////////////////////
462 // During type check, we store promises with the result of trait
463 // lookup rather than the actual results (because the results are not
464 // necessarily available immediately). These routines unwind the
465 // promises. It is expected that we will have already reported any
466 // errors that may be encountered, so if the promises store an error,
467 // a dummy result is returned.