diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2015-01-30 20:23:17 -0500 |
|---|---|---|
| committer | Niko Matsakis <niko@alum.mit.edu> | 2015-02-01 06:13:06 -0500 |
| commit | e1f54f01d666279f50153536eb5a8494f26d89a7 (patch) | |
| tree | f6e701e84bd1cc40ef333bf82235e5bcd807fc2d | |
| parent | 2f465869fd23d1bea7a99ca2e602c94e13d0dffb (diff) | |
| download | rust-e1f54f01d666279f50153536eb5a8494f26d89a7.tar.gz rust-e1f54f01d666279f50153536eb5a8494f26d89a7.zip | |
Implement basic inference for closure kinds and some simple tests.
Trickier cases not yet handled.
| -rw-r--r-- | src/librustc_typeck/check/callee.rs | 14 | ||||
| -rw-r--r-- | src/librustc_typeck/check/closure.rs | 28 | ||||
| -rw-r--r-- | src/librustc_typeck/check/upvar.rs | 82 | ||||
| -rw-r--r-- | src/librustc_typeck/check/vtable.rs | 2 | ||||
| -rw-r--r-- | src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs | 18 | ||||
| -rw-r--r-- | src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs | 21 | ||||
| -rw-r--r-- | src/test/run-pass/unboxed-closures-infer-fnmut.rs | 24 | ||||
| -rw-r--r-- | src/test/run-pass/unboxed-closures-infer-fnonce.rs | 37 |
8 files changed, 196 insertions, 30 deletions
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index dc8bde9f84d..d7a28f1843c 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -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 @@ impl<'tcx> DeferredResolution<'tcx> for CallResolution<'tcx> { 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()) { diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 22390b9e98b..2bbf832fdef 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -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>( diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 449220b1c85..2b043076e46 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -46,7 +46,9 @@ use middle::expr_use_visitor as euv; 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; @@ -56,13 +58,15 @@ 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 @@ impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> { 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 @@ impl<'a,'tcx> SeedBorrowKind<'a,'tcx> { 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 @@ impl<'a,'tcx> SeedBorrowKind<'a,'tcx> { // 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 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { 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 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { 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 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { } } } + + 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 @@ impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> { 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); } } diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs index 9921a331fa4..60e673bcc7b 100644 --- a/src/librustc_typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -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 index 00000000000..9e4ed307996 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs @@ -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 index 00000000000..0050fbdde26 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs @@ -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 index 00000000000..67f36b9a920 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-fnmut.rs @@ -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 index 00000000000..69beae77184 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-fnonce.rs @@ -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); +} |
