Auto merge of #78801 - sexxi-goose:min_capture, r=nikomatsakis

RFC-2229: Implement Precise Capture Analysis

### This PR introduces
- Feature gate for RFC-2229 (incomplete) `capture_disjoint_field`
- Rustc Attribute to print out the capture analysis `rustc_capture_analysis`
- Precise capture analysis

### Description of the analysis
1. If the feature gate is not set then all variables that are not local to the closure will be added to the list of captures. (This is for backcompat)
2. The rest of the analysis is based entirely on how the captured `Place`s are used within the closure. Precise information (i.e. projections) about the `Place` is maintained throughout.
3. To reduce the amount of information we need to keep track of, we do a minimization step. In this step, we determine a list such that no Place within this list represents an ancestor path to another entry in the list.  Check rust-lang/project-rfc-2229#9 for more detailed examples.
4. To keep the compiler functional as before we implement a Bridge between the results of this new analysis to existing data structures used for closure captures. Note the new capture analysis results are only part of MaybeTypeckTables that is the information is only available during typeck-ing.

### Known issues
- Statements like `let _ = x` will make the compiler ICE when used within a closure with the feature enabled. More generally speaking the issue is caused by `let` statements that create no bindings and are init'ed using a Place expression.

### Testing
We removed the code that would handle the case where the feature gate is not set, to enable the feature as default and did a bors try and perf run. More information here: #78762

### Thanks
This has been slowly in the works for a while now.
I want to call out `@Azhng` `@ChrisPardy` `@null-sleep` `@jenniferwills` `@logmosier` `@roxelo` for working on this and the previous PRs that led up to this, `@nikomatsakis` for guiding us.

Closes rust-lang/project-rfc-2229#7
Closes rust-lang/project-rfc-2229#9
Closes rust-lang/project-rfc-2229#6
Closes rust-lang/project-rfc-2229#19

r? `@nikomatsakis`
This commit is contained in:
bors 2020-11-17 03:56:03 +00:00
commit b5c37e86ff
37 changed files with 2484 additions and 177 deletions

View File

@ -616,6 +616,9 @@ declare_features! (
/// Enables `#[cfg(panic = "...")]` config key. /// Enables `#[cfg(panic = "...")]` config key.
(active, cfg_panic, "1.49.0", Some(77443), None), (active, cfg_panic, "1.49.0", Some(77443), None),
/// Allows capturing disjoint fields in a closure/generator (RFC 2229).
(active, capture_disjoint_fields, "1.49.0", Some(53488), None),
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// feature-group-end: actual feature gates // feature-group-end: actual feature gates
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@ -639,6 +642,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
sym::inline_const, sym::inline_const,
sym::repr128, sym::repr128,
sym::unsized_locals, sym::unsized_locals,
sym::capture_disjoint_fields,
]; ];
/// Some features are not allowed to be used together at the same time, if /// Some features are not allowed to be used together at the same time, if

View File

@ -547,6 +547,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// ========================================================================== // ==========================================================================
rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)), rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)),
rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)),
rustc_attr!(TEST, rustc_variance, Normal, template!(Word)), rustc_attr!(TEST, rustc_variance, Normal, template!(Word)),
rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")), rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")),
rustc_attr!(TEST, rustc_regions, Normal, template!(Word)), rustc_attr!(TEST, rustc_regions, Normal, template!(Word)),

View File

@ -415,6 +415,10 @@ pub struct TypeckResults<'tcx> {
/// entire variable. /// entire variable.
pub closure_captures: ty::UpvarListMap, pub closure_captures: ty::UpvarListMap,
/// Tracks the minimum captures required for a closure;
/// see `MinCaptureInformationMap` for more details.
pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>,
/// Stores the type, expression, span and optional scope span of all types /// Stores the type, expression, span and optional scope span of all types
/// that are live across the yield of this generator (if a generator). /// that are live across the yield of this generator (if a generator).
pub generator_interior_types: Vec<GeneratorInteriorTypeCause<'tcx>>, pub generator_interior_types: Vec<GeneratorInteriorTypeCause<'tcx>>,
@ -442,6 +446,7 @@ impl<'tcx> TypeckResults<'tcx> {
tainted_by_errors: None, tainted_by_errors: None,
concrete_opaque_types: Default::default(), concrete_opaque_types: Default::default(),
closure_captures: Default::default(), closure_captures: Default::default(),
closure_min_captures: Default::default(),
generator_interior_types: Default::default(), generator_interior_types: Default::default(),
} }
} }
@ -676,6 +681,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
tainted_by_errors, tainted_by_errors,
ref concrete_opaque_types, ref concrete_opaque_types,
ref closure_captures, ref closure_captures,
ref closure_min_captures,
ref generator_interior_types, ref generator_interior_types,
} = *self; } = *self;
@ -709,6 +715,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
tainted_by_errors.hash_stable(hcx, hasher); tainted_by_errors.hash_stable(hcx, hasher);
concrete_opaque_types.hash_stable(hcx, hasher); concrete_opaque_types.hash_stable(hcx, hasher);
closure_captures.hash_stable(hcx, hasher); closure_captures.hash_stable(hcx, hasher);
closure_min_captures.hash_stable(hcx, hasher);
generator_interior_types.hash_stable(hcx, hasher); generator_interior_types.hash_stable(hcx, hasher);
}) })
} }

View File

@ -6,6 +6,7 @@ pub use self::IntVarValue::*;
pub use self::Variance::*; pub use self::Variance::*;
use crate::hir::exports::ExportMap; use crate::hir::exports::ExportMap;
use crate::hir::place::Place as HirPlace;
use crate::ich::StableHashingContext; use crate::ich::StableHashingContext;
use crate::middle::cstore::CrateStoreDyn; use crate::middle::cstore::CrateStoreDyn;
use crate::middle::resolve_lifetime::ObjectLifetimeDefault; use crate::middle::resolve_lifetime::ObjectLifetimeDefault;
@ -685,6 +686,12 @@ pub struct UpvarId {
pub closure_expr_id: LocalDefId, pub closure_expr_id: LocalDefId,
} }
impl UpvarId {
pub fn new(var_hir_id: hir::HirId, closure_def_id: LocalDefId) -> UpvarId {
UpvarId { var_path: UpvarPath { hir_id: var_hir_id }, closure_expr_id: closure_def_id }
}
}
#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)] #[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)]
pub enum BorrowKind { pub enum BorrowKind {
/// Data must be immutable and is aliasable. /// Data must be immutable and is aliasable.
@ -767,6 +774,56 @@ pub struct UpvarBorrow<'tcx> {
pub region: ty::Region<'tcx>, pub region: ty::Region<'tcx>,
} }
/// Given the closure DefId this map provides a map of root variables to minimum
/// set of `CapturedPlace`s that need to be tracked to support all captures of that closure.
pub type MinCaptureInformationMap<'tcx> = FxHashMap<DefId, RootVariableMinCaptureList<'tcx>>;
/// Part of `MinCaptureInformationMap`; Maps a root variable to the list of `CapturedPlace`.
/// Used to track the minimum set of `Place`s that need to be captured to support all
/// Places captured by the closure starting at a given root variable.
///
/// This provides a convenient and quick way of checking if a variable being used within
/// a closure is a capture of a local variable.
pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureList<'tcx>>;
/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s.
pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
/// A `Place` and the corresponding `CaptureInfo`.
#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
pub struct CapturedPlace<'tcx> {
pub place: HirPlace<'tcx>,
pub info: CaptureInfo<'tcx>,
}
/// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move)
/// for a particular capture as well as identifying the part of the source code
/// that triggered this capture to occur.
#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)]
pub struct CaptureInfo<'tcx> {
/// Expr Id pointing to use that resulted in selecting the current capture kind
///
/// If the user doesn't enable feature `capture_disjoint_fields` (RFC 2229) then, it is
/// possible that we don't see the use of a particular place resulting in expr_id being
/// None. In such case we fallback on uvpars_mentioned for span.
///
/// Eg:
/// ```rust,no_run
/// let x = 5;
///
/// let c = || {
/// let _ = x
/// };
/// ```
///
/// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured,
/// but we won't see it being used during capture analysis, since it's essentially a discard.
pub expr_id: Option<hir::HirId>,
/// Capture mode that was selected
pub capture_kind: UpvarCapture<'tcx>,
}
pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>; pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>;
pub type UpvarCaptureMap<'tcx> = FxHashMap<UpvarId, UpvarCapture<'tcx>>; pub type UpvarCaptureMap<'tcx> = FxHashMap<UpvarId, UpvarCapture<'tcx>>;

View File

@ -383,14 +383,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
self.describe_field_from_ty(&ty, field, variant_index) self.describe_field_from_ty(&ty, field, variant_index)
} }
ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
// `tcx.upvars_mentioned(def_id)` returns an `Option`, which is `None` in case // We won't be borrowck'ing here if the closure came from another crate,
// the closure comes from another crate. But in that case we wouldn't // so it's safe to call `expect_local`.
// be borrowck'ing it, so we can just unwrap: //
let (&var_id, _) = self // We know the field exists so it's safe to call operator[] and `unwrap` here.
.infcx let (&var_id, _) =
.tcx self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id]
.upvars_mentioned(def_id)
.unwrap()
.get_index(field.index()) .get_index(field.index())
.unwrap(); .unwrap();
@ -967,9 +965,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr { if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr {
for ((upvar_hir_id, upvar), place) in for (upvar_hir_id, place) in
self.infcx.tcx.upvars_mentioned(def_id)?.iter().zip(places) self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id]
.keys()
.zip(places)
{ {
let span = self.infcx.tcx.upvars_mentioned(local_did)?[upvar_hir_id].span;
match place { match place {
Operand::Copy(place) | Operand::Move(place) Operand::Copy(place) | Operand::Move(place)
if target_place == place.as_ref() => if target_place == place.as_ref() =>
@ -991,7 +992,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let usage_span = let usage_span =
match self.infcx.tcx.typeck(local_did).upvar_capture(upvar_id) { match self.infcx.tcx.typeck(local_did).upvar_capture(upvar_id) {
ty::UpvarCapture::ByValue(Some(span)) => span, ty::UpvarCapture::ByValue(Some(span)) => span,
_ => upvar.span, _ => span,
}; };
return Some((*args_span, generator_kind, usage_span)); return Some((*args_span, generator_kind, usage_span));
} }

View File

@ -387,8 +387,9 @@ fn make_mirror_unadjusted<'a, 'tcx>(
} }
}; };
let upvars = cx let upvars = cx
.tcx .typeck_results()
.upvars_mentioned(def_id) .closure_captures
.get(&def_id)
.iter() .iter()
.flat_map(|upvars| upvars.iter()) .flat_map(|upvars| upvars.iter())
.zip(substs.upvar_tys()) .zip(substs.upvar_tys())

View File

