Skip to content

Commit e1f54f0

Browse files
committed
Implement basic inference for closure kinds and some simple tests.
Trickier cases not yet handled.
1 parent 2f46586 commit e1f54f0

File tree

8 files changed

+196
-30
lines changed

8 files changed

+196
-30
lines changed

src/librustc_typeck/check/callee.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
150150
adjusted_ty: adjusted_ty,
151151
autoderefref: autoderefref,
152152
fn_sig: fn_sig.clone(),
153+
closure_def_id: def_id,
153154
});
154155
return Some(CallStep::Closure(fn_sig));
155156
}
@@ -321,17 +322,19 @@ struct CallResolution<'tcx> {
321322
adjusted_ty: Ty<'tcx>,
322323
autoderefref: ty::AutoDerefRef<'tcx>,
323324
fn_sig: ty::FnSig<'tcx>,
325+
closure_def_id: ast::DefId,
324326
}
325327

326328
impl<'tcx> Repr<'tcx> for CallResolution<'tcx> {
327329
fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
328330
format!("CallResolution(call_expr={}, callee_expr={}, adjusted_ty={}, \
329-
autoderefref={}, fn_sig={})",
331+
autoderefref={}, fn_sig={}, closure_def_id={})",
330332
self.call_expr.repr(tcx),
331333
self.callee_expr.repr(tcx),
332334
self.adjusted_ty.repr(tcx),
333335
self.autoderefref.repr(tcx),
334-
self.fn_sig.repr(tcx))
336+
self.fn_sig.repr(tcx),
337+
self.closure_def_id.repr(tcx))
335338
}
336339
}
337340

@@ -340,6 +343,13 @@ impl<'tcx> DeferredResolution<'tcx> for CallResolution<'tcx> {
340343
debug!("attempt_resolution() {}",
341344
self.repr(fcx.tcx()));
342345

346+
match fcx.closure_kind(self.closure_def_id) {
347+
Some(_) => { }
348+
None => {
349+
return false;
350+
}
351+
}
352+
343353
// We may now know enough to figure out fn vs fnmut etc.
344354
match try_overloaded_call_traits(fcx, self.call_expr, self.callee_expr,
345355
self.adjusted_ty, self.autoderefref.clone()) {

src/librustc_typeck/check/closure.rs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,10 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
4545
// that, otherwise we'll error, requesting an annotation.
4646
match expected_sig_and_kind {
4747
None => { // don't have information about the kind, request explicit annotation
48-
// NB We still need to typeck the body, so assume `FnMut` kind just for that
49-
let kind = ty::FnMutClosureKind;
50-
51-
check_closure(fcx, expr, kind, decl, body, None);
52-
53-
span_err!(fcx.ccx.tcx.sess, expr.span, E0187,
54-
"can't infer the \"kind\" of the closure; explicitly annotate it; e.g. \
55-
`|&:| {{}}`");
48+
check_closure(fcx, expr, None, decl, body, None);
5649
},
5750
Some((sig, kind)) => {
58-
check_closure(fcx, expr, kind, decl, body, Some(sig));
51+
check_closure(fcx, expr, Some(kind), decl, body, Some(sig));
5952
}
6053
}
6154
}
@@ -68,21 +61,21 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
6861
};
6962

7063
let expected_sig = expected_sig_and_kind.map(|t| t.0);
71-
check_closure(fcx, expr, kind, decl, body, expected_sig);
64+
check_closure(fcx, expr, Some(kind), decl, body, expected_sig);
7265
}
7366
}
7467
}
7568

7669
fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
7770
expr: &ast::Expr,
78-
kind: ty::ClosureKind,
71+
opt_kind: Option<ty::ClosureKind>,
7972
decl: &'tcx ast::FnDecl,
8073
body: &'tcx ast::Block,
8174
expected_sig: Option<ty::FnSig<'tcx>>) {
8275
let expr_def_id = ast_util::local_def(expr.id);
8376

84-
debug!("check_closure kind={:?} expected_sig={}",
85-
kind,
77+
debug!("check_closure opt_kind={:?} expected_sig={}",
78+
opt_kind,
8679
expected_sig.repr(fcx.tcx()));
8780

8881
let mut fn_ty = astconv::ty_of_closure(
@@ -124,13 +117,16 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
124117
// the `closures` table.
125118
fn_ty.sig.0.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.0.inputs)];
126119

127-
debug!("closure for {} --> sig={} kind={:?}",
120+
debug!("closure for {} --> sig={} opt_kind={:?}",
128121
expr_def_id.repr(fcx.tcx()),
129122
fn_ty.sig.repr(fcx.tcx()),
130-
kind);
123+
opt_kind);
131124

132125
fcx.inh.closure_tys.borrow_mut().insert(expr_def_id, fn_ty);
133-
fcx.inh.closure_kinds.borrow_mut().insert(expr_def_id, kind);
126+
match opt_kind {
127+
Some(kind) => { fcx.inh.closure_kinds.borrow_mut().insert(expr_def_id, kind); }
128+
None => { }
129+
}
134130
}
135131

