1
Fork 0

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?
This commit is contained in:
bors 2016-07-31 11:45:19 -07:00 committed by GitHub
commit 2b87f031e7
4 changed files with 123 additions and 53 deletions

View file

@ -271,10 +271,19 @@ enum PassArgs {
impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
pub fn new(delegate: &'a mut (Delegate<'tcx>+'a), pub fn new(delegate: &'a mut (Delegate<'tcx>+'a),
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self infcx: &'a InferCtxt<'a, 'gcx, 'tcx>)
-> Self
{
ExprUseVisitor::with_options(delegate, infcx, mc::MemCategorizationOptions::default())
}
pub fn with_options(delegate: &'a mut (Delegate<'tcx>+'a),
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
options: mc::MemCategorizationOptions)
-> Self
{ {
ExprUseVisitor { ExprUseVisitor {
mc: mc::MemCategorizationContext::new(infcx), mc: mc::MemCategorizationContext::with_options(infcx, options),
delegate: delegate delegate: delegate
} }
} }

View file

@ -259,6 +259,18 @@ impl ast_node for hir::Pat {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct MemCategorizationContext<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { pub struct MemCategorizationContext<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
pub infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, pub infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
options: MemCategorizationOptions,
}
#[derive(Copy, Clone, Default)]
pub struct MemCategorizationOptions {
// If true, then when analyzing a closure upvar, if the closure
// has a missing kind, we treat it like a Fn closure. When false,
// we ICE if the closure has a missing kind. Should be false
// except during closure kind inference. It is used by the
// mem-categorization code to be able to have stricter assertions
// (which are always true except during upvar inference).
pub during_closure_kind_inference: bool,
} }
pub type McResult<T> = Result<T, ()>; pub type McResult<T> = Result<T, ()>;
@ -362,7 +374,16 @@ impl MutabilityCategory {
impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>)
-> MemCategorizationContext<'a, 'gcx, 'tcx> { -> MemCategorizationContext<'a, 'gcx, 'tcx> {
MemCategorizationContext { infcx: infcx } MemCategorizationContext::with_options(infcx, MemCategorizationOptions::default())
}
pub fn with_options(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
options: MemCategorizationOptions)
-> MemCategorizationContext<'a, 'gcx, 'tcx> {
MemCategorizationContext {
infcx: infcx,
options: options,
}
} }
fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
@ -584,10 +605,20 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
self.cat_upvar(id, span, var_id, fn_node_id, kind) self.cat_upvar(id, span, var_id, fn_node_id, kind)
} }
None => { None => {
span_bug!( if !self.options.during_closure_kind_inference {
span, span_bug!(
"No closure kind for {:?}", span,
closure_id); "No closure kind for {:?}",
closure_id);
}
// during closure kind inference, we
// don't know the closure kind yet, but
// it's ok because we detect that we are
// accessing an upvar and handle that
// case specially anyhow. Use Fn
// arbitrarily.
self.cat_upvar(id, span, var_id, fn_node_id, ty::ClosureKind::Fn)
} }
} }
} }

View file