@ -317,10 +317,11 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
// swap in a new set of IR maps for this body // swap in a new set of IR maps for this body
let mut maps = IrMaps::new(self.tcx); let mut maps = IrMaps::new(self.tcx);
let hir_id = maps.tcx.hir().body_owner(body.id()); let hir_id = maps.tcx.hir().body_owner(body.id());
let def_id = maps.tcx.hir().local_def_id(hir_id); let local_def_id = maps.tcx.hir().local_def_id(hir_id);
let def_id = local_def_id.to_def_id();
// Don't run unused pass for #[derive()] // Don't run unused pass for #[derive()]
if let Some(parent) = self.tcx.parent(def_id.to_def_id()) { if let Some(parent) = self.tcx.parent(def_id) {
if let DefKind::Impl = self.tcx.def_kind(parent.expect_local()) { if let DefKind::Impl = self.tcx.def_kind(parent.expect_local()) {
if self.tcx.has_attr(parent, sym::automatically_derived) { if self.tcx.has_attr(parent, sym::automatically_derived) {
return; return;
@ -328,8 +329,8 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
} }
} }
if let Some(upvars) = maps.tcx.upvars_mentioned(def_id) { if let Some(captures) = maps.tcx.typeck(local_def_id).closure_captures.get(&def_id) {
for (&var_hir_id, _upvar) in upvars { for &var_hir_id in captures.keys() {
let var_name = maps.tcx.hir().name(var_hir_id); let var_name = maps.tcx.hir().name(var_hir_id);
maps.add_variable(Upvar(var_hir_id, var_name)); maps.add_variable(Upvar(var_hir_id, var_name));
} }
@ -340,7 +341,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
intravisit::walk_body(&mut maps, body); intravisit::walk_body(&mut maps, body);
// compute liveness // compute liveness
let mut lsets = Liveness::new(&mut maps, def_id); let mut lsets = Liveness::new(&mut maps, local_def_id);
let entry_ln = lsets.compute(&body, hir_id); let entry_ln = lsets.compute(&body, hir_id);
lsets.log_liveness(entry_ln, body.id().hir_id); lsets.log_liveness(entry_ln, body.id().hir_id);
@ -397,10 +398,18 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
// construction site. // construction site.
let mut call_caps = Vec::new(); let mut call_caps = Vec::new();
let closure_def_id = self.tcx.hir().local_def_id(expr.hir_id); let closure_def_id = self.tcx.hir().local_def_id(expr.hir_id);
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { if let Some(captures) = self
call_caps.extend(upvars.iter().map(|(&var_id, upvar)| { .tcx
.typeck(closure_def_id)
.closure_captures
.get(&closure_def_id.to_def_id())
{
// If closure captures is Some, upvars_mentioned must also be Some
let upvars = self.tcx.upvars_mentioned(closure_def_id).unwrap();
call_caps.extend(captures.keys().map(|var_id| {
let upvar = upvars[var_id];
let upvar_ln = self.add_live_node(UpvarNode(upvar.span)); let upvar_ln = self.add_live_node(UpvarNode(upvar.span));
CaptureInfo { ln: upvar_ln, var_hid: var_id } CaptureInfo { ln: upvar_ln, var_hid: *var_id }
})); }));
} }
self.set_captures(expr.hir_id, call_caps); self.set_captures(expr.hir_id, call_caps);
@ -564,6 +573,7 @@ struct Liveness<'a, 'tcx> {
typeck_results: &'a ty::TypeckResults<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>, upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>,
closure_captures: Option<&'tcx FxIndexMap<hir::HirId, ty::UpvarId>>,
successors: IndexVec<LiveNode, Option<LiveNode>>, successors: IndexVec<LiveNode, Option<LiveNode>>,
rwu_table: RWUTable, rwu_table: RWUTable,
@ -587,6 +597,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
let typeck_results = ir.tcx.typeck(body_owner); let typeck_results = ir.tcx.typeck(body_owner);
let param_env = ir.tcx.param_env(body_owner); let param_env = ir.tcx.param_env(body_owner);
let upvars = ir.tcx.upvars_mentioned(body_owner); let upvars = ir.tcx.upvars_mentioned(body_owner);
let closure_captures = typeck_results.closure_captures.get(&body_owner.to_def_id());
let closure_ln = ir.add_live_node(ClosureNode); let closure_ln = ir.add_live_node(ClosureNode);
let exit_ln = ir.add_live_node(ExitNode); let exit_ln = ir.add_live_node(ExitNode);
@ -600,6 +611,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
typeck_results, typeck_results,
param_env, param_env,
upvars, upvars,
closure_captures,
successors: IndexVec::from_elem_n(None, num_live_nodes), successors: IndexVec::from_elem_n(None, num_live_nodes),
rwu_table: RWUTable::new(num_live_nodes * num_vars), rwu_table: RWUTable::new(num_live_nodes * num_vars),
closure_ln, closure_ln,
@ -850,14 +862,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
// if they are live on the entry to the closure, since only the closure // if they are live on the entry to the closure, since only the closure
// itself can access them on subsequent calls. // itself can access them on subsequent calls.
if let Some(upvars) = self.upvars { if let Some(closure_captures) = self.closure_captures {
// Mark upvars captured by reference as used after closure exits. // Mark upvars captured by reference as used after closure exits.
for (&var_hir_id, upvar) in upvars.iter().rev() { // Since closure_captures is Some, upvars must exists too.
let upvar_id = ty::UpvarId { let upvars = self.upvars.unwrap();
var_path: ty::UpvarPath { hir_id: var_hir_id }, for (&var_hir_id, upvar_id) in closure_captures {
closure_expr_id: self.body_owner, let upvar = upvars[&var_hir_id];
}; match self.typeck_results.upvar_capture(*upvar_id) {
match self.typeck_results.upvar_capture(upvar_id) {
ty::UpvarCapture::ByRef(_) => { ty::UpvarCapture::ByRef(_) => {
let var = self.variable(var_hir_id, upvar.span); let var = self.variable(var_hir_id, upvar.span);
self.acc(self.exit_ln, var, ACC_READ | ACC_USE); self.acc(self.exit_ln, var, ACC_READ | ACC_USE);
@ -869,7 +880,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
let succ = self.propagate_through_expr(&body.value, self.exit_ln); let succ = self.propagate_through_expr(&body.value, self.exit_ln);
if self.upvars.is_none() { if self.closure_captures.is_none() {
// Either not a closure, or closure without any captured variables. // Either not a closure, or closure without any captured variables.
// No need to determine liveness of captured variables, since there // No need to determine liveness of captured variables, since there
// are none. // are none.
@ -1341,7 +1352,21 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
acc: u32, acc: u32,
) -> LiveNode { ) -> LiveNode {
match path.res { match path.res {
Res::Local(hid) => self.access_var(hir_id, hid, succ, acc, path.span), Res::Local(hid) => {
let in_upvars = self.upvars.map_or(false, |u| u.contains_key(&hid));
let in_captures = self.closure_captures.map_or(false, |c| c.contains_key(&hid));
match (in_upvars, in_captures) {
(false, _) | (true, true) => self.access_var(hir_id, hid, succ, acc, path.span),
(true, false) => {
// This case is possible when with RFC-2229, a wild pattern
// is used within a closure.
// eg: `let _ = x`. The closure doesn't capture x here,
// even though it's mentioned in the closure.
succ
}
}
}
_ => succ, _ => succ,
} }
} }
@ -1531,11 +1556,15 @@ impl<'tcx> Liveness<'_, 'tcx> {
} }
fn warn_about_unused_upvars(&self, entry_ln: LiveNode) { fn warn_about_unused_upvars(&self, entry_ln: LiveNode) {
let upvars = match self.upvars { let closure_captures = match self.closure_captures {
None => return, None => return,
Some(upvars) => upvars, Some(closure_captures) => closure_captures,
}; };
for (&var_hir_id, upvar) in upvars.iter() {
// If closure_captures is Some(), upvars must be Some() too.
let upvars = self.upvars.unwrap();
for &var_hir_id in closure_captures.keys() {
let upvar = upvars[&var_hir_id];
let var = self.variable(var_hir_id, upvar.span); let var = self.variable(var_hir_id, upvar.span);
let upvar_id = ty::UpvarId { let upvar_id = ty::UpvarId {
var_path: ty::UpvarPath { hir_id: var_hir_id }, var_path: ty::UpvarPath { hir_id: var_hir_id },

View File

@ -318,6 +318,7 @@ symbols! {
call_mut, call_mut,
call_once, call_once,
caller_location, caller_location,
capture_disjoint_fields,
cdylib, cdylib,
ceilf32, ceilf32,
ceilf64, ceilf64,
@ -909,6 +910,7 @@ symbols! {
rustc_args_required_const, rustc_args_required_const,
rustc_attrs, rustc_attrs,
rustc_builtin_macro, rustc_builtin_macro,
rustc_capture_analysis,
rustc_clean, rustc_clean,
rustc_const_stable, rustc_const_stable,
rustc_const_unstable, rustc_const_unstable,

View File

@ -39,10 +39,21 @@ use rustc_hir::def_id::DefId;
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_infer::infer::UpvarRegion; use rustc_infer::infer::UpvarRegion;
use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId}; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind};
use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts};
use rustc_span::sym;
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
use std::collections::hash_map::Entry;
/// Describe the relationship between the paths of two places
/// eg:
/// - `foo` is ancestor of `foo.bar.baz`
/// - `foo.bar.baz` is an descendant of `foo.bar`
/// - `foo.bar` and `foo.baz` are divergent
enum PlaceAncestryRelation {
Ancestor,
Descendant,
Divergent,
}
impl<'a, 'tcx> FnCtxt<'a, 'tcx> { impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) {
@ -111,40 +122,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None None
}; };
let local_def_id = closure_def_id.expect_local();
let mut capture_information: FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>> =
Default::default();
if !self.tcx.features().capture_disjoint_fields {
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
let mut closure_captures: FxIndexMap<hir::HirId, ty::UpvarId> =
FxIndexMap::with_capacity_and_hasher(upvars.len(), Default::default());
for (&var_hir_id, _) in upvars.iter() { for (&var_hir_id, _) in upvars.iter() {
let upvar_id = ty::UpvarId { let place = self.place_for_root_variable(local_def_id, var_hir_id);
var_path: ty::UpvarPath { hir_id: var_hir_id },
closure_expr_id: closure_def_id.expect_local(),
};
debug!("seed upvar_id {:?}", upvar_id);
// Adding the upvar Id to the list of Upvars, which will be added
// to the map for the closure at the end of the for loop.
closure_captures.insert(var_hir_id, upvar_id);
let capture_kind = match capture_clause { debug!("seed place {:?}", place);
hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None),
hir::CaptureBy::Ref => {
let origin = UpvarRegion(upvar_id, span);
let upvar_region = self.next_region_var(origin);
let upvar_borrow =
ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region };
ty::UpvarCapture::ByRef(upvar_borrow)
}
};
self.typeck_results.borrow_mut().upvar_capture_map.insert(upvar_id, capture_kind); let upvar_id = ty::UpvarId::new(var_hir_id, local_def_id);
let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span);
let info = ty::CaptureInfo { expr_id: None, capture_kind };
capture_information.insert(place, info);
} }
// Add the vector of upvars to the map keyed with the closure id.
// This gives us an easier access to them without having to call
// tcx.upvars again..
if !closure_captures.is_empty() {
self.typeck_results
.borrow_mut()
.closure_captures
.insert(closure_def_id, closure_captures);
} }
} }
@ -153,9 +147,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut delegate = InferBorrowKind { let mut delegate = InferBorrowKind {
fcx: self, fcx: self,
closure_def_id, closure_def_id,
closure_span: span,
capture_clause,
current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM, current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM,
current_origin: None, current_origin: None,
adjust_upvar_captures: ty::UpvarCaptureMap::default(), capture_information,
}; };
euv::ExprUseVisitor::new( euv::ExprUseVisitor::new(
&mut delegate, &mut delegate,
@ -166,6 +162,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) )
.consume_body(body); .consume_body(body);
debug!(
"For closure={:?}, capture_information={:#?}",
closure_def_id, delegate.capture_information
);
self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span);
if let Some(closure_substs) = infer_kind { if let Some(closure_substs) = infer_kind {
// Unify the (as yet unbound) type variable in the closure // Unify the (as yet unbound) type variable in the closure
// substs with the kind we inferred. // substs with the kind we inferred.
@ -182,7 +184,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
} }
self.typeck_results.borrow_mut().upvar_capture_map.extend(delegate.adjust_upvar_captures); self.compute_min_captures(closure_def_id, delegate);
self.log_closure_min_capture_info(closure_def_id, span);
self.min_captures_to_closure_captures_bridge(closure_def_id);
// Now that we've analyzed the closure, we know how each // Now that we've analyzed the closure, we know how each
// variable is borrowed, and we know what traits the closure // variable is borrowed, and we know what traits the closure
@ -226,15 +231,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let tcx = self.tcx; let tcx = self.tcx;
let closure_def_id = tcx.hir().local_def_id(closure_id); let closure_def_id = tcx.hir().local_def_id(closure_id);
tcx.upvars_mentioned(closure_def_id) self.typeck_results
.borrow()
.closure_captures
.get(&closure_def_id.to_def_id())
.iter() .iter()
.flat_map(|upvars| { .flat_map(|upvars| {
upvars.iter().map(|(&var_hir_id, _)| { upvars.iter().map(|(&var_hir_id, _)| {
let upvar_ty = self.node_ty(var_hir_id); let upvar_ty = self.node_ty(var_hir_id);
let upvar_id = ty::UpvarId { let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id);
var_path: ty::UpvarPath { hir_id: var_hir_id },
closure_expr_id: closure_def_id,
};
let capture = self.typeck_results.borrow().upvar_capture(upvar_id); let capture = self.typeck_results.borrow().upvar_capture(upvar_id);
debug!("var_id={:?} upvar_ty={:?} capture={:?}", var_hir_id, upvar_ty, capture); debug!("var_id={:?} upvar_ty={:?} capture={:?}", var_hir_id, upvar_ty, capture);
@ -250,6 +255,296 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}) })
.collect() .collect()
} }
/// Bridge for closure analysis
/// ----------------------------
///
/// For closure with DefId `c`, the bridge converts structures required for supporting RFC 2229,
/// to structures currently used in the compiler for handling closure captures.
///
/// For example the following structure will be converted:
///
/// closure_min_captures
/// foo -> [ {foo.x, ImmBorrow}, {foo.y, MutBorrow} ]
/// bar -> [ {bar.z, ByValue}, {bar.q, MutBorrow} ]
///
/// to
///
/// 1. closure_captures
/// foo -> UpvarId(foo, c), bar -> UpvarId(bar, c)
///
/// 2. upvar_capture_map
/// UpvarId(foo,c) -> MutBorrow, UpvarId(bar, c) -> ByValue
fn min_captures_to_closure_captures_bridge(&self, closure_def_id: DefId) {
let mut closure_captures: FxIndexMap<hir::HirId, ty::UpvarId> = Default::default();
let mut upvar_capture_map = ty::UpvarCaptureMap::default();
if let Some(min_captures) =
self.typeck_results.borrow().closure_min_captures.get(&closure_def_id)
{
for (var_hir_id, min_list) in min_captures.iter() {
for captured_place in min_list {
let place = &captured_place.place;
let capture_info = captured_place.info;
let upvar_id = match place.base {
PlaceBase::Upvar(upvar_id) => upvar_id,
base => bug!("Expected upvar, found={:?}", base),
};
assert_eq!(upvar_id.var_path.hir_id, *var_hir_id);
assert_eq!(upvar_id.closure_expr_id, closure_def_id.expect_local());
closure_captures.insert(*var_hir_id, upvar_id);
let new_capture_kind = if let Some(capture_kind) =
upvar_capture_map.get(&upvar_id)
{
// upvar_capture_map only stores the UpvarCapture (CaptureKind),
// so we create a fake capture info with no expression.
let fake_capture_info =
ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() };
determine_capture_info(fake_capture_info, capture_info).capture_kind
} else {
capture_info.capture_kind
};
upvar_capture_map.insert(upvar_id, new_capture_kind);
}
}
}
debug!("For closure_def_id={:?}, closure_captures={:#?}", closure_def_id, closure_captures);
debug!(
"For closure_def_id={:?}, upvar_capture_map={:#?}",
closure_def_id, upvar_capture_map
);
if !closure_captures.is_empty() {
self.typeck_results
.borrow_mut()
.closure_captures
.insert(closure_def_id, closure_captures);
self.typeck_results.borrow_mut().upvar_capture_map.extend(upvar_capture_map);
}
}
/// Analyzes the information collected by `InferBorrowKind` to compute the min number of
/// Places (and corresponding capture kind) that we need to keep track of to support all
/// the required captured paths.
///
/// Eg:
/// ```rust,no_run
/// struct Point { x: i32, y: i32 }
///
/// let s: String; // hir_id_s
/// let mut p: Point; // his_id_p
/// let c = || {
/// println!("{}", s); // L1
/// p.x += 10; // L2
/// println!("{}" , p.y) // L3
/// println!("{}", p) // L4
/// drop(s); // L5
/// };
/// ```
/// and let hir_id_L1..5 be the expressions pointing to use of a captured variable on
/// the lines L1..5 respectively.
///
/// InferBorrowKind results in a structure like this:
///
/// ```
/// {
/// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L5, ByValue),
/// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> (hir_id_L2, ByRef(MutBorrow))
/// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> (hir_id_L3, ByRef(ImmutBorrow))
/// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L4, ByRef(ImmutBorrow))
/// ```
///
/// After the min capture analysis, we get:
/// ```
/// {
/// hir_id_s -> [
/// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L4, ByValue)
/// ],
/// hir_id_p -> [
/// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L2, ByRef(MutBorrow)),
/// ],
/// ```
fn compute_min_captures(
&self,
closure_def_id: DefId,
inferred_info: InferBorrowKind<'_, 'tcx>,
) {
let mut root_var_min_capture_list: ty::RootVariableMinCaptureList<'_> = Default::default();
for (place, capture_info) in inferred_info.capture_information.into_iter() {
let var_hir_id = match place.base {
PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
base => bug!("Expected upvar, found={:?}", base),
};
// Arrays are captured in entirety, drop Index projections and projections
// after Index projections.
let first_index_projection =
place.projections.split(|proj| ProjectionKind::Index == proj.kind).next();
let place = Place {
base_ty: place.base_ty,
base: place.base,
projections: first_index_projection.map_or(Vec::new(), |p| p.to_vec()),
};
let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
None => {
let min_cap_list = vec![ty::CapturedPlace { place: place, info: capture_info }];
root_var_min_capture_list.insert(var_hir_id, min_cap_list);
continue;
}
Some(min_cap_list) => min_cap_list,
};
// Go through each entry in the current list of min_captures
// - if ancestor is found, update it's capture kind to account for current place's
// capture information.
//
// - if descendant is found, remove it from the list, and update the current place's
// capture information to account for the descendants's capture kind.
//
// We can never be in a case where the list contains both an ancestor and a descendant
// Also there can only be ancestor but in case of descendants there might be
// multiple.
let mut descendant_found = false;
let mut updated_capture_info = capture_info;
min_cap_list.retain(|possible_descendant| {
match determine_place_ancestry_relation(&place, &possible_descendant.place) {
// current place is ancestor of possible_descendant
PlaceAncestryRelation::Ancestor => {
descendant_found = true;
updated_capture_info =
determine_capture_info(updated_capture_info, possible_descendant.info);
false
}
_ => true,
}
});
let mut ancestor_found = false;
if !descendant_found {
for possible_ancestor in min_cap_list.iter_mut() {
match determine_place_ancestry_relation(&place, &possible_ancestor.place) {
// current place is descendant of possible_ancestor
PlaceAncestryRelation::Descendant => {
ancestor_found = true;
possible_ancestor.info =
determine_capture_info(possible_ancestor.info, capture_info);
// Only one ancestor of the current place will be in the list.
break;
}
_ => {}
}
}
}
// Only need to insert when we don't have an ancestor in the existing min capture list
if !ancestor_found {
let captured_place =
ty::CapturedPlace { place: place.clone(), info: updated_capture_info };
min_cap_list.push(captured_place);
}
}
debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list);
if !root_var_min_capture_list.is_empty() {
self.typeck_results
.borrow_mut()
.closure_min_captures
.insert(closure_def_id, root_var_min_capture_list);
}
}
fn init_capture_kind(
&self,
capture_clause: hir::CaptureBy,
upvar_id: ty::UpvarId,
closure_span: Span,
) -> ty::UpvarCapture<'tcx> {
match capture_clause {
hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None),
hir::CaptureBy::Ref => {
let origin = UpvarRegion(upvar_id, closure_span);
let upvar_region = self.next_region_var(origin);
let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region };
ty::UpvarCapture::ByRef(upvar_borrow)
}
}
}
fn place_for_root_variable(
&self,
closure_def_id: LocalDefId,
var_hir_id: hir::HirId,
) -> Place<'tcx> {
let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id);
Place {
base_ty: self.node_ty(var_hir_id),
base: PlaceBase::Upvar(upvar_id),
projections: Default::default(),
}
}
fn should_log_capture_analysis(&self, closure_def_id: DefId) -> bool {
self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis)
}
fn log_capture_analysis_first_pass(
&self,
closure_def_id: rustc_hir::def_id::DefId,
capture_information: &FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>,
closure_span: Span,
) {
if self.should_log_capture_analysis(closure_def_id) {
let mut diag =
self.tcx.sess.struct_span_err(closure_span, "First Pass analysis includes:");
for (place, capture_info) in capture_information {
let capture_str = construct_capture_info_string(self.tcx, place, capture_info);
let output_str = format!("Capturing {}", capture_str);
let span = capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e));
diag.span_note(span, &output_str);
}
diag.emit();
}
}
fn log_closure_min_capture_info(&self, closure_def_id: DefId, closure_span: Span) {
if self.should_log_capture_analysis(closure_def_id) {
if let Some(min_captures) =
self.typeck_results.borrow().closure_min_captures.get(&closure_def_id)
{
let mut diag =
self.tcx.sess.struct_span_err(closure_span, "Min Capture analysis includes:");
for (_, min_captures_for_var) in min_captures {
for capture in min_captures_for_var {
let place = &capture.place;
let capture_info = &capture.info;
let capture_str =
construct_capture_info_string(self.tcx, place, capture_info);
let output_str = format!("Min Capture {}", capture_str);
let span =
capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e));
diag.span_note(span, &output_str);
}
}
diag.emit();
}
}
}
} }
struct InferBorrowKind<'a, 'tcx> { struct InferBorrowKind<'a, 'tcx> {
@ -258,6 +553,10 @@ struct InferBorrowKind<'a, 'tcx> {
// The def-id of the closure whose kind and upvar accesses are being inferred. // The def-id of the closure whose kind and upvar accesses are being inferred.
closure_def_id: DefId, closure_def_id: DefId,
closure_span: Span,
capture_clause: hir::CaptureBy,
// The kind that we have inferred that the current closure // The kind that we have inferred that the current closure
// requires. Note that we *always* infer a minimal kind, even if // 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 don't always *use* that in the final result (i.e., sometimes
@ -270,9 +569,31 @@ struct InferBorrowKind<'a, 'tcx> {
// variable access that caused us to do so. // variable access that caused us to do so.
current_origin: Option<(Span, Symbol)>, current_origin: Option<(Span, Symbol)>,
// For each upvar that we access, we track the minimal kind of /// For each Place that is captured by the closure, we track the minimal kind of
// access we need (ref, ref mut, move, etc). /// access we need (ref, ref mut, move, etc) and the expression that resulted in such access.
adjust_upvar_captures: ty::UpvarCaptureMap<'tcx>, ///
/// Consider closure where s.str1 is captured via an ImmutableBorrow and
/// s.str2 via a MutableBorrow
///
/// ```rust,no_run
/// struct SomeStruct { str1: String, str2: String }
///
/// // Assume that the HirId for the variable definition is `V1`
/// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") }
///
/// let fix_s = |new_s2| {
/// // Assume that the HirId for the expression `s.str1` is `E1`
/// println!("Updating SomeStruct with str1=", s.str1);
/// // Assume that the HirId for the expression `*s.str2` is `E2`
/// s.str2 = new_s2;
/// };
/// ```
///
/// For closure `fix_s`, (at a high level) the map contains
///
/// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow }
/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
capture_information: FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>,
} }
impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
@ -314,26 +635,15 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
var_name(tcx, upvar_id.var_path.hir_id), var_name(tcx, upvar_id.var_path.hir_id),
); );
let new_capture = ty::UpvarCapture::ByValue(Some(usage_span)); let capture_info = ty::CaptureInfo {
match self.adjust_upvar_captures.entry(upvar_id) { expr_id: Some(diag_expr_id),
Entry::Occupied(mut e) => { capture_kind: ty::UpvarCapture::ByValue(Some(usage_span)),
match e.get() { };
// We always overwrite `ByRef`, since we require
// that the upvar be available by value. let curr_info = self.capture_information[&place_with_id.place];
// let updated_info = determine_capture_info(curr_info, capture_info);
// If we had a previous by-value usage without a specific
// span, use ours instead. Otherwise, keep the first span self.capture_information[&place_with_id.place] = updated_info;
// we encountered, since there isn't an obviously better one.
ty::UpvarCapture::ByRef(_) | ty::UpvarCapture::ByValue(None) => {
e.insert(new_capture);
}
_ => {}
}
}
Entry::Vacant(e) => {
e.insert(new_capture);
}
}
} }
/// Indicates that `place_with_id` is being directly mutated (e.g., assigned /// Indicates that `place_with_id` is being directly mutated (e.g., assigned
@ -349,7 +659,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
place_with_id, diag_expr_id place_with_id, diag_expr_id
); );
if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { if let PlaceBase::Upvar(_) = place_with_id.place.base {
let mut borrow_kind = ty::MutBorrow; let mut borrow_kind = ty::MutBorrow;
for pointer_ty in place_with_id.place.deref_tys() { for pointer_ty in place_with_id.place.deref_tys() {
match pointer_ty.kind() { match pointer_ty.kind() {
@ -363,7 +673,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
_ => (), _ => (),
} }
} }
self.adjust_upvar_deref(upvar_id, self.fcx.tcx.hir().span(diag_expr_id), borrow_kind); self.adjust_upvar_deref(place_with_id, diag_expr_id, borrow_kind);
} }
} }
@ -377,24 +687,20 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
place_with_id, diag_expr_id place_with_id, diag_expr_id
); );
if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { if let PlaceBase::Upvar(_) = place_with_id.place.base {
if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) { if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
// Raw pointers don't inherit mutability. // Raw pointers don't inherit mutability.
return; return;
} }
// for a borrowed pointer to be unique, its base must be unique // for a borrowed pointer to be unique, its base must be unique
self.adjust_upvar_deref( self.adjust_upvar_deref(place_with_id, diag_expr_id, ty::UniqueImmBorrow);
upvar_id,
self.fcx.tcx.hir().span(diag_expr_id),
ty::UniqueImmBorrow,
);
} }
} }
fn adjust_upvar_deref( fn adjust_upvar_deref(
&mut self, &mut self,
upvar_id: ty::UpvarId, place_with_id: &PlaceWithHirId<'tcx>,
place_span: Span, diag_expr_id: hir::HirId,
borrow_kind: ty::BorrowKind, borrow_kind: ty::BorrowKind,
) { ) {
assert!(match borrow_kind { assert!(match borrow_kind {
@ -411,53 +717,51 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
// upvar, then we need to modify the // upvar, then we need to modify the
// borrow_kind of the upvar to make sure it // borrow_kind of the upvar to make sure it
// is inferred to mutable if necessary // is inferred to mutable if necessary
self.adjust_upvar_borrow_kind(upvar_id, borrow_kind); self.adjust_upvar_borrow_kind(place_with_id, diag_expr_id, borrow_kind);
// also need to be in an FnMut closure since this is not an ImmBorrow if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
self.adjust_closure_kind( self.adjust_closure_kind(
upvar_id.closure_expr_id, upvar_id.closure_expr_id,
ty::ClosureKind::FnMut, ty::ClosureKind::FnMut,
place_span, tcx.hir().span(diag_expr_id),
var_name(tcx, upvar_id.var_path.hir_id), var_name(tcx, upvar_id.var_path.hir_id),
); );
} }
}
/// We infer the borrow_kind with which to borrow upvars in a stack closure. /// We infer the borrow_kind with which to borrow upvars in a stack closure.
/// The borrow_kind basically follows a lattice of `imm < unique-imm < mut`, /// The borrow_kind basically follows a lattice of `imm < unique-imm < mut`,
/// 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(&mut self, upvar_id: ty::UpvarId, kind: ty::BorrowKind) { fn adjust_upvar_borrow_kind(
let upvar_capture = self &mut self,
.adjust_upvar_captures place_with_id: &PlaceWithHirId<'tcx>,
.get(&upvar_id) diag_expr_id: hir::HirId,
.copied() kind: ty::BorrowKind,
.unwrap_or_else(|| self.fcx.typeck_results.borrow().upvar_capture(upvar_id)); ) {
let curr_capture_info = self.capture_information[&place_with_id.place];
debug!( debug!(
"adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})", "adjust_upvar_borrow_kind(place={:?}, diag_expr_id={:?}, capture_info={:?}, kind={:?})",
upvar_id, upvar_capture, kind place_with_id, diag_expr_id, curr_capture_info, kind
); );
match upvar_capture { if let ty::UpvarCapture::ByValue(_) = curr_capture_info.capture_kind {
ty::UpvarCapture::ByValue(_) => { // It's already captured by value, we don't need to do anything here
// Upvar is already by-value, the strongest criteria. return;
} } else if let ty::UpvarCapture::ByRef(curr_upvar_borrow) = curr_capture_info.capture_kind {
ty::UpvarCapture::ByRef(mut upvar_borrow) => { // Use the same region as the current capture information
match (upvar_borrow.kind, kind) { // Doesn't matter since only one of the UpvarBorrow will be used.
// Take RHS: let new_upvar_borrow = ty::UpvarBorrow { kind, region: curr_upvar_borrow.region };
(ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow)
| (ty::UniqueImmBorrow, ty::MutBorrow) => { let capture_info = ty::CaptureInfo {
upvar_borrow.kind = kind; expr_id: Some(diag_expr_id),
self.adjust_upvar_captures capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow),
.insert(upvar_id, ty::UpvarCapture::ByRef(upvar_borrow)); };
} let updated_info = determine_capture_info(curr_capture_info, capture_info);
// Take LHS: self.capture_information[&place_with_id.place] = updated_info;
(ty::ImmBorrow, ty::ImmBorrow) };
| (ty::UniqueImmBorrow, ty::ImmBorrow | ty::UniqueImmBorrow)
| (ty::MutBorrow, _) => {}
}
}
}
} }
fn adjust_closure_kind( fn adjust_closure_kind(
@ -501,6 +805,28 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
} }
} }
} }
fn init_capture_info_for_place(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
diag_expr_id: hir::HirId,
) {
if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
assert_eq!(self.closure_def_id.expect_local(), upvar_id.closure_expr_id);
let capture_kind =
self.fcx.init_capture_kind(self.capture_clause, upvar_id, self.closure_span);
let expr_id = Some(diag_expr_id);
let capture_info = ty::CaptureInfo { expr_id, capture_kind };
debug!("Capturing new place {:?}, capture_info={:?}", place_with_id, capture_info);
self.capture_information.insert(place_with_id.place.clone(), capture_info);
} else {
debug!("Not upvar: {:?}", place_with_id);
}
}
} }
impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
@ -514,7 +840,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
"consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})", "consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})",
place_with_id, diag_expr_id, mode place_with_id, diag_expr_id, mode
); );
self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id, mode); if !self.capture_information.contains_key(&place_with_id.place) {
self.init_capture_info_for_place(place_with_id, diag_expr_id);
}
self.adjust_upvar_borrow_kind_for_consume(place_with_id, diag_expr_id, mode);
} }
fn borrow( fn borrow(
@ -528,6 +858,10 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
place_with_id, diag_expr_id, bk place_with_id, diag_expr_id, bk
); );
if !self.capture_information.contains_key(&place_with_id.place) {
self.init_capture_info_for_place(place_with_id, diag_expr_id);
}
match bk { match bk {
ty::ImmBorrow => {} ty::ImmBorrow => {}
ty::UniqueImmBorrow => { ty::UniqueImmBorrow => {
@ -541,10 +875,175 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
debug!("mutate(assignee_place={:?}, diag_expr_id={:?})", assignee_place, diag_expr_id); debug!("mutate(assignee_place={:?}, diag_expr_id={:?})", assignee_place, diag_expr_id);
if !self.capture_information.contains_key(&assignee_place.place) {
self.init_capture_info_for_place(assignee_place, diag_expr_id);
}
self.adjust_upvar_borrow_kind_for_mut(assignee_place, diag_expr_id); self.adjust_upvar_borrow_kind_for_mut(assignee_place, diag_expr_id);
} }
} }
fn construct_capture_info_string(
tcx: TyCtxt<'_>,
place: &Place<'tcx>,
capture_info: &ty::CaptureInfo<'tcx>,
) -> String {
let variable_name = match place.base {
PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_string(),
_ => bug!("Capture_information should only contain upvars"),
};
let mut projections_str = String::new();
for (i, item) in place.projections.iter().enumerate() {
let proj = match item.kind {
ProjectionKind::Field(a, b) => format!("({:?}, {:?})", a, b),
ProjectionKind::Deref => String::from("Deref"),
ProjectionKind::Index => String::from("Index"),
ProjectionKind::Subslice => String::from("Subslice"),
};
if i != 0 {
projections_str.push_str(",");
}
projections_str.push_str(proj.as_str());
}
let capture_kind_str = match capture_info.capture_kind {
ty::UpvarCapture::ByValue(_) => "ByValue".into(),
ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind),
};
format!("{}[{}] -> {}", variable_name, projections_str, capture_kind_str)
}
fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
tcx.hir().name(var_hir_id) tcx.hir().name(var_hir_id)
} }
/// Helper function to determine if we need to escalate CaptureKind from
/// CaptureInfo A to B and returns the escalated CaptureInfo.
/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
///
/// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based
/// on the `CaptureInfo` containing an associated expression id.
///
/// If both the CaptureKind and Expression are considered to be equivalent,
/// then `CaptureInfo` A is preferred. This can be useful in cases where we want to priortize
/// expressions reported back to the user as part of diagnostics based on which appears earlier
/// in the closure. This can be acheived simply by calling
/// `determine_capture_info(existing_info, current_info)`. This works out because the
/// expressions that occur earlier in the closure body than the current expression are processed before.
/// Consider the following example
/// ```rust,no_run
/// struct Point { x: i32, y: i32 }
/// let mut p: Point { x: 10, y: 10 };
///
/// let c = || {
/// p.x += 10;
/// // ^ E1 ^
/// // ...
/// // More code
/// // ...
/// p.x += 10; // E2
/// // ^ E2 ^
/// };
/// ```
/// `CaptureKind` associated with both `E1` and `E2` will be ByRef(MutBorrow),
/// and both have an expression associated, however for diagnostics we prefer reporting
/// `E1` since it appears earlier in the closure body. When `E2` is being processed we
/// would've already handled `E1`, and have an existing capture_information for it.
/// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return
/// `existing_info_e1` in this case, allowing us to point to `E1` in case of diagnostics.
fn determine_capture_info(
capture_info_a: ty::CaptureInfo<'tcx>,
capture_info_b: ty::CaptureInfo<'tcx>,
) -> ty::CaptureInfo<'tcx> {
// If the capture kind is equivalent then, we don't need to escalate and can compare the
// expressions.
let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
(ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => {
// We don't need to worry about the spans being ignored here.
//
// The expr_id in capture_info corresponds to the span that is stored within
// ByValue(span) and therefore it gets handled with priortizing based on
// expressions below.
true
}
(ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => {
ref_a.kind == ref_b.kind
}
(ty::UpvarCapture::ByValue(_), _) | (ty::UpvarCapture::ByRef(_), _) => false,
};
if eq_capture_kind {
match (capture_info_a.expr_id, capture_info_b.expr_id) {
(Some(_), _) | (None, None) => capture_info_a,
(None, Some(_)) => capture_info_b,
}
} else {
// We select the CaptureKind which ranks higher based the following priority order:
// ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow
match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
(ty::UpvarCapture::ByValue(_), _) => capture_info_a,
(_, ty::UpvarCapture::ByValue(_)) => capture_info_b,
(ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => {
match (ref_a.kind, ref_b.kind) {
// Take LHS:
(ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow)
| (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a,
// Take RHS:
(ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow)
| (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b,
(ty::ImmBorrow, ty::ImmBorrow)
| (ty::UniqueImmBorrow, ty::UniqueImmBorrow)
| (ty::MutBorrow, ty::MutBorrow) => {
bug!("Expected unequal capture kinds");
}
}
}
}
}
}
/// Determines the Ancestry relationship of Place A relative to Place B
///
/// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B
/// `PlaceAncestryRelation::Descendant` implies Place A is descendant of Place B
/// `PlaceAncestryRelation::Divergent` implies neither of them is the ancestor of the other.
fn determine_place_ancestry_relation(
place_a: &Place<'tcx>,
place_b: &Place<'tcx>,
) -> PlaceAncestryRelation {
// If Place A and Place B, don't start off from the same root variable, they are divergent.
if place_a.base != place_b.base {
return PlaceAncestryRelation::Divergent;
}
// Assume of length of projections_a = n
let projections_a = &place_a.projections;
// Assume of length of projections_b = m
let projections_b = &place_b.projections;
let mut same_initial_projections = true;
for (proj_a, proj_b) in projections_a.iter().zip(projections_b.iter()) {
if proj_a != proj_b {
same_initial_projections = false;
break;
}
}
if same_initial_projections {
// First min(n, m) projections are the same
// Select Ancestor/Descendant
if projections_b.len() >= projections_a.len() {
PlaceAncestryRelation::Ancestor
} else {
PlaceAncestryRelation::Descendant
}
} else {
PlaceAncestryRelation::Divergent
}
}

View File

@ -15,10 +15,10 @@ use rustc_index::vec::Idx;
use rustc_infer::infer::InferCtxt; use rustc_infer::infer::InferCtxt;
use rustc_middle::hir::place::ProjectionKind; use rustc_middle::hir::place::ProjectionKind;
use rustc_middle::ty::{self, adjustment, TyCtxt}; use rustc_middle::ty::{self, adjustment, TyCtxt};
use rustc_span::Span;
use rustc_target::abi::VariantIdx; use rustc_target::abi::VariantIdx;
use crate::mem_categorization as mc; use crate::mem_categorization as mc;
use rustc_span::Span;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// The Delegate trait // The Delegate trait
@ -73,6 +73,7 @@ pub enum MutateMode {
// This is the code that actually walks the tree. // This is the code that actually walks the tree.
pub struct ExprUseVisitor<'a, 'tcx> { pub struct ExprUseVisitor<'a, 'tcx> {
mc: mc::MemCategorizationContext<'a, 'tcx>, mc: mc::MemCategorizationContext<'a, 'tcx>,
body_owner: LocalDefId,
delegate: &'a mut dyn Delegate<'tcx>, delegate: &'a mut dyn Delegate<'tcx>,
} }
@ -110,6 +111,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
) -> Self { ) -> Self {
ExprUseVisitor { ExprUseVisitor {
mc: mc::MemCategorizationContext::new(infcx, param_env, body_owner, typeck_results), mc: mc::MemCategorizationContext::new(infcx, param_env, body_owner, typeck_results),
body_owner,
delegate, delegate,
} }
} }
@ -329,8 +331,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
self.consume_expr(base); self.consume_expr(base);
} }
hir::ExprKind::Closure(_, _, _, fn_decl_span, _) => { hir::ExprKind::Closure(..) => {
self.walk_captures(expr, fn_decl_span); self.walk_captures(expr);
} }
hir::ExprKind::Box(ref base) => { hir::ExprKind::Box(ref base) => {
@ -529,7 +531,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat); debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat);
let tcx = self.tcx(); let tcx = self.tcx();
let ExprUseVisitor { ref mc, ref mut delegate } = *self; let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self;
return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| {
if let PatKind::Binding(_, canonical_id, ..) = pat.kind { if let PatKind::Binding(_, canonical_id, ..) = pat.kind {
debug!("walk_pat: binding place={:?} pat={:?}", place, pat,); debug!("walk_pat: binding place={:?} pat={:?}", place, pat,);
@ -569,37 +571,113 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
})); }));
} }
fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>, fn_decl_span: Span) { /// Walk closure captures but using `closure_caputes` instead
debug!("walk_captures({:?})", closure_expr); /// of `closure_min_captures`.
///
/// This is needed because clippy uses `ExprUseVisitor` after TypeckResults
/// are written back. We don't currently writeback min_captures to
/// TypeckResults.
fn walk_captures_closure_captures(&mut self, closure_expr: &hir::Expr<'_>) {
// FIXME(arora-aman): Remove this function once rust-lang/project-rfc-2229#18
// is completed.
debug!("walk_captures_closure_captures({:?}), ", closure_expr);
let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id); let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id();
if let Some(upvars) = self.tcx().upvars_mentioned(closure_def_id) { let cl_span = self.tcx().hir().span(closure_expr.hir_id);
for &var_id in upvars.keys() {
let upvar_id = ty::UpvarId { let captures = &self.mc.typeck_results.closure_captures[&closure_def_id];
var_path: ty::UpvarPath { hir_id: var_id },
closure_expr_id: closure_def_id, for (&var_id, &upvar_id) in captures {
};
let upvar_capture = self.mc.typeck_results.upvar_capture(upvar_id); let upvar_capture = self.mc.typeck_results.upvar_capture(upvar_id);
let captured_place = return_if_err!(self.cat_captured_var( let captured_place =
closure_expr.hir_id, return_if_err!(self.cat_captured_var(closure_expr.hir_id, cl_span, var_id));
fn_decl_span,
var_id,
));
match upvar_capture { match upvar_capture {
ty::UpvarCapture::ByValue(_) => { ty::UpvarCapture::ByValue(_) => {
let mode = copy_or_move(&self.mc, &captured_place); let mode = copy_or_move(&self.mc, &captured_place);
self.delegate.consume(&captured_place, captured_place.hir_id, mode); self.delegate.consume(&captured_place, captured_place.hir_id, mode);
} }
ty::UpvarCapture::ByRef(upvar_borrow) => {
self.delegate.borrow(&captured_place, captured_place.hir_id, upvar_borrow.kind);
}
}
}
}
/// Handle the case where the current body contains a closure.
///
/// When the current body being handled is a closure, then we must make sure that
/// - The parent closure only captures Places from the nested closure that are not local to it.
///
/// In the following example the closures `c` only captures `p.x`` even though `incr`
/// is a capture of the nested closure
///
/// ```rust,ignore(cannot-test-this-because-pseduo-code)
/// let p = ..;
/// let c = || {
/// let incr = 10;
/// let nested = || p.x += incr;
/// }
/// ```
///
/// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing
/// closure as the DefId.
fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) {
debug!("walk_captures({:?})", closure_expr);
let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id();
let upvars = self.tcx().upvars_mentioned(self.body_owner);
// For purposes of this function, generator and closures are equivalent.
let body_owner_is_closure = match self.tcx().type_of(self.body_owner.to_def_id()).kind() {
ty::Closure(..) | ty::Generator(..) => true,
_ => false,
};
if let Some(min_captures) = self.mc.typeck_results.closure_min_captures.get(&closure_def_id)
{
for (var_hir_id, min_list) in min_captures.iter() {
if upvars.map_or(body_owner_is_closure, |upvars| !upvars.contains_key(var_hir_id)) {
// The nested closure might be capturing the current (enclosing) closure's local variables.
// We check if the root variable is ever mentioned within the enclosing closure, if not
// then for the current body (if it's a closure) these aren't captures, we will ignore them.
continue;
}
for captured_place in min_list {
let place = &captured_place.place;
let capture_info = captured_place.info;
let upvar_id = if body_owner_is_closure {
// Mark the place to be captured by the enclosing closure
ty::UpvarId::new(*var_hir_id, self.body_owner)
} else {
ty::UpvarId::new(*var_hir_id, closure_def_id.expect_local())
};
let place_with_id = PlaceWithHirId::new(
capture_info.expr_id.unwrap_or(closure_expr.hir_id),
place.base_ty,
PlaceBase::Upvar(upvar_id),
place.projections.clone(),
);
match capture_info.capture_kind {
ty::UpvarCapture::ByValue(_) => {
let mode = copy_or_move(&self.mc, &place_with_id);
self.delegate.consume(&place_with_id, place_with_id.hir_id, mode);
}
ty::UpvarCapture::ByRef(upvar_borrow) => { ty::UpvarCapture::ByRef(upvar_borrow) => {
self.delegate.borrow( self.delegate.borrow(
&captured_place, &place_with_id,
captured_place.hir_id, place_with_id.hir_id,
upvar_borrow.kind, upvar_borrow.kind,
); );
} }
} }
} }
} }
} else if self.mc.typeck_results.closure_captures.contains_key(&closure_def_id) {
// Handle the case where clippy calls ExprUseVisitor after
self.walk_captures_closure_captures(closure_expr)
}
} }
fn cat_captured_var( fn cat_captured_var(

View File

@ -0,0 +1,24 @@
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| `#[warn(incomplete_features)]` on by default
//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
// Ensure that capture analysis results in arrays being completely captured.
fn main() {
let mut m = [1, 2, 3, 4, 5];
let mut c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
m[0] += 10;
//~^ NOTE: Capturing m[] -> MutBorrow
//~| NOTE: Min Capture m[] -> MutBorrow
m[1] += 40;
};
c();
}

View File

@ -0,0 +1,57 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/arrays-completely-captured.rs:11:17
|
LL | let mut c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/arrays-completely-captured.rs:1:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/arrays-completely-captured.rs:14:5
|
LL | / || {
LL | |
LL | |
LL | | m[0] += 10;
... |
LL | | m[1] += 40;
LL | | };
| |_____^
|
note: Capturing m[] -> MutBorrow
--> $DIR/arrays-completely-captured.rs:17:9
|
LL | m[0] += 10;
| ^
error: Min Capture analysis includes:
--> $DIR/arrays-completely-captured.rs:14:5
|
LL | / || {
LL | |
LL | |
LL | | m[0] += 10;
... |
LL | | m[1] += 40;
LL | | };
| |_____^
|
note: Min Capture m[] -> MutBorrow
--> $DIR/arrays-completely-captured.rs:17:9
|
LL | m[0] += 10;
| ^
error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,33 @@
// FIXME(arora-aman) add run-pass once 2229 is implemented
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut p = Point { x: 10, y: 10 };
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ First Pass analysis includes:
//~| Min Capture analysis includes:
println!("{}", p.x);
//~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow
};
// `c` should only capture `p.x`, therefore mutating `p.y` is allowed.
let py = &mut p.y;
c();
*py = 20;
}

