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