]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_typeck/check/upvar.rs
Future-proof indexing on maps: remove IndexMut
[rust.git] / src / librustc_typeck / check / upvar.rs
index 7fc0a83e34ac591765e935a6928b33fe4014de88..ce4bb4465517b21ef55189e1fc99f2312fae03ee 100644 (file)
@@ -46,7 +46,9 @@
 use middle::mem_categorization as mc;
 use middle::ty::{self};
 use middle::infer::{InferCtxt, UpvarRegion};
+use std::collections::HashSet;
 use syntax::ast;
+use syntax::ast_util;
 use syntax::codemap::Span;
 use syntax::visit::{self, Visitor};
 use util::ppaux::Repr;
 
 pub fn closure_analyze_fn(fcx: &FnCtxt,
                           _id: ast::NodeId,
-                          decl: &ast::FnDecl,
-                          body: &ast::Block) {
+                          _decl: &ast::FnDecl,
+                          body: &ast::Block)
+{
     let mut seed = SeedBorrowKind::new(fcx);
     seed.visit_block(body);
+    let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
 
-    let mut adjust = AdjustBorrowKind::new(fcx);
-    adjust.analyze_fn(decl, body);
+    let mut adjust = AdjustBorrowKind::new(fcx, &closures_with_inferred_kinds);
+    adjust.visit_block(body);
+
+    // it's our job to process these.
+    assert!(fcx.inh.deferred_call_resolutions.borrow().is_empty());
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -70,12 +77,13 @@ pub fn closure_analyze_fn(fcx: &FnCtxt,
 
 struct SeedBorrowKind<'a,'tcx:'a> {
     fcx: &'a FnCtxt<'a,'tcx>,
+    closures_with_inferred_kinds: HashSet<ast::NodeId>,
 }
 
 impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
     fn visit_expr(&mut self, expr: &ast::Expr) {
         match expr.node {
-            ast::ExprClosure(cc, _, _, ref body) => {
+            ast::ExprClosure(cc, _, ref body) => {
                 self.check_closure(expr, cc, &**body);
             }
 
@@ -105,7 +113,7 @@ fn visit_fn(&mut self,
 
 impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
     fn new(fcx: &'a FnCtxt<'a,'tcx>) -> SeedBorrowKind<'a,'tcx> {
-        SeedBorrowKind { fcx: fcx }
+        SeedBorrowKind { fcx: fcx, closures_with_inferred_kinds: HashSet::new() }
     }
 
     fn tcx(&self) -> &'a ty::ctxt<'tcx> {
@@ -121,25 +129,37 @@ fn check_closure(&mut self,
                      capture_clause: ast::CaptureClause,
                      _body: &ast::Block)
     {
-        match capture_clause {
-            ast::CaptureByValue => {}
-            _ => {
-                ty::with_freevars(self.tcx(), expr.id, |freevars| {
-                    for freevar in freevars.iter() {
-                        let var_node_id = freevar.def.local_node_id();
-                        let upvar_id = ty::UpvarId { var_id: var_node_id,
-                                                     closure_expr_id: expr.id };
-                        debug!("seed upvar_id {:?}", upvar_id);
+        let closure_def_id = ast_util::local_def(expr.id);
+        if !self.fcx.inh.closure_kinds.borrow().contains_key(&closure_def_id) {
+            self.closures_with_inferred_kinds.insert(expr.id);
+            self.fcx.inh.closure_kinds.borrow_mut().insert(closure_def_id, ty::FnClosureKind);
+            debug!("check_closure: adding closure_id={} to closures_with_inferred_kinds",
+                   closure_def_id.repr(self.tcx()));
+        }
+
+        ty::with_freevars(self.tcx(), expr.id, |freevars| {
+            for freevar in freevars {
+                let var_node_id = freevar.def.local_node_id();
+                let upvar_id = ty::UpvarId { var_id: var_node_id,
+                                             closure_expr_id: expr.id };
+                debug!("seed upvar_id {:?}", upvar_id);
+
+                let capture_kind = match capture_clause {
+                    ast::CaptureByValue => {
+                        ty::UpvarCapture::ByValue
+                    }
+                    ast::CaptureByRef => {
                         let origin = UpvarRegion(upvar_id, expr.span);
                         let freevar_region = self.infcx().next_region_var(origin);
                         let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow,
                                                              region: freevar_region };
-                        self.fcx.inh.upvar_borrow_map.borrow_mut().insert(upvar_id,
-                                                                          upvar_borrow);
+                        ty::UpvarCapture::ByRef(upvar_borrow)
                     }
-                });
+                };
+
+                self.fcx.inh.upvar_capture_map.borrow_mut().insert(upvar_id, capture_kind);
             }
-        }
+        });
     }
 }
 
@@ -147,34 +167,134 @@ fn check_closure(&mut self,
 // ADJUST BORROW KIND
 
 struct AdjustBorrowKind<'a,'tcx:'a> {
-    fcx: &'a FnCtxt<'a,'tcx>
+    fcx: &'a FnCtxt<'a,'tcx>,
+    closures_with_inferred_kinds: &'a HashSet<ast::NodeId>,
 }
 
-impl<'a,'tcx> AdjustBorrowKind<'a,'tcx>{
-    fn new(fcx: &'a FnCtxt<'a,'tcx>) -> AdjustBorrowKind<'a,'tcx> {
-        AdjustBorrowKind { fcx: fcx }
+impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
+    fn new(fcx: &'a FnCtxt<'a,'tcx>,
+           closures_with_inferred_kinds: &'a HashSet<ast::NodeId>)
+           -> AdjustBorrowKind<'a,'tcx> {
+        AdjustBorrowKind { fcx: fcx, closures_with_inferred_kinds: closures_with_inferred_kinds }
     }
 
     fn tcx(&self) -> &'a ty::ctxt<'tcx> {
         self.fcx.tcx()
     }
 
-    fn analyze_fn(&mut self, decl: &ast::FnDecl, body: &ast::Block) {
+    fn analyze_closure(&mut self, id: ast::NodeId, decl: &ast::FnDecl, body: &ast::Block) {
         /*!
          * Analysis starting point.
          */
 
         self.visit_block(body);
 
-        debug!("analyzing fn body with id {}", body.id);
+        debug!("analyzing closure `{}` with fn body id `{}`", id, body.id);
 
         let mut euv = euv::ExprUseVisitor::new(self, self.fcx);
         euv.walk_fn(decl, body);
+
+        // If we had not yet settled on a closure kind for this closure,
+        // then we should have by now. Process and remove any deferred resolutions.
+        //
+        // Interesting fact: all calls to this closure must come
+        // *after* its definition.  Initially, I thought that some
+        // kind of fixed-point iteration would be required, due to the
+        // possibility of twisted examples like this one:
+        //
+        // ```rust
+        // let mut closure0 = None;
+        // let vec = vec!(1, 2, 3);
+        //
+        // loop {
+        //     {
+        //         let closure1 = || {
+        //             match closure0.take() {
+        //                 Some(c) => {
+        //                     return c(); // (*) call to `closure0` before it is defined
+        //                 }
+        //                 None => { }
+        //             }
+        //         };
+        //         closure1();
+        //     }
+        //
+        //     closure0 = || vec;
+        // }
+        // ```
+        //
+        // However, this turns out to be wrong. Examples like this
+        // fail to compile because the type of the variable `c` above
+        // is an inference variable.  And in fact since closure types
+        // cannot be written, there is no way to make this example
+        // work without a boxed closure. This implies that we can't
+        // have two closures that recursively call one another without
+        // some form of boxing (and hence explicit writing of a
+        // closure kind) involved. Huzzah. -nmatsakis
+        let closure_def_id = ast_util::local_def(id);
+        if self.closures_with_inferred_kinds.contains(&id) {
+            let mut deferred_call_resolutions =
+                self.fcx.remove_deferred_call_resolutions(closure_def_id);
+            for deferred_call_resolution in deferred_call_resolutions.iter_mut() {
+                deferred_call_resolution.resolve(self.fcx);
+            }
+        }
+    }
+
+    fn adjust_upvar_borrow_kind_for_consume(&self,
+                                            cmt: mc::cmt<'tcx>,
+                                            mode: euv::ConsumeMode)
+    {
+        debug!("adjust_upvar_borrow_kind_for_consume(cmt={}, mode={:?})",
+               cmt.repr(self.tcx()), mode);
+
+        // we only care about moves
+        match mode {
+            euv::Copy => { return; }
+            euv::Move(_) => { }
+        }
+
+        // watch out for a move of the deref of a borrowed pointer;
+        // for that to be legal, the upvar would have to be borrowed
+        // by value instead
+        let guarantor = cmt.guarantor();
+        debug!("adjust_upvar_borrow_kind_for_consume: guarantor={}",
+               guarantor.repr(self.tcx()));
+        match guarantor.cat {
+            mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
+            mc::cat_deref(_, _, mc::Implicit(..)) => {
+                match cmt.note {
+                    mc::NoteUpvarRef(upvar_id) => {
+                        debug!("adjust_upvar_borrow_kind_for_consume: \
+                                setting upvar_id={:?} to by value",
+                               upvar_id);
+
+                        // to move out of an upvar, this must be a FnOnce closure
+                        self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnOnceClosureKind);
+
+                        let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
+                        upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue);
+                    }
+                    mc::NoteClosureEnv(upvar_id) => {
+                        // we get just a closureenv ref if this is a
+                        // `move` closure, or if the upvar has already
+                        // been inferred to by-value. In any case, we
+                        // must still adjust the kind of the closure
+                        // to be a FnOnce closure to permit moves out
+                        // of the environment.
+                        self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnOnceClosureKind);
+                    }
+                    mc::NoteNone => {
+                    }
+                }
+            }
+            _ => { }
+        }
     }
 
     /// Indicates that `cmt` is being directly mutated (e.g., assigned
     /// to). If cmt contains any by-ref upvars, this implies that
-    /// those upvars must be borrowed using an `&mut` borow.
+    /// those upvars must be borrowed using an `&mut` borrow.
     fn adjust_upvar_borrow_kind_for_mut(&mut self, cmt: mc::cmt<'tcx>) {
         debug!("adjust_upvar_borrow_kind_for_mut(cmt={})",
                cmt.repr(self.tcx()));
@@ -190,15 +310,7 @@ fn adjust_upvar_borrow_kind_for_mut(&mut self, cmt: mc::cmt<'tcx>) {
 
             mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
             mc::cat_deref(base, _, mc::Implicit(..)) => {
-                if let mc::NoteUpvarRef(upvar_id) = cmt.note {
-                    // if this is an implicit deref of an
-                    // upvar, then we need to modify the
-                    // borrow_kind of the upvar to make sure it
-                    // is inferred to mutable if necessary
-                    let mut upvar_borrow_map = self.fcx.inh.upvar_borrow_map.borrow_mut();
-                    let ub = &mut upvar_borrow_map[upvar_id];
-                    self.adjust_upvar_borrow_kind(upvar_id, ub, ty::MutBorrow);
-                } else {
+                if !self.try_adjust_upvar_deref(&cmt.note, ty::MutBorrow) {
                     // assignment to deref of an `&mut`
                     // borrowed pointer implies that the
                     // pointer itself must be unique, but not
@@ -232,15 +344,7 @@ fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) {
 
             mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
             mc::cat_deref(base, _, mc::Implicit(..)) => {
-                if let mc::NoteUpvarRef(upvar_id) = cmt.note {
-                    // if this is an implicit deref of an
-                    // upvar, then we need to modify the
-                    // borrow_kind of the upvar to make sure it
-                    // is inferred to unique if necessary
-                    let mut ub = self.fcx.inh.upvar_borrow_map.borrow_mut();
-                    let ub = &mut ub[upvar_id];
-                    self.adjust_upvar_borrow_kind(upvar_id, ub, ty::UniqueImmBorrow);
-                } else {
+                if !self.try_adjust_upvar_deref(&cmt.note, ty::UniqueImmBorrow) {
                     // for a borrowed pointer to be unique, its
                     // base must be unique
                     self.adjust_upvar_borrow_kind_for_unique(base);
@@ -256,29 +360,112 @@ fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) {
         }
     }
 
+    fn try_adjust_upvar_deref(&self,
+                              note: &mc::Note,
+                              borrow_kind: ty::BorrowKind)
+                              -> bool
+    {
+        assert!(match borrow_kind {
+            ty::MutBorrow => true,
+            ty::UniqueImmBorrow => true,
+
+            // imm borrows never require adjusting any kinds, so we don't wind up here
+            ty::ImmBorrow => false,
+        });
+
+        match *note {
+            mc::NoteUpvarRef(upvar_id) => {
+                // if this is an implicit deref of an
+                // upvar, then we need to modify the
+                // borrow_kind of the upvar to make sure it
+                // is inferred to mutable if necessary
+                let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
+                let ub = upvar_capture_map.get_mut(&upvar_id).unwrap();
+                self.adjust_upvar_borrow_kind(upvar_id, ub, borrow_kind);
+
+                // also need to be in an FnMut closure since this is not an ImmBorrow
+                self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnMutClosureKind);
+
+                true
+            }
+            mc::NoteClosureEnv(upvar_id) => {
+                // this kind of deref occurs in a `move` closure, or
+                // for a by-value upvar; in either case, to mutate an
+                // upvar, we need to be an FnMut closure
+                self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnMutClosureKind);
+
+                true
+            }
+            mc::NoteNone => {
+                false
+            }
+        }
+    }
+
     /// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind
     /// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed
     /// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by
     /// some particular use.
     fn adjust_upvar_borrow_kind(&self,
                                 upvar_id: ty::UpvarId,
-                                upvar_borrow: &mut ty::UpvarBorrow,
+                                upvar_capture: &mut ty::UpvarCapture,
                                 kind: ty::BorrowKind) {
-        debug!("adjust_upvar_borrow_kind: id={:?} kind=({:?} -> {:?})",
-               upvar_id, upvar_borrow.kind, kind);
-
-        match (upvar_borrow.kind, kind) {
-            // Take RHS:
-            (ty::ImmBorrow, ty::UniqueImmBorrow) |
-            (ty::ImmBorrow, ty::MutBorrow) |
-            (ty::UniqueImmBorrow, ty::MutBorrow) => {
-                upvar_borrow.kind = kind;
+        debug!("adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})",
+               upvar_id, upvar_capture, kind);
+
+        match *upvar_capture {
+            ty::UpvarCapture::ByValue => {
+                // Upvar is already by-value, the strongest criteria.
+            }
+            ty::UpvarCapture::ByRef(ref mut upvar_borrow) => {
+                match (upvar_borrow.kind, kind) {
+                    // Take RHS:
+                    (ty::ImmBorrow, ty::UniqueImmBorrow) |
+                    (ty::ImmBorrow, ty::MutBorrow) |
+                    (ty::UniqueImmBorrow, ty::MutBorrow) => {
+                        upvar_borrow.kind = kind;
+                    }
+                    // Take LHS:
+                    (ty::ImmBorrow, ty::ImmBorrow) |
+                    (ty::UniqueImmBorrow, ty::ImmBorrow) |
+                    (ty::UniqueImmBorrow, ty::UniqueImmBorrow) |
+                    (ty::MutBorrow, _) => {
+                    }
+                }
+            }
+        }
+    }
+
+    fn adjust_closure_kind(&self,
+                           closure_id: ast::NodeId,
+                           new_kind: ty::ClosureKind) {
+        debug!("adjust_closure_kind(closure_id={}, new_kind={:?})",
+               closure_id, new_kind);
+
+        if !self.closures_with_inferred_kinds.contains(&closure_id) {
+            return;
+        }
+
+        let closure_def_id = ast_util::local_def(closure_id);
+        let mut closure_kinds = self.fcx.inh.closure_kinds.borrow_mut();
+        let existing_kind = closure_kinds[closure_def_id];
+
+        debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}",
+               closure_id, existing_kind, new_kind);
+
+        match (existing_kind, new_kind) {
+            (ty::FnClosureKind, ty::FnClosureKind) |
+            (ty::FnMutClosureKind, ty::FnClosureKind) |
+            (ty::FnMutClosureKind, ty::FnMutClosureKind) |
+            (ty::FnOnceClosureKind, _) => {
+                // no change needed
             }
-            // Take LHS:
-            (ty::ImmBorrow, ty::ImmBorrow) |
-            (ty::UniqueImmBorrow, ty::ImmBorrow) |
-            (ty::UniqueImmBorrow, ty::UniqueImmBorrow) |
-            (ty::MutBorrow, _) => {
+
+            (ty::FnClosureKind, ty::FnMutClosureKind) |
+            (ty::FnClosureKind, ty::FnOnceClosureKind) |
+            (ty::FnMutClosureKind, ty::FnOnceClosureKind) => {
+                // new kind is stronger than the old kind
+                closure_kinds.insert(closure_def_id, new_kind);
             }
         }
     }
@@ -290,14 +477,14 @@ fn visit_fn(&mut self,
                 decl: &'v ast::FnDecl,
                 body: &'v ast::Block,
                 span: Span,
-                _id: ast::NodeId)
+                id: ast::NodeId)
     {
         match fn_kind {
             visit::FkItemFn(..) | visit::FkMethod(..) => {
                 // ignore nested fn items
             }
             visit::FkFnBlock => {
-                self.analyze_fn(decl, body);
+                self.analyze_closure(id, decl, body);
                 visit::walk_fn(self, fn_kind, decl, body, span);
             }
         }
@@ -308,9 +495,12 @@ impl<'a,'tcx> euv::Delegate<'tcx> for AdjustBorrowKind<'a,'tcx> {
     fn consume(&mut self,
                _consume_id: ast::NodeId,
                _consume_span: Span,
-               _cmt: mc::cmt<'tcx>,
-               _mode: euv::ConsumeMode)
-    {}
+               cmt: mc::cmt<'tcx>,
+               mode: euv::ConsumeMode)
+    {
+        debug!("consume(cmt={},mode={:?})", cmt.repr(self.tcx()), mode);
+        self.adjust_upvar_borrow_kind_for_consume(cmt, mode);
+    }
 
     fn matched_pat(&mut self,
                    _matched_pat: &ast::Pat,
@@ -320,9 +510,12 @@ fn matched_pat(&mut self,
 
     fn consume_pat(&mut self,
                    _consume_pat: &ast::Pat,
-                   _cmt: mc::cmt<'tcx>,
-                   _mode: euv::ConsumeMode)
-    {}
+                   cmt: mc::cmt<'tcx>,
+                   mode: euv::ConsumeMode)
+    {
+        debug!("consume_pat(cmt={},mode={:?})", cmt.repr(self.tcx()), mode);
+        self.adjust_upvar_borrow_kind_for_consume(cmt, mode);
+    }
 
     fn borrow(&mut self,
               borrow_id: ast::NodeId,
@@ -363,5 +556,3 @@ fn mutate(&mut self,
         self.adjust_upvar_borrow_kind_for_mut(assignee_cmt);
     }
 }
-
-