View File

@ -0,0 +1,57 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/capture-disjoint-field-struct.rs:17:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/capture-disjoint-field-struct.rs:3:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/capture-disjoint-field-struct.rs:20:5
|
LL | / || {
LL | |
LL | |
LL | | println!("{}", p.x);
LL | |
LL | |
LL | | };
| |_____^
|
note: Capturing p[(0, 0)] -> ImmBorrow
--> $DIR/capture-disjoint-field-struct.rs:23:24
|
LL | println!("{}", p.x);
| ^^^
error: Min Capture analysis includes:
--> $DIR/capture-disjoint-field-struct.rs:20:5
|
LL | / || {
LL | |
LL | |
LL | | println!("{}", p.x);
LL | |
LL | |
LL | | };
| |_____^
|
note: Min Capture p[(0, 0)] -> ImmBorrow
--> $DIR/capture-disjoint-field-struct.rs:23:24
|
LL | println!("{}", p.x);
| ^^^
error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,28 @@
// FIXME(arora-aman) add run-pass once 2229 is implemented
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
fn main() {
let mut t = (10, 10);
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ First Pass analysis includes:
//~| Min Capture analysis includes:
println!("{}", t.0);
//~^ NOTE: Capturing t[(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture t[(0, 0)] -> ImmBorrow
};
// `c` only captures t.0, therefore mutating t.1 is allowed.
let t1 = &mut t.1;
c();
*t1 = 20;
}

