Skip to content

Commit 6298f09

Browse files
nikomatsakisalexcrichton
authored andcommitted
Avoid writing a temporary closure kind
We used to write a temporary closure kind into the inference table, but this could lead to obligations being incorrectled resolved before inference had completed. This result could then be cached, leading to further trouble. This patch avoids writing any closure kind until the computation is complete. Fixes #34349.
1 parent f313f0b commit 6298f09

File tree

4 files changed

+105
-50
lines changed

4 files changed

+105
-50
lines changed

src/librustc/infer/mod.rs

+16
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,12 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
175175
// any obligations set during the current snapshot. In that case, the
176176
// snapshot can't be rolled back.
177177
pub obligations_in_snapshot: Cell<bool>,
178+
179+
// This is false except during closure kind inference. It is used
180+
// by the mem-categorization code to be able to have stricter
181+
// assertions (which are always true except during upvar
182+
// inference).
183+
during_closure_kind_inference: Cell<bool>,
178184
}
179185

180186
/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
@@ -491,6 +497,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
491497
tainted_by_errors_flag: Cell::new(false),
492498
err_count_on_creation: self.sess.err_count(),
493499
obligations_in_snapshot: Cell::new(false),
500+
during_closure_kind_inference: Cell::new(false),
494501
}
495502
}
496503
}
@@ -532,6 +539,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
532539
tainted_by_errors_flag: Cell::new(false),
533540
err_count_on_creation: tcx.sess.err_count(),
534541
obligations_in_snapshot: Cell::new(false),
542+
during_closure_kind_inference: Cell::new(false),
535543
}))
536544
}
537545
}
@@ -1294,6 +1302,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
12941302
.map(|method| resolve_ty(method.ty)))
12951303
}
12961304

1305+
pub fn set_during_closure_kind_inference(&self, value: bool) {
1306+
self.during_closure_kind_inference.set(value);
1307+
}
1308+
1309+
pub fn during_closure_kind_inference(&self) -> bool {
1310+
self.during_closure_kind_inference.get()
1311+
}
1312+
12971313
/// True if errors have been reported since this infcx was
12981314
/// created. This is sometimes used as a heuristic to skip
12991315
/// reporting errors that often occur as a result of earlier

src/librustc/middle/mem_categorization.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,9 @@ impl MutabilityCategory {
362362
impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
363363
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>)
364364
-> MemCategorizationContext<'a, 'gcx, 'tcx> {
365-
MemCategorizationContext { infcx: infcx }
365+
MemCategorizationContext {
366+
infcx: infcx,
367+
}
366368
}
367369

368370
fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
@@ -584,10 +586,20 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
584586
self.cat_upvar(id, span, var_id, fn_node_id, kind)
585587
}
586588
None => {
587-
span_bug!(
588-
span,
589-
"No closure kind for {:?}",
590-
closure_id);
589+
if !self.infcx.during_closure_kind_inference() {
590+
span_bug!(
591+
span,
592+
"No closure kind for {:?}",
593+
closure_id);
594+
}
595+
596+
// during closure kind inference, we
597+
// don't know the closure kind yet, but
598+
// it's ok because we detect that we are
599+
// accessing an upvar and handle that
600+
// case specially anyhow. Use Fn
601+
// arbitrarily.
602+
self.cat_upvar(id, span, var_id, fn_node_id, ty::ClosureKind::Fn)
591603
}
592604
}
593605
}

src/librustc_typeck/check/upvar.rs

+40-45
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ use middle::mem_categorization as mc;
4747
use middle::mem_categorization::Categorization;
4848
use rustc::ty::{self, Ty};
4949
use rustc::infer::UpvarRegion;
50-
use std::collections::HashSet;
5150
use syntax::ast;
5251
use syntax_pos::Span;
5352
use rustc::hir;
5453
use rustc::hir::intravisit::{self, Visitor};
54+
use rustc::util::nodemap::NodeMap;
5555

