]> git.lizzy.rs Git - rust.git/commitdiff
Implement basic inference for closure kinds and some simple tests.
authorNiko Matsakis <niko@alum.mit.edu>
Sat, 31 Jan 2015 01:23:17 +0000 (20:23 -0500)
committerNiko Matsakis <niko@alum.mit.edu>
Sun, 1 Feb 2015 11:13:06 +0000 (06:13 -0500)
Trickier cases not yet handled.

src/librustc_typeck/check/callee.rs
src/librustc_typeck/check/closure.rs
src/librustc_typeck/check/upvar.rs
src/librustc_typeck/check/vtable.rs
src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs [new file with mode: 0644]
src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs [new file with mode: 0644]
src/test/run-pass/unboxed-closures-infer-fnmut.rs [new file with mode: 0644]
src/test/run-pass/unboxed-closures-infer-fnonce.rs [new file with mode: 0644]

index dc8bde9f84d6dfaf9502141329e132364d8a3ad6..d7a28f1843c09b1fa3525f49c454dd17633dbe25 100644 (file)
@@ -150,6 +150,7 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                 adjusted_ty: adjusted_ty,
                 autoderefref: autoderefref,
                 fn_sig: fn_sig.clone(),
+                closure_def_id: def_id,
             });
             return Some(CallStep::Closure(fn_sig));
         }
@@ -321,17 +322,19 @@ struct CallResolution<'tcx> {
     adjusted_ty: Ty<'tcx>,
     autoderefref: ty::AutoDerefRef<'tcx>,
     fn_sig: ty::FnSig<'tcx>,
+    closure_def_id: ast::DefId,
 }
 
 impl<'tcx> Repr<'tcx> for CallResolution<'tcx> {
     fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
         format!("CallResolution(call_expr={}, callee_expr={}, adjusted_ty={}, \
-                autoderefref={}, fn_sig={})",
+                autoderefref={}, fn_sig={}, closure_def_id={})",
                 self.call_expr.repr(tcx),
                 self.callee_expr.repr(tcx),
                 self.adjusted_ty.repr(tcx),
                 self.autoderefref.repr(tcx),
-                self.fn_sig.repr(tcx))
+                self.fn_sig.repr(tcx),
+                self.closure_def_id.repr(tcx))
     }
 }
 
@@ -340,6 +343,13 @@ fn attempt_resolution<'a>(&self, fcx: &FnCtxt<'a,'tcx>) -> bool {
         debug!("attempt_resolution() {}",
                self.repr(fcx.tcx()));
 
+        match fcx.closure_kind(self.closure_def_id) {
+            Some(_) => { }
+            None => {
+                return false;
+            }
+        }
+
         // We may now know enough to figure out fn vs fnmut etc.
         match try_overloaded_call_traits(fcx, self.call_expr, self.callee_expr,
                                          self.adjusted_ty, self.autoderefref.clone()) {
index 22390b9e98b11ac2d284272fd494db3f01063daa..2bbf832fdef01bd72b61d309c1921272e76b6883 100644 (file)
@@ -45,17 +45,10 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
             // that, otherwise we'll error, requesting an annotation.
             match expected_sig_and_kind {
                 None => { // don't have information about the kind, request explicit annotation
-                    // NB We still need to typeck the body, so assume `FnMut` kind just for that
-                    let kind = ty::FnMutClosureKind;
-
-                    check_closure(fcx, expr, kind, decl, body, None);
-
-                    span_err!(fcx.ccx.tcx.sess, expr.span, E0187,
-                        "can't infer the \"kind\" of the closure; explicitly annotate it; e.g. \
-                        `|&:| {{}}`");
+                    check_closure(fcx, expr, None, decl, body, None);
                 },
                 Some((sig, kind)) => {
-                    check_closure(fcx, expr, kind, decl, body, Some(sig));
+                    check_closure(fcx, expr, Some(kind), decl, body, Some(sig));
                 }
             }
         }
@@ -68,21 +61,21 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
             };
 
             let expected_sig = expected_sig_and_kind.map(|t| t.0);
-            check_closure(fcx, expr, kind, decl, body, expected_sig);
+            check_closure(fcx, expr, Some(kind), decl, body, expected_sig);
         }
     }
 }
 
 fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                           expr: &ast::Expr,
-                          kind: ty::ClosureKind,
+                          opt_kind: Option<ty::ClosureKind>,
                           decl: &'tcx ast::FnDecl,
                           body: &'tcx ast::Block,
                           expected_sig: Option<ty::FnSig<'tcx>>) {
     let expr_def_id = ast_util::local_def(expr.id);
 