View File

@ -0,0 +1,57 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/capture-disjoint-field-tuple.rs:12:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/capture-disjoint-field-tuple.rs:3:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/capture-disjoint-field-tuple.rs:15:5
|
LL | / || {
LL | |
LL | |
LL | | println!("{}", t.0);
LL | |
LL | |
LL | | };
| |_____^
|
note: Capturing t[(0, 0)] -> ImmBorrow
--> $DIR/capture-disjoint-field-tuple.rs:18:24
|
LL | println!("{}", t.0);
| ^^^
error: Min Capture analysis includes:
--> $DIR/capture-disjoint-field-tuple.rs:15:5
|
LL | / || {
LL | |
LL | |
LL | | println!("{}", t.0);
LL | |
LL | |
LL | | };
| |_____^
|
note: Min Capture t[(0, 0)] -> ImmBorrow
--> $DIR/capture-disjoint-field-tuple.rs:18:24
|
LL | println!("{}", t.0);
| ^^^
error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,64 @@
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
enum Info {
Point(i32, i32, String),
Meta(String, Vec<(i32, i32)>)
}
fn multi_variant_enum() {
let point = Info::Point(10, -10, "1".into());
let vec = Vec::new();
let meta = Info::Meta("meta".into(), vec);
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ First Pass analysis includes:
//~| Min Capture analysis includes:
if let Info::Point(_, _, str) = point {
//~^ NOTE: Capturing point[] -> ImmBorrow
//~| NOTE: Capturing point[(2, 0)] -> ByValue
//~| NOTE: Min Capture point[] -> ByValue
println!("{}", str);
}
if let Info::Meta(_, v) = meta {
//~^ NOTE: Capturing meta[] -> ImmBorrow
//~| NOTE: Capturing meta[(1, 1)] -> ByValue
//~| NOTE: Min Capture meta[] -> ByValue
println!("{:?}", v);
}
};
c();
}
enum SingleVariant {
Point(i32, i32, String),
}
fn single_variant_enum() {
let point = SingleVariant::Point(10, -10, "1".into());
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ First Pass analysis includes:
//~| Min Capture analysis includes:
let SingleVariant::Point(_, _, str) = point;
//~^ NOTE: Capturing point[(2, 0)] -> ByValue
//~| NOTE: Min Capture point[(2, 0)] -> ByValue
println!("{}", str);
};
c();
}
fn main() {}