@ -47,11 +47,11 @@ use middle::mem_categorization as mc;
use middle::mem_categorization::Categorization; use middle::mem_categorization::Categorization;
use rustc::ty::{self, Ty}; use rustc::ty::{self, Ty};
use rustc::infer::UpvarRegion; use rustc::infer::UpvarRegion;
use std::collections::HashSet;
use syntax::ast; use syntax::ast;
use syntax_pos::Span; use syntax_pos::Span;
use rustc::hir; use rustc::hir;
use rustc::hir::intravisit::{self, Visitor}; use rustc::hir::intravisit::{self, Visitor};
use rustc::util::nodemap::NodeMap;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// PUBLIC ENTRY POINTS // PUBLIC ENTRY POINTS
@ -60,9 +60,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn closure_analyze_fn(&self, body: &hir::Block) { pub fn closure_analyze_fn(&self, body: &hir::Block) {
let mut seed = SeedBorrowKind::new(self); let mut seed = SeedBorrowKind::new(self);
seed.visit_block(body); seed.visit_block(body);
let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
let mut adjust = AdjustBorrowKind::new(self, &closures_with_inferred_kinds); let mut adjust = AdjustBorrowKind::new(self, seed.temp_closure_kinds);
adjust.visit_block(body); adjust.visit_block(body);
// it's our job to process these. // it's our job to process these.
@ -72,9 +71,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn closure_analyze_const(&self, body: &hir::Expr) { pub fn closure_analyze_const(&self, body: &hir::Expr) {
let mut seed = SeedBorrowKind::new(self); let mut seed = SeedBorrowKind::new(self);
seed.visit_expr(body); seed.visit_expr(body);
let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
let mut adjust = AdjustBorrowKind::new(self, &closures_with_inferred_kinds); let mut adjust = AdjustBorrowKind::new(self, seed.temp_closure_kinds);
adjust.visit_expr(body); adjust.visit_expr(body);
// it's our job to process these. // it's our job to process these.
@ -87,7 +85,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
struct SeedBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { struct SeedBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
closures_with_inferred_kinds: HashSet<ast::NodeId>, temp_closure_kinds: NodeMap<ty::ClosureKind>,
} }
impl<'a, 'gcx, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'gcx, 'tcx> { 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> {
impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> {
fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>) -> SeedBorrowKind<'a, 'gcx, 'tcx> { fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>) -> SeedBorrowKind<'a, 'gcx, 'tcx> {
SeedBorrowKind { fcx: fcx, closures_with_inferred_kinds: HashSet::new() } SeedBorrowKind { fcx: fcx, temp_closure_kinds: NodeMap() }
} }
fn check_closure(&mut self, fn check_closure(&mut self,
@ -116,11 +114,8 @@ impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> {
{ {
let closure_def_id = self.fcx.tcx.map.local_def_id(expr.id); let closure_def_id = self.fcx.tcx.map.local_def_id(expr.id);
if !self.fcx.tables.borrow().closure_kinds.contains_key(&closure_def_id) { if !self.fcx.tables.borrow().closure_kinds.contains_key(&closure_def_id) {
self.closures_with_inferred_kinds.insert(expr.id); self.temp_closure_kinds.insert(expr.id, ty::ClosureKind::Fn);
self.fcx.tables.borrow_mut().closure_kinds debug!("check_closure: adding closure {:?} as Fn", expr.id);
.insert(closure_def_id, ty::ClosureKind::Fn);
debug!("check_closure: adding closure_id={:?} to closures_with_inferred_kinds",
closure_def_id);
} }
self.fcx.tcx.with_freevars(expr.id, |freevars| { self.fcx.tcx.with_freevars(expr.id, |freevars| {
@ -154,14 +149,14 @@ impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> {
struct AdjustBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { struct AdjustBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
closures_with_inferred_kinds: &'a HashSet<ast::NodeId>, temp_closure_kinds: NodeMap<ty::ClosureKind>,
} }
impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
closures_with_inferred_kinds: &'a HashSet<ast::NodeId>) temp_closure_kinds: NodeMap<ty::ClosureKind>)
-> AdjustBorrowKind<'a, 'gcx, 'tcx> { -> AdjustBorrowKind<'a, 'gcx, 'tcx> {
AdjustBorrowKind { fcx: fcx, closures_with_inferred_kinds: closures_with_inferred_kinds } AdjustBorrowKind { fcx: fcx, temp_closure_kinds: temp_closure_kinds }
} }
fn analyze_closure(&mut self, fn analyze_closure(&mut self,
@ -176,7 +171,12 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
debug!("analyze_closure(id={:?}, body.id={:?})", id, body.id); debug!("analyze_closure(id={:?}, body.id={:?})", id, body.id);
{ {
let mut euv = euv::ExprUseVisitor::new(self, self.fcx); let mut euv =
euv::ExprUseVisitor::with_options(self,
self.fcx,
mc::MemCategorizationOptions {
during_closure_kind_inference: true
});
euv.walk_fn(decl, body); euv.walk_fn(decl, body);
} }
@ -211,10 +211,14 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
self.fcx.demand_eqtype(span, final_upvar_ty, upvar_ty); self.fcx.demand_eqtype(span, final_upvar_ty, upvar_ty);
} }
// Now we must process and remove any deferred resolutions, // If we are also inferred the closure kind here, update the
// since we have a concrete closure kind. // main table and process any deferred resolutions.
let closure_def_id = self.fcx.tcx.map.local_def_id(id); let closure_def_id = self.fcx.tcx.map.local_def_id(id);
if self.closures_with_inferred_kinds.contains(&id) { if let Some(&kind) = self.temp_closure_kinds.get(&id) {
self.fcx.tables.borrow_mut().closure_kinds
.insert(closure_def_id, kind);
debug!("closure_kind({:?}) = {:?}", closure_def_id, kind);
let mut deferred_call_resolutions = let mut deferred_call_resolutions =
self.fcx.remove_deferred_call_resolutions(closure_def_id); self.fcx.remove_deferred_call_resolutions(closure_def_id);
for deferred_call_resolution in &mut deferred_call_resolutions { for deferred_call_resolution in &mut deferred_call_resolutions {
@ -259,7 +263,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
}) })
} }
fn adjust_upvar_borrow_kind_for_consume(&self, fn adjust_upvar_borrow_kind_for_consume(&mut self,
cmt: mc::cmt<'tcx>, cmt: mc::cmt<'tcx>,
mode: euv::ConsumeMode) mode: euv::ConsumeMode)
{ {
@ -350,7 +354,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
} }
} }
fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) { fn adjust_upvar_borrow_kind_for_unique(&mut self, cmt: mc::cmt<'tcx>) {
debug!("adjust_upvar_borrow_kind_for_unique(cmt={:?})", debug!("adjust_upvar_borrow_kind_for_unique(cmt={:?})",
cmt); cmt);
@ -381,7 +385,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
} }
} }
fn try_adjust_upvar_deref(&self, fn try_adjust_upvar_deref(&mut self,
note: &mc::Note, note: &mc::Note,
borrow_kind: ty::BorrowKind) borrow_kind: ty::BorrowKind)
-> bool -> bool
@ -430,7 +434,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
/// moving from left to right as needed (but never right to left). /// moving from left to right as needed (but never right to left).
/// Here the argument `mutbl` is the borrow_kind that is required by /// Here the argument `mutbl` is the borrow_kind that is required by
/// some particular use. /// some particular use.
fn adjust_upvar_borrow_kind(&self, fn adjust_upvar_borrow_kind(&mut self,
upvar_id: ty::UpvarId, upvar_id: ty::UpvarId,
upvar_capture: &mut ty::UpvarCapture, upvar_capture: &mut ty::UpvarCapture,
kind: ty::BorrowKind) { kind: ty::BorrowKind) {
@ -460,36 +464,30 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
} }
} }
fn adjust_closure_kind(&self, fn adjust_closure_kind(&mut self,
closure_id: ast::NodeId, closure_id: ast::NodeId,
new_kind: ty::ClosureKind) { new_kind: ty::ClosureKind) {
debug!("adjust_closure_kind(closure_id={}, new_kind={:?})", debug!("adjust_closure_kind(closure_id={}, new_kind={:?})",
closure_id, new_kind); closure_id, new_kind);
if !self.closures_with_inferred_kinds.contains(&closure_id) { if let Some(&existing_kind) = self.temp_closure_kinds.get(&closure_id) {
return; debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}",
} closure_id, existing_kind, new_kind);
let closure_def_id = self.fcx.tcx.map.local_def_id(closure_id); match (existing_kind, new_kind) {
let closure_kinds = &mut self.fcx.tables.borrow_mut().closure_kinds; (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
let existing_kind = *closure_kinds.get(&closure_def_id).unwrap(); (ty::ClosureKind::FnMut, ty::ClosureKind::Fn) |
(ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
(ty::ClosureKind::FnOnce, _) => {
// no change needed
}
debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}", (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) |
closure_id, existing_kind, new_kind); (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
(ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
match (existing_kind, new_kind) { // new kind is stronger than the old kind
(ty::ClosureKind::Fn, ty::ClosureKind::Fn) | self.temp_closure_kinds.insert(closure_id, new_kind);
(ty::ClosureKind::FnMut, ty::ClosureKind::Fn) | }
(ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
(ty::ClosureKind::FnOnce, _) => {
// no change needed
}
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut) |
(ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
(ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
// new kind is stronger than the old kind
closure_kinds.insert(closure_def_id, new_kind);
} }
} }
} }

View file

@ -0,0 +1,32 @@
// Copyright 2016 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.
// This is a regression test for a problem encountered around upvar
// inference and trait caching: in particular, we were entering a
// temporary closure kind during inference, and then caching results
// based on that temporary kind, which led to no error being reported
// in this particular test.
fn main() {
let inc = || {};
inc();
fn apply<F>(f: F) where F: Fn() {
f()
}
let mut farewell = "goodbye".to_owned();
let diary = || { //~ ERROR E0525
farewell.push_str("!!!");
println!("Then I screamed {}.", farewell);
};
apply(diary);
}