5656
///////////////////////////////////////////////////////////////////////////
5757
// PUBLIC ENTRY POINTS
@@ -60,9 +60,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
6060
pub fn closure_analyze_fn(&self, body: &hir::Block) {
6161
let mut seed = SeedBorrowKind::new(self);
6262
seed.visit_block(body);
63-
let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
6463

65-
let mut adjust = AdjustBorrowKind::new(self, &closures_with_inferred_kinds);
64+
let mut adjust = AdjustBorrowKind::new(self, seed.temp_closure_kinds);
6665
adjust.visit_block(body);
6766

6867
// it's our job to process these.
@@ -72,9 +71,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
7271
pub fn closure_analyze_const(&self, body: &hir::Expr) {
7372
let mut seed = SeedBorrowKind::new(self);
7473
seed.visit_expr(body);
75-
let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
7674

77-
let mut adjust = AdjustBorrowKind::new(self, &closures_with_inferred_kinds);
75+
let mut adjust = AdjustBorrowKind::new(self, seed.temp_closure_kinds);
7876
adjust.visit_expr(body);
7977

8078
// it's our job to process these.
@@ -87,7 +85,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
8785

8886
struct SeedBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
8987
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
90-
closures_with_inferred_kinds: HashSet<ast::NodeId>,
88+
temp_closure_kinds: NodeMap<ty::ClosureKind>,
9189
}
9290

9391
impl<'a, 'gcx, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'gcx, 'tcx> {
@@ -106,7 +104,7 @@ impl<'a, 'gcx, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'gcx, 'tcx> {
106104

107105
impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> {
108106
fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>) -> SeedBorrowKind<'a, 'gcx, 'tcx> {
109-
SeedBorrowKind { fcx: fcx, closures_with_inferred_kinds: HashSet::new() }
107+
SeedBorrowKind { fcx: fcx, temp_closure_kinds: NodeMap() }
110108
}
111109

112110
fn check_closure(&mut self,
@@ -116,11 +114,8 @@ impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> {
116114
{
117115
let closure_def_id = self.fcx.tcx.map.local_def_id(expr.id);
118116
if !self.fcx.tables.borrow().closure_kinds.contains_key(&closure_def_id) {
119-
self.closures_with_inferred_kinds.insert(expr.id);
120-
self.fcx.tables.borrow_mut().closure_kinds
121-
.insert(closure_def_id, ty::ClosureKind::Fn);
122-
debug!("check_closure: adding closure_id={:?} to closures_with_inferred_kinds",
123-
closure_def_id);
117+
self.temp_closure_kinds.insert(expr.id, ty::ClosureKind::Fn);
118+
debug!("check_closure: adding closure {:?} as Fn", expr.id);
124119
}
125120

126121
self.fcx.tcx.with_freevars(expr.id, |freevars| {
@@ -154,14 +149,14 @@ impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> {
154149

155150
struct AdjustBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
156151
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
157-
closures_with_inferred_kinds: &'a HashSet<ast::NodeId>,
152+
temp_closure_kinds: NodeMap<ty::ClosureKind>,
158153
}
159154

160155
impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
161156
fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
162-
closures_with_inferred_kinds: &'a HashSet<ast::NodeId>)
157+
temp_closure_kinds: NodeMap<ty::ClosureKind>)
163158
-> AdjustBorrowKind<'a, 'gcx, 'tcx> {
164-
AdjustBorrowKind { fcx: fcx, closures_with_inferred_kinds: closures_with_inferred_kinds }
159+
AdjustBorrowKind { fcx: fcx, temp_closure_kinds: temp_closure_kinds }
165160
}
166161

167162
fn analyze_closure(&mut self,
@@ -176,8 +171,10 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
176171
debug!("analyze_closure(id={:?}, body.id={:?})", id, body.id);
177172

178173
{
174+
self.fcx.set_during_closure_kind_inference(true);
179175
let mut euv = euv::ExprUseVisitor::new(self, self.fcx);
180176
euv.walk_fn(decl, body);
177+
self.fcx.set_during_closure_kind_inference(false);
181178
}
182179

183180
// Now that we've analyzed the closure, we know how each
@@ -211,10 +208,14 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
211208
self.fcx.demand_eqtype(span, final_upvar_ty, upvar_ty);
212209
}
213210

214-
// Now we must process and remove any deferred resolutions,
215-
// since we have a concrete closure kind.
211+
// If we are also inferred the closure kind here, update the
212+
// main table and process any deferred resolutions.
216213
let closure_def_id = self.fcx.tcx.map.local_def_id(id);
217-
if self.closures_with_inferred_kinds.contains(&id) {
214+
if let Some(&kind) = self.temp_closure_kinds.get(&id) {
215+
self.fcx.tables.borrow_mut().closure_kinds
216+
.insert(closure_def_id, kind);
217+
debug!("closure_kind({:?}) = {:?}", closure_def_id, kind);
218+
218219
let mut deferred_call_resolutions =
219220
self.fcx.remove_deferred_call_resolutions(closure_def_id);
220221
for deferred_call_resolution in &mut deferred_call_resolutions {
@@ -259,7 +260,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
259260
})
260261
}
261262

262-
fn adjust_upvar_borrow_kind_for_consume(&self,
263+
fn adjust_upvar_borrow_kind_for_consume(&mut self,
263264
cmt: mc::cmt<'tcx>,
264265
mode: euv::ConsumeMode)
265266
{
@@ -350,7 +351,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
350351
}
351352
}
352353

353-
fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) {
354+
fn adjust_upvar_borrow_kind_for_unique(&mut self, cmt: mc::cmt<'tcx>) {
354355
debug!("adjust_upvar_borrow_kind_for_unique(cmt={:?})",
355356
cmt);
356357

@@ -381,7 +382,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
381382
}
382383
}
383384

384-
fn try_adjust_upvar_deref(&self,
385+
fn try_adjust_upvar_deref(&mut self,
385386
note: &mc::Note,
386387
borrow_kind: ty::BorrowKind)
387388
-> bool
@@ -430,7 +431,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
430431
/// moving from left to right as needed (but never right to left).
431432
/// Here the argument `mutbl` is the borrow_kind that is required by
432433
/// some particular use.
433-
fn adjust_upvar_borrow_kind(&self,
434+
fn adjust_upvar_borrow_kind(&mut self,
434435
upvar_id: ty::UpvarId,
435436
upvar_capture: &mut ty::UpvarCapture,
436437
kind: ty::BorrowKind) {
@@ -460,36 +461,30 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
460461
}
461462
}
462463

463-
fn adjust_closure_kind(&self,
464+
fn adjust_closure_kind(&mut self,
464465
closure_id: ast::NodeId,
465466
new_kind: ty::ClosureKind) {
466467
debug!("adjust_closure_kind(closure_id={}, new_kind={:?})",
467468
closure_id, new_kind);
468469

469-
if !self.closures_with_inferred_kinds.contains(&closure_id) {
470-
return;
471-
}
472-
473-
let closure_def_id = self.fcx.tcx.map.local_def_id(closure_id);
474-
let closure_kinds = &mut self.fcx.tables.borrow_mut().closure_kinds;
475-
let existing_kind = *closure_kinds.get(&closure_def_id).unwrap();
470+
if let Some(&existing_kind) = self.temp_closure_kinds.get(&closure_id) {
471+
debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}",
472+
closure_id, existing_kind, new_kind);
476473

477-
debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}",
478-
closure_id, existing_kind, new_kind);
479-
480-
match (existing_kind, new_kind) {
481-
(ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
482-
(ty::ClosureKind::FnMut, ty::ClosureKind::Fn) |
483-
(ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
484-
(ty::ClosureKind::FnOnce, _) => {
485-
// no change needed
486-
}
474+
match (existing_kind, new_kind) {
475+
(ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
476+
(ty::ClosureKind::FnMut, ty::ClosureKind::Fn) |
477+
(ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
478+
(ty::ClosureKind::FnOnce, _) => {
479+
// no change needed
480+
}
487481

488-
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut) |
489-
(ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
490-
(ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
491-
// new kind is stronger than the old kind
492-
closure_kinds.insert(closure_def_id, new_kind);
482+
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut) |
483+
(ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
484+
(ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
485+
// new kind is stronger than the old kind
486+
self.temp_closure_kinds.insert(closure_id, new_kind);
487+
}
493488
}
494489
}
495490
}

src/test/compile-fail/issue-34349.rs

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2016 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+
// This is a regression test for a problem encountered around upvar
12+
// inference and trait caching: in particular, we were entering a
13+
// temporary closure kind during inference, and then caching results
14+
// based on that temporary kind, which led to no error being reported
15+
// in this particular test.
16+
17+
fn main() {
18+
let inc = || {};
19+
inc();
20+
21+
fn apply<F>(f: F) where F: Fn() {
22+
f()
23+
}
24+
25+
let mut farewell = "goodbye".to_owned();
26+
let diary = || { //~ ERROR E0525
27+
farewell.push_str("!!!");
28+
println!("Then I screamed {}.", farewell);
29+
};
30+
31+
apply(diary);
32+
}

0 commit comments

Comments
 (0)