View File

@ -0,0 +1,122 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/capture-enums.rs:18:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/capture-enums.rs:49:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/capture-enums.rs:1:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/capture-enums.rs:21:5
|
LL | / || {
LL | |
LL | |
LL | | if let Info::Point(_, _, str) = point {
... |
LL | | }
LL | | };
| |_____^
|
note: Capturing point[] -> ImmBorrow
--> $DIR/capture-enums.rs:24:41
|
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Capturing point[(2, 0)] -> ByValue
--> $DIR/capture-enums.rs:24:41
|
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Capturing meta[] -> ImmBorrow
--> $DIR/capture-enums.rs:31:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
note: Capturing meta[(1, 1)] -> ByValue
--> $DIR/capture-enums.rs:31:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
error: Min Capture analysis includes:
--> $DIR/capture-enums.rs:21:5
|
LL | / || {
LL | |
LL | |
LL | | if let Info::Point(_, _, str) = point {
... |
LL | | }
LL | | };
| |_____^
|
note: Min Capture point[] -> ByValue
--> $DIR/capture-enums.rs:24:41
|
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Min Capture meta[] -> ByValue
--> $DIR/capture-enums.rs:31:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
error: First Pass analysis includes:
--> $DIR/capture-enums.rs:52:5
|
LL | / || {
LL | |
LL | |
LL | | let SingleVariant::Point(_, _, str) = point;
... |
LL | | println!("{}", str);
LL | | };
| |_____^
|
note: Capturing point[(2, 0)] -> ByValue
--> $DIR/capture-enums.rs:55:47
|
LL | let SingleVariant::Point(_, _, str) = point;
| ^^^^^
error: Min Capture analysis includes:
--> $DIR/capture-enums.rs:52:5
|
LL | / || {
LL | |
LL | |
LL | | let SingleVariant::Point(_, _, str) = point;
... |
LL | | println!("{}", str);
LL | | };
| |_____^
|
note: Min Capture point[(2, 0)] -> ByValue
--> $DIR/capture-enums.rs:55:47
|
LL | let SingleVariant::Point(_, _, str) = point;
| ^^^^^
error: aborting due to 6 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,77 @@
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
// Test to ensure Index projections are handled properly during capture analysis
// The array should be moved in entirety, even though only some elements are used.
fn arrays() {
let arr: [String; 5] = [format!("A"), format!("B"), format!("C"), format!("D"), format!("E")];
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
let [a, b, .., e] = arr;
//~^ NOTE: Capturing arr[Index] -> ByValue
//~| NOTE: Min Capture arr[] -> ByValue
assert_eq!(a, "A");
assert_eq!(b, "B");
assert_eq!(e, "E");
};
c();
}
struct Point {
x: i32,
y: i32,
id: String,
}
fn structs() {
let mut p = Point { x: 10, y: 10, id: String::new() };
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
let Point { x: ref mut x, y: _, id: moved_id } = p;
//~^ NOTE: Capturing p[(0, 0)] -> MutBorrow
//~| NOTE: Capturing p[(2, 0)] -> ByValue
//~| NOTE: Min Capture p[(0, 0)] -> MutBorrow
//~| NOTE: Min Capture p[(2, 0)] -> ByValue
println!("{}, {}", x, moved_id);
};
c();
}
fn tuples() {
let mut t = (10, String::new(), (String::new(), 42));
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
let (ref mut x, ref ref_str, (moved_s, _)) = t;
//~^ NOTE: Capturing t[(0, 0)] -> MutBorrow
//~| NOTE: Capturing t[(1, 0)] -> ImmBorrow
//~| NOTE: Capturing t[(2, 0),(0, 0)] -> ByValue
//~| NOTE: Min Capture t[(0, 0)] -> MutBorrow
//~| NOTE: Min Capture t[(1, 0)] -> ImmBorrow
//~| NOTE: Min Capture t[(2, 0),(0, 0)] -> ByValue
println!("{}, {} {}", x, ref_str, moved_s);
};
c();
}
fn main() {}

