Implement basic inference for closure kinds and some simple tests.
Trickier cases not yet handled.
This commit is contained in:
parent
2f465869fd
commit
e1f54f01d6
8 changed files with 196 additions and 30 deletions
|
@ -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()) {
|
||||
|
|
|
@ -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>(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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`
|
||||
}
|
24
src/test/run-pass/unboxed-closures-infer-fnmut.rs
Normal file
24
src/test/run-pass/unboxed-closures-infer-fnmut.rs
Normal 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);
|
||||
}
|
37
src/test/run-pass/unboxed-closures-infer-fnonce.rs
Normal file
37
src/test/run-pass/unboxed-closures-infer-fnonce.rs
Normal 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);
|
||||
}
|
Loading…
Add table
Reference in a new issue