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