View File

@ -0,0 +1,177 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/destructure_patterns.rs:12:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/destructure_patterns.rs:38:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/destructure_patterns.rs:58:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/destructure_patterns.rs:1:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/destructure_patterns.rs:15:5
|
LL | / || {
LL | |
LL | |
LL | | let [a, b, .., e] = arr;
... |
LL | | assert_eq!(e, "E");
LL | | };
| |_____^
|
note: Capturing arr[Index] -> ByValue
--> $DIR/destructure_patterns.rs:18:29
|
LL | let [a, b, .., e] = arr;
| ^^^
error: Min Capture analysis includes:
--> $DIR/destructure_patterns.rs:15:5
|
LL | / || {
LL | |
LL | |
LL | | let [a, b, .., e] = arr;
... |
LL | | assert_eq!(e, "E");
LL | | };
| |_____^
|
note: Min Capture arr[] -> ByValue
--> $DIR/destructure_patterns.rs:18:29
|
LL | let [a, b, .., e] = arr;
| ^^^
error: First Pass analysis includes:
--> $DIR/destructure_patterns.rs:41:5
|
LL | / || {
LL | |
LL | |
LL | | let Point { x: ref mut x, y: _, id: moved_id } = p;
... |
LL | | println!("{}, {}", x, moved_id);
LL | | };
| |_____^
|
note: Capturing p[(0, 0)] -> MutBorrow
--> $DIR/destructure_patterns.rs:44:58
|
LL | let Point { x: ref mut x, y: _, id: moved_id } = p;
| ^
note: Capturing p[(2, 0)] -> ByValue
--> $DIR/destructure_patterns.rs:44:58
|
LL | let Point { x: ref mut x, y: _, id: moved_id } = p;
| ^
error: Min Capture analysis includes:
--> $DIR/destructure_patterns.rs:41:5
|
LL | / || {
LL | |
LL | |
LL | | let Point { x: ref mut x, y: _, id: moved_id } = p;
... |
LL | | println!("{}, {}", x, moved_id);
LL | | };
| |_____^
|
note: Min Capture p[(0, 0)] -> MutBorrow
--> $DIR/destructure_patterns.rs:44:58
|
LL | let Point { x: ref mut x, y: _, id: moved_id } = p;
| ^
note: Min Capture p[(2, 0)] -> ByValue
--> $DIR/destructure_patterns.rs:44:58
|
LL | let Point { x: ref mut x, y: _, id: moved_id } = p;
| ^
error: First Pass analysis includes:
--> $DIR/destructure_patterns.rs:61:5
|
LL | / || {
LL | |
LL | |
LL | | let (ref mut x, ref ref_str, (moved_s, _)) = t;
... |
LL | | println!("{}, {} {}", x, ref_str, moved_s);
LL | | };
| |_____^
|
note: Capturing t[(0, 0)] -> MutBorrow
--> $DIR/destructure_patterns.rs:64:54
|
LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
| ^
note: Capturing t[(1, 0)] -> ImmBorrow
--> $DIR/destructure_patterns.rs:64:54
|
LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
| ^
note: Capturing t[(2, 0),(0, 0)] -> ByValue
--> $DIR/destructure_patterns.rs:64:54
|
LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
| ^
error: Min Capture analysis includes:
--> $DIR/destructure_patterns.rs:61:5
|
LL | / || {
LL | |
LL | |
LL | | let (ref mut x, ref ref_str, (moved_s, _)) = t;
... |
LL | | println!("{}, {} {}", x, ref_str, moved_s);
LL | | };
| |_____^
|
note: Min Capture t[(0, 0)] -> MutBorrow
--> $DIR/destructure_patterns.rs:64:54
|
LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
| ^
note: Min Capture t[(1, 0)] -> ImmBorrow
--> $DIR/destructure_patterns.rs:64:54
|
LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
| ^
note: Min Capture t[(2, 0),(0, 0)] -> ByValue
--> $DIR/destructure_patterns.rs:64:54
|
LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
| ^
error: aborting due to 9 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,20 @@
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
fn main() {
let s = format!("s");
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
println!("This uses new capture analyysis to capture s={}", s);
//~^ NOTE: Capturing s[] -> ImmBorrow
//~| NOTE: Min Capture s[] -> ImmBorrow
};
}

