/// Records the type of each closure.
closure_tys: ItemLocalMap<ty::PolyFnSig<'tcx>>,
- /// Records the kind of each closure and the span and name of the variable
- /// that caused the closure to be this kind.
- closure_kinds: ItemLocalMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>,
+ /// Records the reasons that we picked the kind of each closure;
+ /// not all closures are present in the map.
+ closure_kind_origins: ItemLocalMap<(Span, ast::Name)>,
generator_sigs: ItemLocalMap<Option<ty::GenSig<'tcx>>>,
generator_sigs: ItemLocalMap(),
generator_interiors: ItemLocalMap(),
closure_tys: ItemLocalMap(),
- closure_kinds: ItemLocalMap(),
+ closure_kind_origins: ItemLocalMap(),
liberated_fn_sigs: ItemLocalMap(),
fru_field_types: ItemLocalMap(),
cast_kinds: ItemLocalMap(),
}
}
- pub fn closure_kinds(&self) -> LocalTableInContext<(ty::ClosureKind,
- Option<(Span, ast::Name)>)> {
+ pub fn closure_kind_origins(&self) -> LocalTableInContext<(Span, ast::Name)> {
LocalTableInContext {
local_id_root: self.local_id_root,
- data: &self.closure_kinds
+ data: &self.closure_kind_origins
}
}
- pub fn closure_kinds_mut(&mut self)
- -> LocalTableInContextMut<(ty::ClosureKind, Option<(Span, ast::Name)>)> {
+ pub fn closure_kind_origins_mut(&mut self) -> LocalTableInContextMut<(Span, ast::Name)> {
LocalTableInContextMut {
local_id_root: self.local_id_root,
- data: &mut self.closure_kinds
+ data: &mut self.closure_kind_origins
}
}
ref pat_adjustments,
ref upvar_capture_map,
ref closure_tys,
- ref closure_kinds,
+ ref closure_kind_origins,
ref liberated_fn_sigs,
ref fru_field_types,
});
closure_tys.hash_stable(hcx, hasher);
- closure_kinds.hash_stable(hcx, hasher);
+ closure_kind_origins.hash_stable(hcx, hasher);
liberated_fn_sigs.hash_stable(hcx, hasher);
fru_field_types.hash_stable(hcx, hasher);
cast_kinds.hash_stable(hcx, hasher);
use middle::expr_use_visitor as euv;
use middle::mem_categorization as mc;
use middle::mem_categorization::Categorization;
+use rustc::hir::def_id::DefId;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::infer::UpvarRegion;
use syntax::ast;
use rustc::hir;
use rustc::hir::def_id::LocalDefId;
use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc::util::nodemap::FxHashMap;
-
-use std::collections::hash_map::Entry;
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn closure_analyze(&self, body: &'gcx hir::Body) {
span: Span,
body: &hir::Body,
capture_clause: hir::CaptureClause,
- gen: bool,
+ is_generator: bool,
) {
/*!
* Analysis starting point.
body.id()
);
- let infer_kind = if gen {
- false
- } else {
- match self.tables
- .borrow_mut()
- .closure_kinds_mut()
- .entry(closure_hir_id)
- {
- Entry::Occupied(_) => false,
- Entry::Vacant(entry) => {
- debug!("check_closure: adding closure {:?} as Fn", closure_node_id);
- entry.insert((ty::ClosureKind::Fn, None));
- true
- }
+ // Extract the type of the closure.
+ let (closure_def_id, closure_substs) = match self.node_ty(closure_hir_id).sty {
+ ty::TyClosure(def_id, substs) | ty::TyGenerator(def_id, substs, _) => (def_id, substs),
+ ref t => {
+ span_bug!(
+ span,
+ "type of closure expr {:?} is not a closure {:?}",
+ closure_node_id,
+ t
+ );
}
};
- let closure_def_id = self.tcx.hir.local_def_id(closure_node_id);
+ let infer_kind = if is_generator {
+ false
+ } else {
+ self.closure_kind(closure_def_id, closure_substs).is_none()
+ };
self.tcx.with_freevars(closure_node_id, |freevars| {
for freevar in freevars {
}
});
- // Extract the type of the closure.
- let (def_id, closure_substs) = match self.node_ty(closure_hir_id).sty {
- ty::TyClosure(def_id, substs) | ty::TyGenerator(def_id, substs, _) => (def_id, substs),
- ref t => {
- span_bug!(
- span,
- "type of closure expr {:?} is not a closure {:?}",
- closure_node_id,
- t
- );
- }
- };
-
let body_owner_def_id = self.tcx.hir.body_owner_def_id(body.id());
let region_scope_tree = &self.tcx.region_scope_tree(body_owner_def_id);
let mut delegate = InferBorrowKind {
fcx: self,
- adjust_closure_kinds: FxHashMap(),
+ closure_def_id: closure_def_id,
+ current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM,
+ current_origin: None,
adjust_upvar_captures: ty::UpvarCaptureMap::default(),
};
euv::ExprUseVisitor::with_infer(
&self.tables.borrow(),
).consume_body(body);
- // Write the adjusted values back into the main tables.
if infer_kind {
- let opt_adjusted = delegate.adjust_closure_kinds.remove(&closure_def_id.to_local());
- let closure_kind_ty = closure_substs.closure_kind_ty(def_id, self.tcx);
- if let Some((kind, origin)) = opt_adjusted {
+ // Unify the (as yet unbound) type variable in the closure
+ // substs with the kind we inferred.
+ let inferred_kind = delegate.current_closure_kind;
+ let closure_kind_ty = closure_substs.closure_kind_ty(closure_def_id, self.tcx);
+ self.demand_eqtype(span, inferred_kind.to_ty(self.tcx), closure_kind_ty);
+
+ // If we have an origin, store it.
+ if let Some(origin) = delegate.current_origin {
self.tables
.borrow_mut()
- .closure_kinds_mut()
- .insert(closure_hir_id, (kind, origin));
-
- self.demand_eqtype(span, kind.to_ty(self.tcx), closure_kind_ty);
- } else {
- // If there are only reads, or no upvars, then the
- // default of `Fn` will never *have* to be adjusted, so there will be
- // no entry in the map.
- self.demand_eqtype(span, ty::ClosureKind::Fn.to_ty(self.tcx), closure_kind_ty);
+ .closure_kind_origins_mut()
+ .insert(closure_hir_id, origin);
}
}
final_upvar_tys
);
for (upvar_ty, final_upvar_ty) in closure_substs
- .upvar_tys(def_id, self.tcx)
+ .upvar_tys(closure_def_id, self.tcx)
.zip(final_upvar_tys)
{
self.demand_eqtype(span, final_upvar_ty, upvar_ty);
// If we are also inferred the closure kind here,
// process any deferred resolutions.
- if infer_kind {
- let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id);
- for deferred_call_resolution in deferred_call_resolutions {
- deferred_call_resolution.resolve(self);
- }
+ let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id);
+ for deferred_call_resolution in deferred_call_resolutions {
+ deferred_call_resolution.resolve(self);
}
}
struct InferBorrowKind<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
- adjust_closure_kinds: FxHashMap<LocalDefId, (ty::ClosureKind, Option<(Span, ast::Name)>)>,
+
+ // The def-id of the closure whose kind and upvar accesses are being inferred.
+ closure_def_id: DefId,
+
+ // The kind that we have inferred that the current closure
+ // requires. Note that we *always* infer a minimal kind, even if
+ // we don't always *use* that in the final result (i.e., sometimes
+ // we've taken the closure kind from the expectations instead, and
+ // for generators we don't even implement the closure traits
+ // really).
+ current_closure_kind: ty::ClosureKind,
+
+ // If we modified `current_closure_kind`, this field contains a `Some()` with the
+ // variable access that caused us to do so.
+ current_origin: Option<(Span, ast::Name)>,
+
+ // For each upvar that we access, we track the minimal kind of
+ // access we need (ref, ref mut, move, etc).
adjust_upvar_captures: ty::UpvarCaptureMap<'tcx>,
}
var_name
);
- let closure_kind = self.adjust_closure_kinds
- .get(&closure_id)
- .cloned()
- .or_else(|| {
- let closure_id = self.fcx.tcx.hir.local_def_id_to_hir_id(closure_id);
- self.fcx
- .tables
- .borrow()
- .closure_kinds()
- .get(closure_id)
- .cloned()
- });
-
- if let Some((existing_kind, _)) = closure_kind {
- debug!(
- "adjust_closure_kind: closure_id={:?}, existing_kind={:?}, new_kind={:?}",
- closure_id,
- existing_kind,
- new_kind
- );
-
- match (existing_kind, new_kind) {
- (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
- (ty::ClosureKind::FnMut, ty::ClosureKind::Fn) |
- (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
- (ty::ClosureKind::FnOnce, _) => {
- // no change needed
- }
+ // Is this the closure whose kind is currently being inferred?
+ if closure_id.to_def_id() != self.closure_def_id {
+ debug!("adjust_closure_kind: not current closure");
+ return;
+ }
- (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
- self.adjust_closure_kinds
- .insert(closure_id, (new_kind, Some((upvar_span, var_name))));
- }
+ // closures start out as `Fn`.
+ let existing_kind = self.current_closure_kind;
+
+ debug!(
+ "adjust_closure_kind: closure_id={:?}, existing_kind={:?}, new_kind={:?}",
+ closure_id,
+ existing_kind,
+ new_kind
+ );
+
+ match (existing_kind, new_kind) {
+ (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
+ (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
+ self.current_closure_kind = new_kind;
+ self.current_origin = Some((upvar_span, var_name));
}
}
}