2019-05-28 23:31:01 +03:00
|
|
|
//! Upvar (closure capture) collection from cross-body HIR uses of `Res::Local`s.
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir as hir;
|
|
|
|
use rustc_hir::def::Res;
|
2020-01-07 18:12:06 +01:00
|
|
|
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir::{self, HirId};
|
2020-03-29 17:19:48 +02:00
|
|
|
use rustc_middle::ty::query::Providers;
|
|
|
|
use rustc_middle::ty::TyCtxt;
|
2019-12-31 20:15:40 +03:00
|
|
|
use rustc_span::Span;
|
2019-05-28 23:31:01 +03:00
|
|
|
|
2020-07-05 23:00:14 +03:00
|
|
|
pub fn provide(providers: &mut Providers) {
|
2020-05-23 19:29:49 -04:00
|
|
|
providers.upvars_mentioned = |tcx, def_id| {
|
2019-05-28 23:31:01 +03:00
|
|
|
if !tcx.is_closure(def_id) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2020-08-12 12:22:56 +02:00
|
|
|
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
|
2019-06-14 18:58:55 +02:00
|
|
|
let body = tcx.hir().body(tcx.hir().maybe_body_owned_by(hir_id)?);
|
2019-05-28 23:31:01 +03:00
|
|
|
|
|
|
|
let mut local_collector = LocalCollector::default();
|
|
|
|
local_collector.visit_body(body);
|
|
|
|
|
|
|
|
let mut capture_collector = CaptureCollector {
|
|
|
|
tcx,
|
|
|
|
locals: &local_collector.locals,
|
|
|
|
upvars: FxIndexMap::default(),
|
|
|
|
};
|
|
|
|
capture_collector.visit_body(body);
|
|
|
|
|
|
|
|
if !capture_collector.upvars.is_empty() {
|
|
|
|
Some(tcx.arena.alloc(capture_collector.upvars))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
struct LocalCollector {
|
|
|
|
// FIXME(eddyb) perhaps use `ItemLocalId` instead?
|
|
|
|
locals: FxHashSet<HirId>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Visitor<'tcx> for LocalCollector {
|
2020-03-11 12:05:32 +01:00
|
|
|
type Map = intravisit::ErasedMap<'tcx>;
|
2020-01-07 17:25:33 +01:00
|
|
|
|
2020-02-09 15:32:00 +01:00
|
|
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
2019-05-28 23:31:01 +03:00
|
|
|
NestedVisitorMap::None
|
|
|
|
}
|
|
|
|
|
2019-11-29 13:43:03 +01:00
|
|
|
fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
|
2019-09-26 16:18:31 +01:00
|
|
|
if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind {
|
2019-05-28 23:31:01 +03:00
|
|
|
self.locals.insert(hir_id);
|
|
|
|
}
|
|
|
|
intravisit::walk_pat(self, pat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct CaptureCollector<'a, 'tcx> {
|
2019-06-14 00:48:52 +03:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-05-28 23:31:01 +03:00
|
|
|
locals: &'a FxHashSet<HirId>,
|
|
|
|
upvars: FxIndexMap<HirId, hir::Upvar>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CaptureCollector<'_, '_> {
|
|
|
|
fn visit_local_use(&mut self, var_id: HirId, span: Span) {
|
|
|
|
if !self.locals.contains(&var_id) {
|
|
|
|
self.upvars.entry(var_id).or_insert(hir::Upvar { span });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Visitor<'tcx> for CaptureCollector<'a, 'tcx> {
|
2020-03-11 12:05:32 +01:00
|
|
|
type Map = intravisit::ErasedMap<'tcx>;
|
2020-01-07 17:25:33 +01:00
|
|
|
|
2020-02-09 15:32:00 +01:00
|
|
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
2019-05-28 23:31:01 +03:00
|
|
|
NestedVisitorMap::None
|
|
|
|
}
|
|
|
|
|
2019-11-30 17:46:46 +01:00
|
|
|
fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
|
2019-05-28 23:31:01 +03:00
|
|
|
if let Res::Local(var_id) = path.res {
|
|
|
|
self.visit_local_use(var_id, path.span);
|
|
|
|
}
|
|
|
|
|
|
|
|
intravisit::walk_path(self, path);
|
|
|
|
}
|
|
|
|
|
2019-11-29 13:43:03 +01:00
|
|
|
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
|
2019-09-26 14:39:48 +01:00
|
|
|
if let hir::ExprKind::Closure(..) = expr.kind {
|
2019-06-27 11:28:14 +02:00
|
|
|
let closure_def_id = self.tcx.hir().local_def_id(expr.hir_id);
|
2020-05-23 19:29:49 -04:00
|
|
|
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
|
2019-05-28 23:31:01 +03:00
|
|
|
// Every capture of a closure expression is a local in scope,
|
|
|
|
// that is moved/copied/borrowed into the closure value, and
|
|
|
|
// for this analysis they are like any other access to a local.
|
|
|
|
//
|
|
|
|
// E.g. in `|b| |c| (a, b, c)`, the upvars of the inner closure
|
|
|
|
// are `a` and `b`, and while `a` is not directly used in the
|
|
|
|
// outer closure, it needs to be an upvar there too, so that
|
|
|
|
// the inner closure can take it (from the outer closure's env).
|
|
|
|
for (&var_id, upvar) in upvars {
|
|
|
|
self.visit_local_use(var_id, upvar.span);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
intravisit::walk_expr(self, expr);
|
|
|
|
}
|
|
|
|
}
|