View File

@ -0,0 +1,57 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/feature-gate-capture_disjoint_fields.rs:10:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/feature-gate-capture_disjoint_fields.rs:1:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/feature-gate-capture_disjoint_fields.rs:13:5
|
LL | / || {
LL | |
LL | |
LL | | println!("This uses new capture analyysis to capture s={}", s);
LL | |
LL | |
LL | | };
| |_____^
|
note: Capturing s[] -> ImmBorrow
--> $DIR/feature-gate-capture_disjoint_fields.rs:16:69
|
LL | println!("This uses new capture analyysis to capture s={}", s);
| ^
error: Min Capture analysis includes:
--> $DIR/feature-gate-capture_disjoint_fields.rs:13:5
|
LL | / || {
LL | |
LL | |
LL | | println!("This uses new capture analyysis to capture s={}", s);
LL | |
LL | |
LL | | };
| |_____^
|
note: Min Capture s[] -> ImmBorrow
--> $DIR/feature-gate-capture_disjoint_fields.rs:16:69
|
LL | println!("This uses new capture analyysis to capture s={}", s);
| ^
error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,45 @@
// FIXME(arora-aman) add run-pass once 2229 is implemented
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
struct Filter {
div: i32,
}
impl Filter {
fn allowed(&self, x: i32) -> bool {
x % self.div == 1
}
}
struct Data {
filter: Filter,
list: Vec<i32>,
}
impl Data {
fn update(&mut self) {
// The closure passed to filter only captures self.filter,
// therefore mutating self.list is allowed.
self.list.retain(
#[rustc_capture_analysis]
|v| self.filter.allowed(*v),
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
//~| NOTE: Capturing self[Deref,(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture self[Deref,(0, 0)] -> ImmBorrow
);
}
}
fn main() {
let mut d = Data { filter: Filter { div: 3 }, list: Vec::new() };
for i in 1..10 {
d.list.push(i);
}
d.update();
}

View File

@ -0,0 +1,35 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/filter-on-struct-member.rs:3:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/filter-on-struct-member.rs:28:13
|
LL | |v| self.filter.allowed(*v),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: Capturing self[Deref,(0, 0)] -> ImmBorrow
--> $DIR/filter-on-struct-member.rs:28:17
|
LL | |v| self.filter.allowed(*v),
| ^^^^^^^^^^^
error: Min Capture analysis includes:
--> $DIR/filter-on-struct-member.rs:28:13
|
LL | |v| self.filter.allowed(*v),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: Min Capture self[Deref,(0, 0)] -> ImmBorrow
--> $DIR/filter-on-struct-member.rs:28:17
|
LL | |v| self.filter.allowed(*v),
| ^^^^^^^^^^^
error: aborting due to 2 previous errors; 1 warning emitted

View File

@ -0,0 +1,41 @@
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
#![allow(unused)]
struct Point {
x: i32,
y: i32,
}
struct Wrapper {
p: Point,
}
fn main() {
let mut w = Wrapper { p: Point { x: 10, y: 10 } };
// Only paths that appears within the closure that directly start off
// a variable defined outside the closure are captured.
//
// Therefore `w.p` is captured
// Note that `wp.x` doesn't start off a variable defined outside the closure.
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
let wp = &w.p;
//~^ NOTE: Capturing w[(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture w[(0, 0)] -> ImmBorrow
println!("{}", wp.x);
};
// Since `c` captures `w.p` by an ImmBorrow, `w.p.y` can't be mutated.
let py = &mut w.p.y;
c();
*py = 20
}

View File

@ -0,0 +1,57 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/multilevel-path-1.rs:24:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/multilevel-path-1.rs:1:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/multilevel-path-1.rs:27:5
|
LL | / || {
LL | |
LL | |
LL | | let wp = &w.p;
... |
LL | | println!("{}", wp.x);
LL | | };
| |_____^
|
note: Capturing w[(0, 0)] -> ImmBorrow
--> $DIR/multilevel-path-1.rs:30:19
|
LL | let wp = &w.p;
| ^^^
error: Min Capture analysis includes:
--> $DIR/multilevel-path-1.rs:27:5
|
LL | / || {
LL | |
LL | |
LL | | let wp = &w.p;
... |
LL | | println!("{}", wp.x);
LL | | };
| |_____^
|
note: Min Capture w[(0, 0)] -> ImmBorrow
--> $DIR/multilevel-path-1.rs:30:19
|
LL | let wp = &w.p;
| ^^^
error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,37 @@
// FIXME(arora-aman) add run-pass once 2229 is implemented
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
#![allow(unused)]
struct Point {
x: i32,
y: i32,
}
struct Wrapper {
p: Point,
}
fn main() {
let mut w = Wrapper { p: Point { x: 10, y: 10 } };
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
println!("{}", w.p.x);
//~^ NOTE: Capturing w[(0, 0),(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow
};
// `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`.
let py = &mut w.p.y;
c();
*py = 20
}

View File

@ -0,0 +1,57 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/multilevel-path-2.rs:21:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/multilevel-path-2.rs:3:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/multilevel-path-2.rs:24:5
|
LL | / || {
LL | |
LL | |
LL | | println!("{}", w.p.x);
LL | |
LL | |
LL | | };
| |_____^
|
note: Capturing w[(0, 0),(0, 0)] -> ImmBorrow
--> $DIR/multilevel-path-2.rs:27:24
|
LL | println!("{}", w.p.x);
| ^^^^^
error: Min Capture analysis includes:
--> $DIR/multilevel-path-2.rs:24:5
|
LL | / || {
LL | |
LL | |
LL | | println!("{}", w.p.x);
LL | |
LL | |
LL | | };
| |_____^
|
note: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow
--> $DIR/multilevel-path-2.rs:27:24
|
LL | println!("{}", w.p.x);
| ^^^^^
error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,56 @@
// FIXME(arora-aman) add run-pass once 2229 is implemented
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
struct Point {
x: i32,
y: i32,
}
// This testcase ensures that nested closures are handles properly
// - The nested closure is analyzed first.
// - The capture kind of the nested closure is accounted for by the enclosing closure
// - Any captured path by the nested closure that starts off a local variable in the enclosing
// closure is not listed as a capture of the enclosing closure.
fn main() {
let mut p = Point { x: 5, y: 20 };
let mut c1 = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
println!("{}", p.x);
//~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow
let incr = 10;
let mut c2 = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| p.y += incr;
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
//~| NOTE: Capturing p[(1, 0)] -> MutBorrow
//~| NOTE: Capturing incr[] -> ImmBorrow
//~| NOTE: Min Capture p[(1, 0)] -> MutBorrow
//~| NOTE: Min Capture incr[] -> ImmBorrow
//~| NOTE: Capturing p[(1, 0)] -> MutBorrow
//~| NOTE: Min Capture p[(1, 0)] -> MutBorrow
c2();
println!("{}", p.y);
};
c1();
let px = &p.x;
println!("{}", px);
c1();
}

