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