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