View File

@ -0,0 +1,110 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/nested-closure.rs:23:18
|
LL | let mut c1 = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/nested-closure.rs:33:22
|
LL | let mut c2 = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/nested-closure.rs:3:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/nested-closure.rs:36:9
|
LL | || p.y += incr;
| ^^^^^^^^^^^^^^
|
note: Capturing p[(1, 0)] -> MutBorrow
--> $DIR/nested-closure.rs:36:12
|
LL | || p.y += incr;
| ^^^
note: Capturing incr[] -> ImmBorrow
--> $DIR/nested-closure.rs:36:19
|
LL | || p.y += incr;
| ^^^^
error: Min Capture analysis includes:
--> $DIR/nested-closure.rs:36:9
|
LL | || p.y += incr;
| ^^^^^^^^^^^^^^
|
note: Min Capture p[(1, 0)] -> MutBorrow
--> $DIR/nested-closure.rs:36:12
|
LL | || p.y += incr;
| ^^^
note: Min Capture incr[] -> ImmBorrow
--> $DIR/nested-closure.rs:36:19
|
LL | || p.y += incr;
| ^^^^
error: First Pass analysis includes:
--> $DIR/nested-closure.rs:26:5
|
LL | / || {
LL | |
LL | |
LL | | println!("{}", p.x);
... |
LL | | println!("{}", p.y);
LL | | };
| |_____^
|
note: Capturing p[(0, 0)] -> ImmBorrow
--> $DIR/nested-closure.rs:29:24
|
LL | println!("{}", p.x);
| ^^^
note: Capturing p[(1, 0)] -> MutBorrow
--> $DIR/nested-closure.rs:36:12
|
LL | || p.y += incr;
| ^^^
error: Min Capture analysis includes:
--> $DIR/nested-closure.rs:26:5
|
LL | / || {
LL | |
LL | |
LL | | println!("{}", p.x);
... |
LL | | println!("{}", p.y);
LL | | };
| |_____^
|
note: Min Capture p[(0, 0)] -> ImmBorrow
--> $DIR/nested-closure.rs:29:24
|
LL | println!("{}", p.x);
| ^^^
note: Min Capture p[(1, 0)] -> MutBorrow
--> $DIR/nested-closure.rs:36:12
|
LL | || p.y += incr;
| ^^^
error: aborting due to 6 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,35 @@
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
struct Point {
x: f32,
y: f32,
}
struct Pentagon {
points: [Point; 5],
}
fn main() {
let p1 = Point { x: 10.0, y: 10.0 };
let p2 = Point { x: 7.5, y: 12.5 };
let p3 = Point { x: 15.0, y: 15.0 };
let p4 = Point { x: 12.5, y: 12.5 };
let p5 = Point { x: 20.0, y: 10.0 };
let pent = Pentagon { points: [p1, p2, p3, p4, p5] };
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
println!("{}", pent.points[5].x);
//~^ NOTE: Capturing pent[(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture pent[(0, 0)] -> ImmBorrow
};
}

View File

@ -0,0 +1,57 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/path-with-array-access.rs:25:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/path-with-array-access.rs:1:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/path-with-array-access.rs:28:5
|
LL | / || {
LL | |
LL | |
LL | | println!("{}", pent.points[5].x);
LL | |
LL | |
LL | | };
| |_____^
|
note: Capturing pent[(0, 0)] -> ImmBorrow
--> $DIR/path-with-array-access.rs:31:24
|
LL | println!("{}", pent.points[5].x);
| ^^^^^^^^^^^
error: Min Capture analysis includes:
--> $DIR/path-with-array-access.rs:28:5
|
LL | / || {
LL | |
LL | |
LL | | println!("{}", pent.points[5].x);
LL | |
LL | |
LL | | };
| |_____^
|
note: Min Capture pent[(0, 0)] -> ImmBorrow
--> $DIR/path-with-array-access.rs:31:24
|
LL | println!("{}", pent.points[5].x);
| ^^^^^^^^^^^
error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,41 @@
// FIXME(arora-aman) add run-pass once 2229 is implemented
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
// Test to ensure that min analysis meets capture kind for all paths captured.
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut p = Point { x: 10, y: 20 };
//
// Requirements:
// p.x -> MutBoorrow
// p -> ImmBorrow
//
// Requirements met when p is captured via MutBorrow
//
let mut c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
p.x += 10;
//~^ NOTE: Capturing p[(0, 0)] -> MutBorrow
//~| NOTE: Min Capture p[] -> MutBorrow
println!("{:?}", p);
//~^ NOTE: Capturing p[] -> ImmBorrow
};
c();
}

View File

@ -0,0 +1,62 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/simple-struct-min-capture.rs:27:17
|
LL | let mut c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/simple-struct-min-capture.rs:3:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/simple-struct-min-capture.rs:30:5
|
LL | / || {
LL | |
LL | |
LL | | p.x += 10;
... |
LL | |
LL | | };
| |_____^
|
note: Capturing p[(0, 0)] -> MutBorrow
--> $DIR/simple-struct-min-capture.rs:33:9
|
LL | p.x += 10;
| ^^^
note: Capturing p[] -> ImmBorrow
--> $DIR/simple-struct-min-capture.rs:36:26
|
LL | println!("{:?}", p);
| ^
error: Min Capture analysis includes:
--> $DIR/simple-struct-min-capture.rs:30:5
|
LL | / || {
LL | |
LL | |
LL | | p.x += 10;
... |
LL | |
LL | | };
| |_____^
|
note: Min Capture p[] -> MutBorrow
--> $DIR/simple-struct-min-capture.rs:33:9
|
LL | p.x += 10;
| ^^^
error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,75 @@
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
//~| NOTE: `#[warn(incomplete_features)]` on by default
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
#![feature(rustc_attrs)]
// Test to ensure that we can handle cases where
// let statements create no bindings are intialized
// using a Place expression
//
// Note: Currently when feature `capture_disjoint_fields` is enabled
// we can't handle such cases. So the test current use `_x` instead of
// `_` until the issue is resolved.
// Check rust-lang/project-rfc-2229#24 for status.
struct Point {
x: i32,
y: i32,
}
fn wild_struct() {
let p = Point { x: 10, y: 20 };
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
// FIXME(arora-aman): Change `_x` to `_`
let Point { x: _x, y: _ } = p;
//~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow
};
c();
}
fn wild_tuple() {
let t = (String::new(), 10);
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
// FIXME(arora-aman): Change `_x` to `_`
let (_x, _) = t;
//~^ NOTE: Capturing t[(0, 0)] -> ByValue
//~| NOTE: Min Capture t[(0, 0)] -> ByValue
};
c();
}
fn wild_arr() {
let arr = [String::new(), String::new()];
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
// FIXME(arora-aman): Change `_x` to `_`
let [_x, _] = arr;
//~^ NOTE: Capturing arr[Index] -> ByValue
//~| NOTE: Min Capture arr[] -> ByValue
};
c();
}
fn main() {}

View File

@ -0,0 +1,147 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/wild_patterns.rs:24:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/wild_patterns.rs:42:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/wild_patterns.rs:60:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/wild_patterns.rs:1:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/wild_patterns.rs:27:5
|
LL | / || {
LL | |
LL | |
LL | | // FIXME(arora-aman): Change `_x` to `_`
... |
LL | |
LL | | };
| |_____^
|
note: Capturing p[(0, 0)] -> ImmBorrow
--> $DIR/wild_patterns.rs:31:37
|
LL | let Point { x: _x, y: _ } = p;
| ^
error: Min Capture analysis includes:
--> $DIR/wild_patterns.rs:27:5
|
LL | / || {
LL | |
LL | |
LL | | // FIXME(arora-aman): Change `_x` to `_`
... |
LL | |
LL | | };
| |_____^
|
note: Min Capture p[(0, 0)] -> ImmBorrow
--> $DIR/wild_patterns.rs:31:37
|
LL | let Point { x: _x, y: _ } = p;
| ^
error: First Pass analysis includes:
--> $DIR/wild_patterns.rs:45:5
|
LL | / || {
LL | |
LL | |
LL | | // FIXME(arora-aman): Change `_x` to `_`
... |
LL | |
LL | | };
| |_____^
|
note: Capturing t[(0, 0)] -> ByValue
--> $DIR/wild_patterns.rs:49:23
|
LL | let (_x, _) = t;
| ^
error: Min Capture analysis includes:
--> $DIR/wild_patterns.rs:45:5
|
LL | / || {
LL | |
LL | |
LL | | // FIXME(arora-aman): Change `_x` to `_`
... |
LL | |
LL | | };
| |_____^
|
note: Min Capture t[(0, 0)] -> ByValue
--> $DIR/wild_patterns.rs:49:23
|
LL | let (_x, _) = t;
| ^
error: First Pass analysis includes:
--> $DIR/wild_patterns.rs:63:5
|
LL | / || {
LL | |
LL | |
LL | | // FIXME(arora-aman): Change `_x` to `_`
... |
LL | |
LL | | };
| |_____^
|
note: Capturing arr[Index] -> ByValue
--> $DIR/wild_patterns.rs:67:23
|
LL | let [_x, _] = arr;
| ^^^
error: Min Capture analysis includes:
--> $DIR/wild_patterns.rs:63:5
|
LL | / || {
LL | |
LL | |
LL | | // FIXME(arora-aman): Change `_x` to `_`
... |
LL | |
LL | | };
| |_____^
|
note: Min Capture arr[] -> ByValue
--> $DIR/wild_patterns.rs:67:23
|
LL | let [_x, _] = arr;
| ^^^
error: aborting due to 9 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -8,8 +8,8 @@ LL | assert_send(|| {
| ^^^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely | ^^^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely
| |
= help: the trait `Sync` is not implemented for `Cell<i32>` = help: the trait `Sync` is not implemented for `Cell<i32>`
= note: required because of the requirements on the impl of `Send` for `&'_#3r Cell<i32>` = note: required because of the requirements on the impl of `Send` for `&'_#4r Cell<i32>`
= note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#3r Cell<i32>) _#17t]` = note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#4r Cell<i32>) _#17t]`
error: generator cannot be shared between threads safely error: generator cannot be shared between threads safely
--> $DIR/generator-print-verbose-2.rs:12:5 --> $DIR/generator-print-verbose-2.rs:12:5