Skip to content

Commit 2b87f03

Browse files
authored
Auto merge of #34986 - nikomatsakis:issue-34349, r=arielb1
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. r? @arielb1 -- what do you think?
2 parents 379ac50 + 63eb4d9 commit 2b87f03

File tree

4 files changed

+123
-53
lines changed

4 files changed

+123
-53
lines changed

src/librustc/middle/expr_use_visitor.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,19 @@ enum PassArgs {
271271

272272
impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
273273
pub fn new(delegate: &'a mut (Delegate<'tcx>+'a),
274-
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self
274+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>)
275+
-> Self
276+
{
277+
ExprUseVisitor::with_options(delegate, infcx, mc::MemCategorizationOptions::default())
278+
}
279+
280+
pub fn with_options(delegate: &'a mut (Delegate<'tcx>+'a),
281+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
282+
options: mc::MemCategorizationOptions)
283+
-> Self
275284
{
276285
ExprUseVisitor {
277-
mc: mc::MemCategorizationContext::new(infcx),
286+
mc: mc::MemCategorizationContext::with_options(infcx, options),
278287
delegate: delegate
279288
}
280289
}

src/librustc/middle/mem_categorization.rs

+36-5
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,18 @@ impl ast_node for hir::Pat {
259259
#[derive(Copy, Clone)]
260260
pub struct MemCategorizationContext<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
261261
pub infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
262+
options: MemCategorizationOptions,
263+
}
264+
265+
#[derive(Copy, Clone, Default)]
266+
pub struct MemCategorizationOptions {
267+
// If true, then when analyzing a closure upvar, if the closure
268+
// has a missing kind, we treat it like a Fn closure. When false,
269+
// we ICE if the closure has a missing kind. Should be false
270+
// except during closure kind inference. It is used by the
271+
// mem-categorization code to be able to have stricter assertions
272+
// (which are always true except during upvar inference).
273+
pub during_closure_kind_inference: bool,
262274
}
263275

264276
pub type McResult<T> = Result<T, ()>;
@@ -362,7 +374,16 @@ impl MutabilityCategory {
362374
impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
363375
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>)
364376
-> MemCategorizationContext<'a, 'gcx, 'tcx> {
365-
MemCategorizationContext { infcx: infcx }
377+
MemCategorizationContext::with_options(infcx, MemCategorizationOptions::default())
378+
}
379+
380+
pub fn with_options(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
381+
options: MemCategorizationOptions)
382+
-> MemCategorizationContext<'a, 'gcx, 'tcx> {
383+
MemCategorizationContext {
384+
infcx: infcx,
385+
options: options,
386+
}
366387
}
367388

368389
fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
@@ -584,10 +605,20 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
584605
self.cat_upvar(id, span, var_id, fn_node_id, kind)
585606
}
586607
None => {
587-
span_bug!(
588-
span,
589-
"No closure kind for {:?}",
590-
closure_id);
608+
if !self.options.during_closure_kind_inference {
609+
span_bug!(
610+
span,
611+
"No closure kind for {:?}",
612+
closure_id);
613+
}
614+
615+
// during closure kind inference, we
616+
// don't know the closure kind yet, but
617+
// it's ok because we detect that we are
618+
// accessing an upvar and handle that
619+
// case specially anyhow. Use Fn
620+
// arbitrarily.
621+
self.cat_upvar(id, span, var_id, fn_node_id, ty::ClosureKind::Fn)
591622
}
592623
}
593624
}

src/librustc_typeck/check/upvar.rs

+44-46
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,7 +171,12 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
176171
debug!("analyze_closure(id={:?}, body.id={:?})", id, body.id);
177172

178173
{
179-
let mut euv = euv::ExprUseVisitor::new(self, self.fcx);
174+
let mut euv =
175+
euv::ExprUseVisitor::with_options(self,
176+
self.fcx,
177+
mc::MemCategorizationOptions {
178+
during_closure_kind_inference: true
179+
});
180180
euv.walk_fn(decl, body);
181181
}
182182

@@ -211,10 +211,14 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
211211
self.fcx.demand_eqtype(span, final_upvar_ty, upvar_ty);
212212
}
213213

214-
// Now we must process and remove any deferred resolutions,
215-
// since we have a concrete closure kind.
214+
// If we are also inferred the closure kind here, update the
215+
// main table and process any deferred resolutions.
216216
let closure_def_id = self.fcx.tcx.map.local_def_id(id);
217-
if self.closures_with_inferred_kinds.contains(&id) {
217+
if let Some(&kind) = self.temp_closure_kinds.get(&id) {
218+
self.fcx.tables.borrow_mut().closure_kinds
219+
.insert(closure_def_id, kind);
220+
debug!("closure_kind({:?}) = {:?}", closure_def_id, kind);
221+
218222
let mut deferred_call_resolutions =
219223
self.fcx.remove_deferred_call_resolutions(closure_def_id);
220224
for deferred_call_resolution in &mut deferred_call_resolutions {
@@ -259,7 +263,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
259263
})
260264
}
261265

262-
fn adjust_upvar_borrow_kind_for_consume(&self,
266+
fn adjust_upvar_borrow_kind_for_consume(&mut self,
263267
cmt: mc::cmt<'tcx>,
264268
mode: euv::ConsumeMode)
265269
{
@@ -350,7 +354,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
350354
}
351355
}
352356

353-
fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) {
357+
fn adjust_upvar_borrow_kind_for_unique(&mut self, cmt: mc::cmt<'tcx>) {
354358
debug!("adjust_upvar_borrow_kind_for_unique(cmt={:?})",
355359
cmt);
356360

@@ -381,7 +385,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
381385
}
382386
}
383387

384-
fn try_adjust_upvar_deref(&self,
388+
fn try_adjust_upvar_deref(&mut self,
385389
note: &mc::Note,
386390
borrow_kind: ty::BorrowKind)
387391
-> bool
@@ -430,7 +434,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
430434
/// moving from left to right as needed (but never right to left).
431435
/// Here the argument `mutbl` is the borrow_kind that is required by
432436
/// some particular use.
433-
fn adjust_upvar_borrow_kind(&self,
437+
fn adjust_upvar_borrow_kind(&mut self,
434438
upvar_id: ty::UpvarId,
435439
upvar_capture: &mut ty::UpvarCapture,
436440
kind: ty::BorrowKind) {
@@ -460,36 +464,30 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
460464
}
461465
}
462466

463-
fn adjust_closure_kind(&self,
467+
fn adjust_closure_kind(&mut self,
464468
closure_id: ast::NodeId,
465469
new_kind: ty::ClosureKind) {
466470
debug!("adjust_closure_kind(closure_id={}, new_kind={:?})",
467471
closure_id, new_kind);
468472

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();
473+
if let Some(&existing_kind) = self.temp_closure_kinds.get(&closure_id) {
474+
debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}",
475+
closure_id, existing_kind, new_kind);
476476

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-
}
477+
match (existing_kind, new_kind) {
478+
(ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
479+
(ty::ClosureKind::FnMut, ty::ClosureKind::Fn) |
480+
(ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
481+
(ty::ClosureKind::FnOnce, _) => {
482+
// no change needed
483+
}
487484

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);
485+
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut) |
486+
(ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
487+
(ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
488+
// new kind is stronger than the old kind
489+
self.temp_closure_kinds.insert(closure_id, new_kind);
490+
}
493491
}
494492
}
495493
}

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)