-    debug!("check_closure kind={:?} expected_sig={}",
-           kind,
+    debug!("check_closure opt_kind={:?} expected_sig={}",
+           opt_kind,
            expected_sig.repr(fcx.tcx()));
 
     let mut fn_ty = astconv::ty_of_closure(
@@ -124,13 +117,16 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
     // the `closures` table.
     fn_ty.sig.0.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.0.inputs)];
 
-    debug!("closure for {} --> sig={} kind={:?}",
+    debug!("closure for {} --> sig={} opt_kind={:?}",
            expr_def_id.repr(fcx.tcx()),
            fn_ty.sig.repr(fcx.tcx()),
-           kind);
+           opt_kind);
 
     fcx.inh.closure_tys.borrow_mut().insert(expr_def_id, fn_ty);
-    fcx.inh.closure_kinds.borrow_mut().insert(expr_def_id, kind);
+    match opt_kind {
+        Some(kind) => { fcx.inh.closure_kinds.borrow_mut().insert(expr_def_id, kind); }
+        None => { }
+    }
 }
 
 fn deduce_expectations_from_expected_type<'a,'tcx>(
index 449220b1c85285c4b56fc79a76a0b65d60484b3a..2b043076e461498d60e5cacd36f282b18003a3cd 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);
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -70,6 +74,7 @@ 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> {
@@ -105,7 +110,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,6 +126,14 @@ fn check_closure(&mut self,
                      capture_clause: ast::CaptureClause,
                      _body: &ast::Block)
     {
+        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.iter() {
                 let var_node_id = freevar.def.local_node_id();
@@ -151,19 +164,22 @@ 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 }
+    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.
          */
@@ -203,6 +219,9 @@ fn adjust_upvar_borrow_kind_for_consume(&self,
                             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);
                 }
@@ -306,6 +325,13 @@ fn adjust_upvar_borrow_kind(&self,
         debug!("adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})",
                upvar_id, upvar_capture, kind);
 
+        match kind {
+            ty::ImmBorrow => { }
+            ty::UniqueImmBorrow | ty::MutBorrow => {
+                self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnMutClosureKind);
+            }
+        }
+
         match *upvar_capture {
             ty::UpvarCapture::ByValue => {
                 // Upvar is already by-value, the strongest criteria.
@@ -328,6 +354,40 @@ fn adjust_upvar_borrow_kind(&self,
             }
         }
     }
+
+    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
+            }
+
+            (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);
+            }
+        }
+    }
 }
 
 impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> {
@@ -336,14 +396,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);
             }
         }
index 9921a331fa40db965277a17e73110b7a4383bb90..60e673bcc7ba6f172ba8c435bf733da2a0129210 100644 (file)
@@ -278,7 +278,7 @@ fn check_object_type_binds_all_associated_types<'tcx>(tcx: &ty::ctxt<'tcx>,
 }
 
 pub fn select_all_fcx_obligations_and_apply_defaults(fcx: &FnCtxt) {
-    debug!("select_all_fcx_obligations_or_error");
+    debug!("select_all_fcx_obligations_and_apply_defaults");
 
     fcx.inh.deferred_resolutions.borrow_mut()
                                 .retain(|r| !r.attempt_resolution(fcx));
diff --git a/src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs b/src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs
new file mode 100644 (file)
index 0000000..9e4ed30
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+    let tick = || counter += 1;
+    tick(); //~ ERROR cannot borrow immutable local variable `tick` as mutable
+}
diff --git a/src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs b/src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs
new file mode 100644 (file)
index 0000000..0050fbd
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+use std::mem;
+
+fn main() {
+    let mut counter: Vec<i32> = Vec::new();
+    let tick = || mem::drop(counter);
+    tick();
+    tick(); //~ ERROR use of moved value: `tick`
+}
diff --git a/src/test/run-pass/unboxed-closures-infer-fnmut.rs b/src/test/run-pass/unboxed-closures-infer-fnmut.rs
new file mode 100644 (file)
index 0000000..67f36b9
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+
+    {
+        let mut tick = || counter += 1;
+        tick();
+        tick();
+    }
+
+    assert_eq!(counter, 2);
+}
diff --git a/src/test/run-pass/unboxed-closures-infer-fnonce.rs b/src/test/run-pass/unboxed-closures-infer-fnonce.rs
new file mode 100644 (file)
index 0000000..69beae7
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(unsafe_destructor)]
+
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+use std::mem;
+
+struct DropMe<'a>(&'a mut i32);
+
+#[unsafe_destructor]
+impl<'a> Drop for DropMe<'a> {
+    fn drop(&mut self) {
+        *self.0 += 1;
+    }
+}
+
+fn main() {
+    let mut counter = 0;
+
+    {
+        let drop_me = DropMe(&mut counter);
+        let tick = || mem::drop(drop_me);
+        tick();
+    }
+
+    assert_eq!(counter, 1);
+}