Implement basic inference for closure kinds and some simple tests.

Trickier cases not yet handled.
This commit is contained in:
Niko Matsakis 2015-01-30 20:23:17 -05:00
parent 2f465869fd
commit e1f54f01d6
8 changed files with 196 additions and 30 deletions

View 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 @@ 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()) {

View 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>(

View file

@ -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);
}
}

View 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));

View file

@ -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
}

View file

@ -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`
}

View file

@ -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);
}

View file

@ -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);
}