136132
fn deduce_expectations_from_expected_type<'a,'tcx>(

src/librustc_typeck/check/upvar.rs

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ use middle::expr_use_visitor as euv;
4646
use middle::mem_categorization as mc;
4747
use middle::ty::{self};
4848
use middle::infer::{InferCtxt, UpvarRegion};
49+
use std::collections::HashSet;
4950
use syntax::ast;
51+
use syntax::ast_util;
5052
use syntax::codemap::Span;
5153
use syntax::visit::{self, Visitor};
5254
use util::ppaux::Repr;
@@ -56,20 +58,23 @@ use util::ppaux::Repr;
5658

5759
pub fn closure_analyze_fn(fcx: &FnCtxt,
5860
_id: ast::NodeId,
59-
decl: &ast::FnDecl,
60-
body: &ast::Block) {
61+
_decl: &ast::FnDecl,
62+
body: &ast::Block)
63+
{
6164
let mut seed = SeedBorrowKind::new(fcx);
6265
seed.visit_block(body);
66+
let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
6367

64-
let mut adjust = AdjustBorrowKind::new(fcx);
65-
adjust.analyze_fn(decl, body);
68+
let mut adjust = AdjustBorrowKind::new(fcx, &closures_with_inferred_kinds);
69+
adjust.visit_block(body);
6670
}
6771

6872
///////////////////////////////////////////////////////////////////////////
6973
// SEED BORROW KIND
7074

7175
struct SeedBorrowKind<'a,'tcx:'a> {
7276
fcx: &'a FnCtxt<'a,'tcx>,
77+
closures_with_inferred_kinds: HashSet<ast::NodeId>,
7378
}
7479

7580
impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
@@ -105,7 +110,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
105110

106111
impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
107112
fn new(fcx: &'a FnCtxt<'a,'tcx>) -> SeedBorrowKind<'a,'tcx> {
108-
SeedBorrowKind { fcx: fcx }
113+
SeedBorrowKind { fcx: fcx, closures_with_inferred_kinds: HashSet::new() }
109114
}
110115

111116
fn tcx(&self) -> &'a ty::ctxt<'tcx> {
@@ -121,6 +126,14 @@ impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
121126
capture_clause: ast::CaptureClause,
122127
_body: &ast::Block)
123128
{
129+
let closure_def_id = ast_util::local_def(expr.id);
130+
if !self.fcx.inh.closure_kinds.borrow().contains_key(&closure_def_id) {
131+
self.closures_with_inferred_kinds.insert(expr.id);
132+
self.fcx.inh.closure_kinds.borrow_mut().insert(closure_def_id, ty::FnClosureKind);
133+
debug!("check_closure: adding closure_id={} to closures_with_inferred_kinds",
134+
closure_def_id.repr(self.tcx()));
135+
}
136+
124137
ty::with_freevars(self.tcx(), expr.id, |freevars| {
125138
for freevar in freevars.iter() {
126139
let var_node_id = freevar.def.local_node_id();
@@ -151,19 +164,22 @@ impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
151164
// ADJUST BORROW KIND
152165

153166
struct AdjustBorrowKind<'a,'tcx:'a> {
154-
fcx: &'a FnCtxt<'a,'tcx>
167+
fcx: &'a FnCtxt<'a,'tcx>,
168+
closures_with_inferred_kinds: &'a HashSet<ast::NodeId>,
155169
}
156170

157171
impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
158-
fn new(fcx: &'a FnCtxt<'a,'tcx>) -> AdjustBorrowKind<'a,'tcx> {
159-
AdjustBorrowKind { fcx: fcx }
172+
fn new(fcx: &'a FnCtxt<'a,'tcx>,
173+
closures_with_inferred_kinds: &'a HashSet<ast::NodeId>)
174+
-> AdjustBorrowKind<'a,'tcx> {
175+
AdjustBorrowKind { fcx: fcx, closures_with_inferred_kinds: closures_with_inferred_kinds }
160176
}
161177

162178
fn tcx(&self) -> &'a ty::ctxt<'tcx> {
163179
self.fcx.tcx()
164180
}
165181

166-
fn analyze_fn(&mut self, decl: &ast::FnDecl, body: &ast::Block) {
182+
fn analyze_closure(&mut self, id: ast::NodeId, decl: &ast::FnDecl, body: &ast::Block) {
167183
/*!
168184
* Analysis starting point.
169185
*/
@@ -203,6 +219,9 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
203219
setting upvar_id={:?} to by value",
204220
upvar_id);
205221

222+
// to move out of an upvar, this must be a FnOnce closure
223+
self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnOnceClosureKind);
224+
206225
let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut();
207226
upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue);
208227
}
@@ -306,6 +325,13 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
306325
debug!("adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})",
307326
upvar_id, upvar_capture, kind);
308327

328+
match kind {
329+
ty::ImmBorrow => { }
330+
ty::UniqueImmBorrow | ty::MutBorrow => {
331+
self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnMutClosureKind);
332+
}
333+
}
334+
309335
match *upvar_capture {
310336
ty::UpvarCapture::ByValue => {
311337
// Upvar is already by-value, the strongest criteria.
@@ -328,6 +354,40 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> {
328354
}
329355
}
330356
}
357+
358+
fn adjust_closure_kind(&self,
359+
closure_id: ast::NodeId,
360+
new_kind: ty::ClosureKind) {
361+
debug!("adjust_closure_kind(closure_id={}, new_kind={:?})",
362+
closure_id, new_kind);
363+
364+
if !self.closures_with_inferred_kinds.contains(&closure_id) {
365+
return;
366+
}
367+
368+
let closure_def_id = ast_util::local_def(closure_id);
369+
let mut closure_kinds = self.fcx.inh.closure_kinds.borrow_mut();
370+
let existing_kind = closure_kinds[closure_def_id];
371+
372+
debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}",
373+
closure_id, existing_kind, new_kind);
374+
375+
match (existing_kind, new_kind) {
376+
(ty::FnClosureKind, ty::FnClosureKind) |
377+
(ty::FnMutClosureKind, ty::FnClosureKind) |
378+
(ty::FnMutClosureKind, ty::FnMutClosureKind) |
379+
(ty::FnOnceClosureKind, _) => {
380+
// no change needed
381+
}
382+
383+
(ty::FnClosureKind, ty::FnMutClosureKind) |
384+
(ty::FnClosureKind, ty::FnOnceClosureKind) |
385+
(ty::FnMutClosureKind, ty::FnOnceClosureKind) => {
386+
// new kind is stronger than the old kind
387+
closure_kinds.insert(closure_def_id, new_kind);
388+
}
389+
}
390+
}
331391
}
332392

