Trickier cases not yet handled.
adjusted_ty: adjusted_ty,
autoderefref: autoderefref,
fn_sig: fn_sig.clone(),
+ closure_def_id: def_id,
});
return Some(CallStep::Closure(fn_sig));
}
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))
}
}
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()) {
// 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));
}
}
}
};
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(
// 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>(
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);
}
///////////////////////////////////////////////////////////////////////////
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> {
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> {
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();
// 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.
*/
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);
}
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.
}
}
}
+
+ 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> {
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);
}
}
}
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));
--- /dev/null
+// 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
+}
--- /dev/null
+// 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`
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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);
+}