333393
impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> {
@@ -336,14 +396,14 @@ impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> {
336396
decl: &'v ast::FnDecl,
337397
body: &'v ast::Block,
338398
span: Span,
339-
_id: ast::NodeId)
399+
id: ast::NodeId)
340400
{
341401
match fn_kind {
342402
visit::FkItemFn(..) | visit::FkMethod(..) => {
343403
// ignore nested fn items
344404
}
345405
visit::FkFnBlock => {
346-
self.analyze_fn(decl, body);
406+
self.analyze_closure(id, decl, body);
347407
visit::walk_fn(self, fn_kind, decl, body, span);
348408
}
349409
}

src/librustc_typeck/check/vtable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ fn check_object_type_binds_all_associated_types<'tcx>(tcx: &ty::ctxt<'tcx>,
278278
}
279279

280280
pub fn select_all_fcx_obligations_and_apply_defaults(fcx: &FnCtxt) {
281-
debug!("select_all_fcx_obligations_or_error");
281+
debug!("select_all_fcx_obligations_and_apply_defaults");
282282

283283
fcx.inh.deferred_resolutions.borrow_mut()
284284
.retain(|r| !r.attempt_resolution(fcx));
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that we are able to infer a suitable kind for this closure
12+
// that is just called (`FnMut`).
13+
14+
fn main() {
15+
let mut counter = 0;
16+
let tick = || counter += 1;
17+
tick(); //~ ERROR cannot borrow immutable local variable `tick` as mutable
18+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that we are able to infer a suitable kind for this closure
12+
// that is just called (`FnMut`).
13+
14+
use std::mem;
15+
16+
fn main() {
17+
let mut counter: Vec<i32> = Vec::new();
18+
let tick = || mem::drop(counter);
19+
tick();
20+
tick(); //~ ERROR use of moved value: `tick`
21+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that we are able to infer a suitable kind for this closure
12+
// that is just called (`FnMut`).
13+
14+
fn main() {
15+
let mut counter = 0;
16+
17+
{
18+
let mut tick = || counter += 1;
19+
tick();
20+
tick();
21+
}
22+
23+
assert_eq!(counter, 2);
24+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(unsafe_destructor)]
12+
13+
// Test that we are able to infer a suitable kind for this closure
14+
// that is just called (`FnMut`).
15+
16+
use std::mem;
17+
18+
struct DropMe<'a>(&'a mut i32);
19+
20+
#[unsafe_destructor]
21+
impl<'a> Drop for DropMe<'a> {
22+
fn drop(&mut self) {
23+
*self.0 += 1;
24+
}
25+
}
26+
27+
fn main() {
28+
let mut counter = 0;
29+
30+
{
31+
let drop_me = DropMe(&mut counter);
32+
let tick = || mem::drop(drop_me);
33+
tick();
34+
}
35+
36+
assert_eq!(counter, 1);
37+
}

0 commit comments

Comments
 (0)