Auto merge of #79553 - sexxi-goose:mir_min_cap_writeback, r=nikomatsakis

Capture precise paths in THIR and MIR

This PR allows THIR and MIR to use the result of the new capture analysis to actually capture precise paths

To achieve we:
- Writeback min capture results to TypeckResults
- Move handling upvars to PlaceBuilder in mir_build
- Lower precise paths in THIR build by reading min_captures
- Search for ancestors in min_capture when trying to build a MIR place which starts off of an upvar

Closes: https://github.com/rust-lang/project-rfc-2229/issues/10

Partly implements: rust-lang/project-rfc-2229#18

Work that remains (not in this PR):
- [ ] [Known bugs when feature gate is enabled](https://github.com/rust-lang/project-rfc-2229/projects/1?card_filter_query=label%3Abug)
- [ ] Use min_capure_map for
  - [ ] Liveness analysis
  - [ ] rustc_mir/interpret/validity.rs
  - [ ] regionck
- [ ] rust-lang/project-rfc-2229#8
- [ ] remove closure_captures and upvar_capture_map

r? `@ghost`
This commit is contained in:
bors 2020-12-12 00:23:29 +00:00
commit 5bd9b60333
41 changed files with 1479 additions and 229 deletions

View File

@ -4,7 +4,18 @@ use crate::ty::Ty;
use rustc_hir::HirId;
use rustc_target::abi::VariantIdx;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
TyEncodable,
TyDecodable,
TypeFoldable,
HashStable
)]
pub enum PlaceBase {
/// A temporary variable
Rvalue,
@ -16,7 +27,18 @@ pub enum PlaceBase {
Upvar(ty::UpvarId),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
TyEncodable,
TyDecodable,
TypeFoldable,
HashStable
)]
pub enum ProjectionKind {
/// A dereference of a pointer, reference or `Box<T>` of the given type
Deref,
@ -36,7 +58,18 @@ pub enum ProjectionKind {
Subslice,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
TyEncodable,
TyDecodable,
TypeFoldable,
HashStable
)]
pub struct Projection<'tcx> {
/// Type after the projection is being applied.
pub ty: Ty<'tcx>,
@ -48,7 +81,7 @@ pub struct Projection<'tcx> {
/// A `Place` represents how a value is located in memory.
///
/// This is an HIR version of `mir::Place`
#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
pub struct Place<'tcx> {
/// The type of the `PlaceBase`
pub base_ty: Ty<'tcx>,
@ -61,7 +94,7 @@ pub struct Place<'tcx> {
/// A `PlaceWithHirId` represents how a value is located in memory.
///
/// This is an HIR version of `mir::Place`
#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
pub struct PlaceWithHirId<'tcx> {
/// `HirId` of the expression or pattern producing this value.
pub hir_id: HirId,

View File

@ -624,6 +624,19 @@ impl<'tcx> TypeckResults<'tcx> {
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments }
}
/// For a given closure, returns the iterator of `ty::CapturedPlace`s that are captured
/// by the closure.
pub fn closure_min_captures_flattened(
&self,
closure_def_id: DefId,
) -> impl Iterator<Item = &ty::CapturedPlace<'tcx>> {
self.closure_min_captures
.get(&closure_def_id)
.map(|closure_min_captures| closure_min_captures.values().flat_map(|v| v.iter()))
.into_iter()
.flatten()
}
pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> {
self.upvar_capture_map[&upvar_id]
}

View File

@ -672,7 +672,18 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TyS<'tcx> {
#[rustc_diagnostic_item = "Ty"]
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
TyEncodable,
TyDecodable,
TypeFoldable,
HashStable
)]
pub struct UpvarPath {
pub hir_id: hir::HirId,
}
@ -680,7 +691,7 @@ pub struct UpvarPath {
/// Upvars do not get their own `NodeId`. Instead, we use the pair of
/// the original var ID (that is, the root variable that is referenced
/// by the upvar) and the ID of the closure expression.
#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
pub struct UpvarId {
pub var_path: UpvarPath,
pub closure_expr_id: LocalDefId,
@ -692,7 +703,7 @@ impl UpvarId {
}
}
#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)]
#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, TypeFoldable, Copy, HashStable)]
pub enum BorrowKind {
/// Data must be immutable and is aliasable.
ImmBorrow,
@ -746,7 +757,7 @@ pub enum BorrowKind {
/// Information describing the capture of an upvar. This is computed
/// during `typeck`, specifically by `regionck`.
#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)]
#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
pub enum UpvarCapture<'tcx> {
/// Upvar is captured by value. This is always true when the
/// closure is labeled `move`, but can also be true in other cases
@ -763,7 +774,7 @@ pub enum UpvarCapture<'tcx> {
ByRef(UpvarBorrow<'tcx>),
}
#[derive(PartialEq, Clone, Copy, TyEncodable, TyDecodable, HashStable)]
#[derive(PartialEq, Clone, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
pub struct UpvarBorrow<'tcx> {
/// The kind of borrow: by-ref upvars have access to shared
/// immutable borrows, which are not part of the normal language
@ -790,7 +801,7 @@ pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureLis
pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
/// A `Place` and the corresponding `CaptureInfo`.
#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
pub struct CapturedPlace<'tcx> {
pub place: HirPlace<'tcx>,
pub info: CaptureInfo<'tcx>,
@ -799,7 +810,7 @@ pub struct CapturedPlace<'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)]
#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
pub struct CaptureInfo<'tcx> {
/// Expr Id pointing to use that resulted in selecting the current capture kind
///

View File

@ -2174,6 +2174,15 @@ impl<'tcx> TyS<'tcx> {
}
}
/// Get the `i`-th element of a tuple.
/// Panics when called on anything but a tuple.
pub fn tuple_element_ty(&self, i: usize) -> Option<Ty<'tcx>> {
match self.kind() {
Tuple(substs) => substs.iter().nth(i).map(|field| field.expect_ty()),
_ => bug!("tuple_fields called on non-tuple"),
}
}
/// If the type contains variants, returns the valid range of variant indices.
//
// FIXME: This requires the optimized MIR in the case of generators.

View File

@ -9,6 +9,7 @@ use rustc_hir::{HirId, Node};
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
use rustc_middle::mir::{
traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem,
PlaceRef,
@ -75,6 +76,7 @@ crate use region_infer::RegionInferenceContext;
crate struct Upvar {
name: Symbol,
// FIXME(project-rfc-2229#8): This should use Place or something similar
var_hir_id: HirId,
/// If true, the capture is behind a reference.
@ -155,13 +157,13 @@ fn do_mir_borrowck<'a, 'tcx>(
infcx.set_tainted_by_errors();
}
let upvars: Vec<_> = tables
.closure_captures
.get(&def.did.to_def_id())
.into_iter()
.flat_map(|v| v.values())
.map(|upvar_id| {
let var_hir_id = upvar_id.var_path.hir_id;
let capture = tables.upvar_capture(*upvar_id);
.closure_min_captures_flattened(def.did.to_def_id())
.map(|captured_place| {
let var_hir_id = match captured_place.place.base {
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
_ => bug!("Expected upvar"),
};
let capture = captured_place.info.capture_kind;
let by_ref = match capture {
ty::UpvarCapture::ByValue(_) => false,
ty::UpvarCapture::ByRef(..) => true,

View File

@ -749,7 +749,11 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
(&adt_def.variants[VariantIdx::new(0)], substs)
}
ty::Closure(_, substs) => {
return match substs.as_closure().upvar_tys().nth(field.index()) {
return match substs
.as_closure()
.tupled_upvars_ty()
.tuple_element_ty(field.index())
{
Some(ty) => Ok(ty),
None => Err(FieldAccessError::OutOfRange {
field_count: substs.as_closure().upvar_tys().count(),

View File

@ -4,14 +4,62 @@ use crate::build::expr::category::Category;
use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::thir::*;
use rustc_hir::def_id::DefId;
use rustc_hir::HirId;
use rustc_middle::middle::region;
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
use rustc_middle::mir::AssertKind::BoundsCheck;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
use rustc_span::Span;
use rustc_target::abi::VariantIdx;
use rustc_index::vec::Idx;
/// The "outermost" place that holds this value.
#[derive(Copy, Clone)]
crate enum PlaceBase {
/// Denotes the start of a `Place`.
Local(Local),
/// When building place for an expression within a closure, the place might start off a
/// captured path. When `capture_disjoint_fields` is enabled, we might not know the capture
/// index (within the desugared closure) of the captured path until most of the projections
/// are applied. We use `PlaceBase::Upvar` to keep track of the root variable off of which the
/// captured path starts, the closure the capture belongs to and the trait the closure
/// implements.
///
/// Once we have figured out the capture index, we can convert the place builder to start from
/// `PlaceBase::Local`.
///
/// Consider the following example
/// ```rust
/// let t = (10, (10, (10, 10)));
///
/// let c = || {
/// println!("{}", t.0.0.0);
/// };
/// ```
/// Here the THIR expression for `t.0.0.0` will be something like
///
/// ```
/// * Field(0)
/// * Field(0)
/// * Field(0)
/// * UpvarRef(t)
/// ```
///
/// When `capture_disjoint_fields` is enabled, `t.0.0.0` is captured and we won't be able to
/// figure out that it is captured until all the `Field` projections are applied.
Upvar {
/// HirId of the upvar
var_hir_id: HirId,
/// DefId of the closure
closure_def_id: DefId,
/// The trait closure implements, `Fn`, `FnMut`, `FnOnce`
closure_kind: ty::ClosureKind },
}
/// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a
/// place by pushing more and more projections onto the end, and then convert the final set into a
/// place using the `into_place` method.
@ -19,14 +67,240 @@ use rustc_index::vec::Idx;
/// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
#[derive(Clone)]
struct PlaceBuilder<'tcx> {
local: Local,
crate struct PlaceBuilder<'tcx> {
base: PlaceBase,
projection: Vec<PlaceElem<'tcx>>,
}
/// Given a list of MIR projections, convert them to list of HIR ProjectionKind.
/// The projections are truncated to represent a path that might be captured by a
/// closure/generator. This implies the vector returned from this function doesn't contain
/// ProjectionElems `Downcast`, `ConstantIndex`, `Index`, or `Subslice` because those will never be
/// part of a path that is captued by a closure. We stop applying projections once we see the first
/// projection that isn't captured by a closure.
fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
mir_projections: &Vec<PlaceElem<'tcx>>,
) -> Vec<HirProjectionKind> {
let mut hir_projections = Vec::new();
for mir_projection in mir_projections {
let hir_projection = match mir_projection {
ProjectionElem::Deref => HirProjectionKind::Deref,
ProjectionElem::Field(field, _) => {
// We will never encouter this for multivariant enums,
// read the comment for `Downcast`.
HirProjectionKind::Field(field.index() as u32, VariantIdx::new(0))
},
ProjectionElem::Downcast(..) => {
// This projections exist only for enums that have
// multiple variants. Since such enums that are captured
// completely, we can stop here.
break
},
ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {
// We don't capture array-access projections.
// We can stop here as arrays are captured completely.
break
},
};
hir_projections.push(hir_projection);
}
hir_projections
}
/// Return true if the `proj_possible_ancestor` represents an ancestor path
/// to `proj_capture` or `proj_possible_ancestor` is same as `proj_capture`,
/// assuming they both start off of the same root variable.
///
/// **Note:** It's the caller's responsibility to ensure that both lists of projections
/// start off of the same root variable.
///
/// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of
/// `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`.
/// Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`.
/// 2. Since we only look at the projections here function will return `bar.x` as an a valid
/// ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections
/// list are being applied to the same root variable.
fn is_ancestor_or_same_capture(
proj_possible_ancestor: &Vec<HirProjectionKind>,
proj_capture: &Vec<HirProjectionKind>,
) -> bool {
// We want to make sure `is_ancestor_or_same_capture("x.0.0", "x.0")` to return false.
// Therefore we can't just check if all projections are same in the zipped iterator below.
if proj_possible_ancestor.len() > proj_capture.len() {
return false;
}
proj_possible_ancestor.iter().zip(proj_capture).all(|(a, b)| a == b)
}
/// Computes the index of a capture within the desugared closure provided the closure's
/// `closure_min_captures` and the capture's index of the capture in the
/// `ty::MinCaptureList` of the root variable `var_hir_id`.
fn compute_capture_idx<'tcx>(
closure_min_captures: &ty::RootVariableMinCaptureList<'tcx>,
var_hir_id: HirId,
root_var_idx: usize,
) -> usize {
let mut res = 0;
for (var_id, capture_list) in closure_min_captures {
if *var_id == var_hir_id {
res += root_var_idx;
break;
} else {
res += capture_list.len();
}
}
res
}
/// Given a closure, returns the index of a capture within the desugared closure struct and the
/// `ty::CapturedPlace` which is the ancestor of the Place represented using the `var_hir_id`
/// and `projection`.
///
/// Note there will be at most one ancestor for any given Place.
///
/// Returns None, when the ancestor is not found.
fn find_capture_matching_projections<'a, 'tcx>(
typeck_results: &'a ty::TypeckResults<'tcx>,
var_hir_id: HirId,
closure_def_id: DefId,
projections: &Vec<PlaceElem<'tcx>>,
) -> Option<(usize, &'a ty::CapturedPlace<'tcx>)> {
let closure_min_captures = typeck_results.closure_min_captures.get(&closure_def_id)?;
let root_variable_min_captures = closure_min_captures.get(&var_hir_id)?;
let hir_projections = convert_to_hir_projections_and_truncate_for_capture(projections);
// If an ancestor is found, `idx` is the index within the list of captured places
// for root variable `var_hir_id` and `capture` is the `ty::CapturedPlace` itself.
let (idx, capture) = root_variable_min_captures.iter().enumerate().find(|(_, capture)| {
let possible_ancestor_proj_kinds =
capture.place.projections.iter().map(|proj| proj.kind).collect();
is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections)
})?;
// Convert index to be from the presepective of the entire closure_min_captures map
// instead of just the root variable capture list
Some((compute_capture_idx(closure_min_captures, var_hir_id, idx), capture))
}
/// Takes a PlaceBuilder and resolves the upvar (if any) within it, so that the
/// `PlaceBuilder` now starts from `PlaceBase::Local`.
///
/// Returns a Result with the error being the HirId of the Upvar that was not found.
fn to_upvars_resolved_place_builder<'a, 'tcx>(
from_builder: PlaceBuilder<'tcx>,
tcx: TyCtxt<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
) -> Result<PlaceBuilder<'tcx>, HirId> {
match from_builder.base {
PlaceBase::Local(_) => Ok(from_builder),
PlaceBase::Upvar { var_hir_id, closure_def_id, closure_kind } => {
// Captures are represented using fields inside a structure.
// This represents accessing self in the closure structure
let mut upvar_resolved_place_builder = PlaceBuilder::from(Local::new(1));
match closure_kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
upvar_resolved_place_builder = upvar_resolved_place_builder.deref();
}
ty::ClosureKind::FnOnce => {}
}
let (capture_index, capture) =
if let Some(capture_details) = find_capture_matching_projections(
typeck_results,
var_hir_id,
closure_def_id,
&from_builder.projection,
) {
capture_details
} else {
if !tcx.features().capture_disjoint_fields {
bug!(
"No associated capture found for {:?}[{:#?}] even though \
capture_disjoint_fields isn't enabled",
var_hir_id,
from_builder.projection
)
} else {
// FIXME(project-rfc-2229#24): Handle this case properly
debug!(
"No associated capture found for {:?}[{:#?}]",
var_hir_id,
from_builder.projection,
);
}
return Err(var_hir_id);
};
let closure_ty =
typeck_results.node_type(tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()));
let substs = match closure_ty.kind() {
ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
_ => bug!("Lowering capture for non-closure type {:?}", closure_ty),
};
// Access the capture by accessing the field within the Closure struct.
//
// We must have inferred the capture types since we are building MIR, therefore
// it's safe to call `tuple_element_ty` and we can unwrap here because
// we know that the capture exists and is the `capture_index`-th capture.
let var_ty = substs.tupled_upvars_ty().tuple_element_ty(capture_index).unwrap();
upvar_resolved_place_builder = upvar_resolved_place_builder.field(Field::new(capture_index), var_ty);
// If the variable is captured via ByRef(Immutable/Mutable) Borrow,
// we need to deref it
upvar_resolved_place_builder = match capture.info.capture_kind {
ty::UpvarCapture::ByRef(_) => upvar_resolved_place_builder.deref(),
ty::UpvarCapture::ByValue(_) => upvar_resolved_place_builder,
};
let next_projection = capture.place.projections.len();
let mut curr_projections = from_builder.projection;
// We used some of the projections to build the capture itself,
// now we apply the remaining to the upvar resolved place.
upvar_resolved_place_builder.projection.extend(
curr_projections.drain(next_projection..));
Ok(upvar_resolved_place_builder)
}
}
}
impl<'tcx> PlaceBuilder<'tcx> {
fn into_place(self, tcx: TyCtxt<'tcx>) -> Place<'tcx> {
Place { local: self.local, projection: tcx.intern_place_elems(&self.projection) }
crate fn into_place<'a>(
self,
tcx: TyCtxt<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
) -> Place<'tcx> {
if let PlaceBase::Local(local) = self.base {
Place { local, projection: tcx.intern_place_elems(&self.projection) }
} else {
self.expect_upvars_resolved(tcx, typeck_results).into_place(tcx, typeck_results)
}
}
fn expect_upvars_resolved<'a>(
self,
tcx: TyCtxt<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
) -> PlaceBuilder<'tcx> {
to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap()
}
crate fn base(&self) -> PlaceBase {
self.base
}
fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
@ -49,7 +323,13 @@ impl<'tcx> PlaceBuilder<'tcx> {
impl<'tcx> From<Local> for PlaceBuilder<'tcx> {
fn from(local: Local) -> Self {
Self { local, projection: Vec::new() }
Self { base: PlaceBase::Local(local), projection: Vec::new() }
}
}
impl<'tcx> From<PlaceBase> for PlaceBuilder<'tcx> {
fn from(base: PlaceBase) -> Self {
Self { base, projection: Vec::new() }
}
}
@ -71,12 +351,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
M: Mirror<'tcx, Output = Expr<'tcx>>,
{
let place_builder = unpack!(block = self.as_place_builder(block, expr));
block.and(place_builder.into_place(self.hir.tcx()))
block.and(place_builder.into_place(self.hir.tcx(), self.hir.typeck_results()))
}
/// This is used when constructing a compound `Place`, so that we can avoid creating
/// intermediate `Place` values until we know the full set of projections.
fn as_place_builder<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<PlaceBuilder<'tcx>>
crate fn as_place_builder<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<PlaceBuilder<'tcx>>
where
M: Mirror<'tcx, Output = Expr<'tcx>>,
{
@ -98,7 +378,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
M: Mirror<'tcx, Output = Expr<'tcx>>,
{
let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
block.and(place_builder.into_place(self.hir.tcx()))
block.and(place_builder.into_place(self.hir.tcx(), self.hir.typeck_results()))
}
/// This is used when constructing a compound `Place`, so that we can avoid creating
@ -161,27 +441,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
source_info,
),
ExprKind::UpvarRef { closure_def_id, var_hir_id } => {
let capture = this
.hir
.typeck_results
.closure_captures
.get(&closure_def_id)
.and_then(|captures| captures.get_full(&var_hir_id));
if capture.is_none() {
if !this.hir.tcx().features().capture_disjoint_fields {
bug!(
"No associated capture found for {:?} even though \
capture_disjoint_fields isn't enabled",
expr.kind
)
}
// FIXME(project-rfc-2229#24): Handle this case properly
}
// Unwrap until the FIXME has been resolved
let (capture_index, _, upvar_id) = capture.unwrap();
this.lower_closure_capture(block, capture_index, *upvar_id)
let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id.expect_local());
this.lower_captured_upvar(block, upvar_id)
}
ExprKind::VarRef { id } => {
@ -208,7 +469,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
inferred_ty: expr.ty,
});
let place = place_builder.clone().into_place(this.hir.tcx());
let place =
place_builder.clone().into_place(this.hir.tcx(), this.hir.typeck_results());
this.cfg.push(
block,
Statement {
@ -293,59 +555,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
/// Lower a closure/generator capture by representing it as a field
/// access within the desugared closure/generator.
///
/// `capture_index` is the index of the capture within the desugared
/// closure/generator.
fn lower_closure_capture(
/// Lower a captured upvar. Note we might not know the actual capture index,
/// so we create a place starting from `PlaceBase::Upvar`, which will be resolved
/// once all projections that allow us to indentify a capture have been applied.
fn lower_captured_upvar(
&mut self,
block: BasicBlock,
capture_index: usize,
upvar_id: ty::UpvarId,
) -> BlockAnd<PlaceBuilder<'tcx>> {
) -> BlockAnd<PlaceBuilder<'tcx>> {
let closure_ty = self
.hir
.typeck_results()
.node_type(self.hir.tcx().hir().local_def_id_to_hir_id(upvar_id.closure_expr_id));
// Captures are represented using fields inside a structure.
// This represents accessing self in the closure structure
let mut place_builder = PlaceBuilder::from(Local::new(1));
// In case of Fn/FnMut closures we must deref to access the fields
// Generators are considered FnOnce, so we ignore this step for them.
if let ty::Closure(_, closure_substs) = closure_ty.kind() {
match self.hir.infcx().closure_kind(closure_substs).unwrap() {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
place_builder = place_builder.deref();
}
ty::ClosureKind::FnOnce => {}
}
}
let substs = match closure_ty.kind() {
ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
_ => bug!("Lowering capture for non-closure type {:?}", closure_ty)
let closure_kind = if let ty::Closure(_, closure_substs) = closure_ty.kind() {
self.hir.infcx().closure_kind(closure_substs).unwrap()
} else {
// Generators are considered FnOnce.
ty::ClosureKind::FnOnce
};
// Access the capture by accessing the field within the Closure struct.
//
// We must have inferred the capture types since we are building MIR, therefore
// it's safe to call `upvar_tys` and we can unwrap here because
// we know that the capture exists and is the `capture_index`-th capture.
let var_ty = substs.upvar_tys().nth(capture_index).unwrap();
place_builder = place_builder.field(Field::new(capture_index), var_ty);
// If the variable is captured via ByRef(Immutable/Mutable) Borrow,
// we need to deref it
match self.hir.typeck_results.upvar_capture(upvar_id) {
ty::UpvarCapture::ByRef(_) => {
block.and(place_builder.deref())
}
ty::UpvarCapture::ByValue(_) => block.and(place_builder),
}
block.and(PlaceBuilder::from(PlaceBase::Upvar {
var_hir_id: upvar_id.var_path.hir_id,
closure_def_id: upvar_id.closure_expr_id.to_def_id(),
closure_kind,
}))
}
/// Lower an index expression
@ -373,7 +607,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let is_outermost_index = fake_borrow_temps.is_none();
let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps);
let base_place =
let mut base_place =
unpack!(block = self.expr_as_place(block, lhs, mutability, Some(fake_borrow_temps),));
// Making this a *fresh* temporary means we do not have to worry about
@ -383,7 +617,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = self.bounds_check(
block,
base_place.clone().into_place(self.hir.tcx()),
base_place.clone().into_place(self.hir.tcx(), self.hir.typeck_results()),
idx,
expr_span,
source_info,
@ -392,6 +626,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if is_outermost_index {
self.read_fake_borrows(block, fake_borrow_temps, source_info)
} else {
base_place = base_place.expect_upvars_resolved(self.hir.tcx(), self.hir.typeck_results());
self.add_fake_borrows_of_base(
&base_place,
block,
@ -441,8 +676,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
source_info: SourceInfo,
) {
let tcx = self.hir.tcx();
let place_ty =
Place::ty_from(base_place.local, &base_place.projection, &self.local_decls, tcx);
let local = match base_place.base {
PlaceBase::Local(local) => local,
PlaceBase::Upvar { .. } => bug!("Expected PlacseBase::Local found Upvar")
};
let place_ty = Place::ty_from(local, &base_place.projection, &self.local_decls, tcx);
if let ty::Slice(_) = place_ty.ty.kind() {
// We need to create fake borrows to ensure that the bounds
// check that we just did stays valid. Since we can't assign to
@ -452,7 +691,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match elem {
ProjectionElem::Deref => {
let fake_borrow_deref_ty = Place::ty_from(
base_place.local,
local,
&base_place.projection[..idx],
&self.local_decls,
tcx,
@ -470,14 +709,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Rvalue::Ref(
tcx.lifetimes.re_erased,
BorrowKind::Shallow,
Place { local: base_place.local, projection },
Place { local, projection },
),
);
fake_borrow_temps.push(fake_borrow_temp);
}
ProjectionElem::Index(_) => {
let index_ty = Place::ty_from(
base_place.local,
local,
&base_place.projection[..idx],
&self.local_decls,
tcx,

View File

@ -4,6 +4,7 @@ use rustc_index::vec::Idx;
use crate::build::expr::category::{Category, RvalueFunc};
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::build::expr::as_place::PlaceBase;
use crate::thir::*;
use rustc_middle::middle::region;
use rustc_middle::mir::AssertKind;
@ -393,44 +394,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
let arg_place = unpack!(block = this.as_place(block, arg));
let arg_place_builder = unpack!(block = this.as_place_builder(block, arg));
let mutability = match arg_place.as_ref() {
PlaceRef { local, projection: &[] } => this.local_decls[local].mutability,
PlaceRef { local, projection: &[ProjectionElem::Deref] } => {
debug_assert!(
this.local_decls[local].is_ref_for_guard(),
"Unexpected capture place",
);
this.local_decls[local].mutability
}
PlaceRef {
local,
projection: &[ref proj_base @ .., ProjectionElem::Field(upvar_index, _)],
}
| PlaceRef {
local,
projection:
&[ref proj_base @ .., ProjectionElem::Field(upvar_index, _), ProjectionElem::Deref],
} => {
let place = PlaceRef { local, projection: proj_base };
let mutability = match arg_place_builder.base() {
// We are capturing a path that starts off a local variable in the parent.
// The mutability of the current capture is same as the mutability
// of the local declaration in the parent.
PlaceBase::Local(local) => this.local_decls[local].mutability,
// Parent is a closure and we are capturing a path that is captured
// by the parent itself. The mutability of the current capture
// is same as that of the capture in the parent closure.
PlaceBase::Upvar { .. } => {
let enclosing_upvars_resolved = arg_place_builder.clone().into_place(
this.hir.tcx(),
this.hir.typeck_results());
// Not projected from the implicit `self` in a closure.
debug_assert!(
match place.local_or_deref_local() {
Some(local) => local == Local::new(1),
None => false,
},
"Unexpected capture place"
);
// Not in a closure
debug_assert!(
this.upvar_mutbls.len() > upvar_index.index(),
"Unexpected capture place"
);
this.upvar_mutbls[upvar_index.index()]
match enclosing_upvars_resolved.as_ref() {
PlaceRef { local, projection: &[ProjectionElem::Field(upvar_index, _), ..] }
| PlaceRef {
local,
projection: &[ProjectionElem::Deref, ProjectionElem::Field(upvar_index, _), ..] } => {
// Not in a closure
debug_assert!(
local == Local::new(1),
"Expected local to be Local(1), found {:?}",
local
);
// Not in a closure
debug_assert!(
this.upvar_mutbls.len() > upvar_index.index(),
"Unexpected capture place, upvar_mutbls={:#?}, upvar_index={:?}",
this.upvar_mutbls, upvar_index
);
this.upvar_mutbls[upvar_index.index()]
}
_ => bug!("Unexpected capture place"),
}
}
_ => bug!("Unexpected capture place"),
};
let borrow_kind = match mutability {
@ -438,6 +438,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
};
let arg_place = arg_place_builder.into_place(
this.hir.tcx(),
this.hir.typeck_results());
this.cfg.push_assign(
block,
source_info,

View File

@ -10,6 +10,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_hir::{GeneratorKind, HirIdMap, Node};
use rustc_index::vec::{Idx, IndexVec};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_middle::ty::subst::Subst;
@ -823,7 +824,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// with the closure's DefId. Here, we run through that vec of UpvarIds for
// the given closure and use the necessary information to create upvar
// debuginfo and to fill `self.upvar_mutbls`.
if let Some(upvars) = hir_typeck_results.closure_captures.get(&fn_def_id) {
if hir_typeck_results.closure_min_captures.get(&fn_def_id).is_some() {
let closure_env_arg = Local::new(1);
let mut closure_env_projs = vec![];
let mut closure_ty = self.local_decls[closure_env_arg].ty;
@ -836,14 +837,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
_ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty),
};
let upvar_tys = upvar_substs.upvar_tys();
let upvars_with_tys = upvars.iter().zip(upvar_tys);
self.upvar_mutbls = upvars_with_tys
let capture_tys = upvar_substs.upvar_tys();
let captures_with_tys = hir_typeck_results
.closure_min_captures_flattened(fn_def_id)
.zip(capture_tys);
self.upvar_mutbls = captures_with_tys
.enumerate()
.map(|(i, ((&var_id, &upvar_id), ty))| {
let capture = hir_typeck_results.upvar_capture(upvar_id);
.map(|(i, (captured_place, ty))| {
let capture = captured_place.info.capture_kind;
let var_id = match captured_place.place.base {
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
_ => bug!("Expected an upvar")
};
let mut mutability = Mutability::Not;
// FIXME(project-rfc-2229#8): Store more precise information
let mut name = kw::Invalid;
if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {

View File

@ -6,6 +6,8 @@ use crate::thir::*;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_index::vec::Idx;
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::BorrowKind;
use rustc_middle::ty::adjustment::{
@ -386,14 +388,12 @@ fn make_mirror_unadjusted<'a, 'tcx>(
span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty);
}
};
let upvars = cx
.typeck_results()
.closure_captures
.get(&def_id)
.iter()
.flat_map(|upvars| upvars.iter())
.closure_min_captures_flattened(def_id)
.zip(substs.upvar_tys())
.map(|((&var_hir_id, _), ty)| capture_upvar(cx, expr, var_hir_id, ty))
.map(|(captured_place, ty)| capture_upvar(cx, expr, captured_place, ty))
.collect();
ExprKind::Closure { closure_id: def_id, substs, upvars, movability }
}
@ -981,27 +981,55 @@ fn overloaded_place<'a, 'tcx>(
ExprKind::Deref { arg: ref_expr.to_ref() }
}
fn capture_upvar<'tcx>(
fn capture_upvar<'a, 'tcx>(
cx: &mut Cx<'_, 'tcx>,
closure_expr: &'tcx hir::Expr<'tcx>,
var_hir_id: hir::HirId,
captured_place: &'a ty::CapturedPlace<'tcx>,
upvar_ty: Ty<'tcx>,
) -> ExprRef<'tcx> {
let upvar_id = ty::UpvarId {
var_path: ty::UpvarPath { hir_id: var_hir_id },
closure_expr_id: cx.tcx.hir().local_def_id(closure_expr.hir_id),
};
let upvar_capture = cx.typeck_results().upvar_capture(upvar_id);
let upvar_capture = captured_place.info.capture_kind;
let temp_lifetime = cx.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
let var_ty = cx.typeck_results().node_type(var_hir_id);
let captured_var = Expr {
let var_ty = captured_place.place.base_ty;
// The result of capture analysis in `rustc_typeck/check/upvar.rs`represents a captured path
// as it's seen for use within the closure and not at the time of closure creation.
//
// That is we see expect to see it start from a captured upvar and not something that is local
// to the closure's parent.
let var_hir_id = match captured_place.place.base {
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
base => bug!("Expected an upvar, found {:?}", base),
};
let mut captured_place_expr = Expr {
temp_lifetime,
ty: var_ty,
span: closure_expr.span,
kind: convert_var(cx, var_hir_id),
};
for proj in captured_place.place.projections.iter() {
let kind = match proj.kind {
HirProjectionKind::Deref => ExprKind::Deref { arg: captured_place_expr.to_ref() },
HirProjectionKind::Field(field, ..) => {
// Variant index will always be 0, because for multi-variant
// enums, we capture the enum entirely.
ExprKind::Field {
lhs: captured_place_expr.to_ref(),
name: Field::new(field as usize),
}
}
HirProjectionKind::Index | HirProjectionKind::Subslice => {
// We don't capture these projections, so we can ignore them here
continue;
}
};
captured_place_expr = Expr { temp_lifetime, ty: proj.ty, span: closure_expr.span, kind };
}
match upvar_capture {
ty::UpvarCapture::ByValue(_) => captured_var.to_ref(),
ty::UpvarCapture::ByValue(_) => captured_place_expr.to_ref(),
ty::UpvarCapture::ByRef(upvar_borrow) => {
let borrow_kind = match upvar_borrow.kind {
ty::BorrowKind::ImmBorrow => BorrowKind::Shared,
@ -1012,7 +1040,7 @@ fn capture_upvar<'tcx>(
temp_lifetime,
ty: upvar_ty,
span: closure_expr.span,
kind: ExprKind::Borrow { borrow_kind, arg: captured_var.to_ref() },
kind: ExprKind::Borrow { borrow_kind, arg: captured_place_expr.to_ref() },
}
.to_ref()
}

View File

@ -202,7 +202,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// inference algorithm will reject it).
// Equate the type variables for the upvars with the actual types.
let final_upvar_tys = self.final_upvar_tys(closure_hir_id);
let final_upvar_tys = self.final_upvar_tys(closure_def_id);
debug!(
"analyze_closure: id={:?} substs={:?} final_upvar_tys={:?}",
closure_hir_id, substs, final_upvar_tys
@ -222,36 +222,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
// Returns a list of `Ty`s for each upvar.
fn final_upvar_tys(&self, closure_id: hir::HirId) -> Vec<Ty<'tcx>> {
fn final_upvar_tys(&self, closure_id: DefId) -> Vec<Ty<'tcx>> {
// Presently an unboxed closure type cannot "escape" out of a
// function, so we will only encounter ones that originated in the
// local crate or were inlined into it along with some function.
// This may change if abstract return types of some sort are
// implemented.
let tcx = self.tcx;
let closure_def_id = tcx.hir().local_def_id(closure_id);
self.typeck_results
.borrow()
.closure_captures
.get(&closure_def_id.to_def_id())
.iter()
.flat_map(|upvars| {
upvars.iter().map(|(&var_hir_id, _)| {
let upvar_ty = self.node_ty(var_hir_id);
let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id);
let capture = self.typeck_results.borrow().upvar_capture(upvar_id);
.closure_min_captures_flattened(closure_id)
.map(|captured_place| {
let upvar_ty = captured_place.place.ty();
let capture = captured_place.info.capture_kind;
debug!("var_id={:?} upvar_ty={:?} capture={:?}", var_hir_id, upvar_ty, capture);
debug!(
"place={:?} upvar_ty={:?} capture={:?}",
captured_place.place, upvar_ty, capture
);
match capture {
ty::UpvarCapture::ByValue(_) => upvar_ty,
ty::UpvarCapture::ByRef(borrow) => tcx.mk_ref(
borrow.region,
ty::TypeAndMut { ty: upvar_ty, mutbl: borrow.kind.to_mutbl_lossy() },
),
}
})
match capture {
ty::UpvarCapture::ByValue(_) => upvar_ty,
ty::UpvarCapture::ByRef(borrow) => tcx.mk_ref(
borrow.region,
ty::TypeAndMut { ty: upvar_ty, mutbl: borrow.kind.to_mutbl_lossy() },
),
}
})
.collect()
}

View File

@ -55,6 +55,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => (),
}
wbcx.visit_body(body);
wbcx.visit_min_capture_map();
wbcx.visit_upvar_capture_map();
wbcx.visit_closures();
wbcx.visit_liberated_fn_sigs();
@ -331,6 +332,37 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
}
impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
fn visit_min_capture_map(&mut self) {
let mut min_captures_wb = ty::MinCaptureInformationMap::with_capacity_and_hasher(
self.fcx.typeck_results.borrow().closure_min_captures.len(),
Default::default(),
);
for (closure_def_id, root_min_captures) in
self.fcx.typeck_results.borrow().closure_min_captures.iter()
{
let mut root_var_map_wb = ty::RootVariableMinCaptureList::with_capacity_and_hasher(
root_min_captures.len(),
Default::default(),
);
for (var_hir_id, min_list) in root_min_captures.iter() {
let min_list_wb = min_list
.iter()
.map(|captured_place| {
let locatable = captured_place.info.expr_id.unwrap_or(
self.tcx().hir().local_def_id_to_hir_id(closure_def_id.expect_local()),
);
self.resolve(captured_place.clone(), &locatable)
})
.collect();
root_var_map_wb.insert(*var_hir_id, min_list_wb);
}
min_captures_wb.insert(*closure_def_id, root_var_map_wb);
}
self.typeck_results.closure_min_captures = min_captures_wb;
}
fn visit_upvar_capture_map(&mut self) {
for (upvar_id, upvar_capture) in self.fcx.typeck_results.borrow().upvar_capture_map.iter() {
let new_upvar_capture = match *upvar_capture {

View File

@ -15,7 +15,6 @@ use rustc_index::vec::Idx;
use rustc_infer::infer::InferCtxt;
use rustc_middle::hir::place::ProjectionKind;
use rustc_middle::ty::{self, adjustment, TyCtxt};
use rustc_span::Span;
use rustc_target::abi::VariantIdx;
use crate::mem_categorization as mc;
@ -571,38 +570,6 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
}));
}
/// Walk closure captures but using `closure_caputes` instead
/// 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).to_def_id();
let cl_span = self.tcx().hir().span(closure_expr.hir_id);
let captures = &self.mc.typeck_results.closure_captures[&closure_def_id];
for (&var_id, &upvar_id) in captures {
let upvar_capture = self.mc.typeck_results.upvar_capture(upvar_id);
let captured_place =
return_if_err!(self.cat_captured_var(closure_expr.hir_id, cl_span, var_id));
match upvar_capture {
ty::UpvarCapture::ByValue(_) => {
let mode = copy_or_move(&self.mc, &captured_place);
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
@ -646,16 +613,18 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
let place = &captured_place.place;
let capture_info = captured_place.info;
let upvar_id = if body_owner_is_closure {
let place_base = if body_owner_is_closure {
// Mark the place to be captured by the enclosing closure
ty::UpvarId::new(*var_hir_id, self.body_owner)
PlaceBase::Upvar(ty::UpvarId::new(*var_hir_id, self.body_owner))
} else {
ty::UpvarId::new(*var_hir_id, closure_def_id.expect_local())
// If the body owner isn't a closure then the variable must
// be a local variable
PlaceBase::Local(*var_hir_id)
};
let place_with_id = PlaceWithHirId::new(
capture_info.expr_id.unwrap_or(closure_expr.hir_id),
place.base_ty,
PlaceBase::Upvar(upvar_id),
place_base,
place.projections.clone(),
);
@ -674,23 +643,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
}
}
}
} 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(
&mut self,
closure_hir_id: hir::HirId,
closure_span: Span,
var_id: hir::HirId,
) -> mc::McResult<PlaceWithHirId<'tcx>> {
// Create the place for the variable being borrowed, from the
// perspective of the creator (parent) of the closure.
let var_ty = self.mc.node_ty(var_id)?;
self.mc.cat_res(closure_hir_id, closure_span, var_ty, Res::Local(var_id))
}
}
fn copy_or_move<'a, 'tcx>(

View File

@ -0,0 +1,86 @@
// Test that arrays are completely captured by closures by relying on the borrow check diagnostics
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
fn arrays_1() {
let mut arr = [1, 2, 3, 4, 5];
let mut c = || {
arr[0] += 10;
};
// c will capture `arr` completely, therefore another index into the
// array can't be modified here
arr[1] += 10;
//~^ ERROR: cannot use `arr` because it was mutably borrowed
//~| ERROR: cannot use `arr[_]` because it was mutably borrowed
c();
}
fn arrays_2() {
let mut arr = [1, 2, 3, 4, 5];
let c = || {
println!("{:#?}", &arr[3..4]);
};
// c will capture `arr` completely, therefore another index into the
// array can't be modified here
arr[1] += 10;
//~^ ERROR: cannot assign to `arr[_]` because it is borrowed
c();
}
fn arrays_3() {
let mut arr = [1, 2, 3, 4, 5];
let c = || {
println!("{}", arr[3]);
};
// c will capture `arr` completely, therefore another index into the
// array can't be modified here
arr[1] += 10;
//~^ ERROR: cannot assign to `arr[_]` because it is borrowed
c();
}
fn arrays_4() {
let mut arr = [1, 2, 3, 4, 5];
let mut c = || {
arr[1] += 10;
};
// c will capture `arr` completely, therefore we cannot borrow another index
// into the array.
println!("{}", arr[3]);
//~^ ERROR: cannot use `arr` because it was mutably borrowed
//~| ERROR: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable
c();
}
fn arrays_5() {
let mut arr = [1, 2, 3, 4, 5];
let mut c = || {
arr[1] += 10;
};
// c will capture `arr` completely, therefore we cannot borrow other indecies
// into the array.
println!("{:#?}", &arr[3..2]);
//~^ ERROR: cannot borrow `arr` as immutable because it is also borrowed as mutable
c();
}
fn main() {
arrays_1();
arrays_2();
arrays_3();
arrays_4();
arrays_5();
}

View File

@ -0,0 +1,111 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/arrays.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[E0503]: cannot use `arr` because it was mutably borrowed
--> $DIR/arrays.rs:15:5
|
LL | let mut c = || {
| -- borrow of `arr` occurs here
LL | arr[0] += 10;
| --- borrow occurs due to use of `arr` in closure
...
LL | arr[1] += 10;
| ^^^^^^ use of borrowed `arr`
...
LL | c();
| - borrow later used here
error[E0503]: cannot use `arr[_]` because it was mutably borrowed
--> $DIR/arrays.rs:15:5
|
LL | let mut c = || {
| -- borrow of `arr` occurs here
LL | arr[0] += 10;
| --- borrow occurs due to use of `arr` in closure
...
LL | arr[1] += 10;
| ^^^^^^^^^^^^ use of borrowed `arr`
...
LL | c();
| - borrow later used here
error[E0506]: cannot assign to `arr[_]` because it is borrowed
--> $DIR/arrays.rs:30:5
|
LL | let c = || {
| -- borrow of `arr[_]` occurs here
LL | println!("{:#?}", &arr[3..4]);
| --- borrow occurs due to use in closure
...
LL | arr[1] += 10;
| ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here
LL |
LL | c();
| - borrow later used here
error[E0506]: cannot assign to `arr[_]` because it is borrowed
--> $DIR/arrays.rs:44:5
|
LL | let c = || {
| -- borrow of `arr[_]` occurs here
LL | println!("{}", arr[3]);
| --- borrow occurs due to use in closure
...
LL | arr[1] += 10;
| ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here
LL |
LL | c();
| - borrow later used here
error[E0503]: cannot use `arr` because it was mutably borrowed
--> $DIR/arrays.rs:58:20
|
LL | let mut c = || {
| -- borrow of `arr` occurs here
LL | arr[1] += 10;
| --- borrow occurs due to use of `arr` in closure
...
LL | println!("{}", arr[3]);
| ^^^^^^ use of borrowed `arr`
...
LL | c();
| - borrow later used here
error[E0502]: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable
--> $DIR/arrays.rs:58:20
|
LL | let mut c = || {
| -- mutable borrow occurs here
LL | arr[1] += 10;
| --- first borrow occurs due to use of `arr` in closure
...
LL | println!("{}", arr[3]);
| ^^^^^^ immutable borrow occurs here
...
LL | c();
| - mutable borrow later used here
error[E0502]: cannot borrow `arr` as immutable because it is also borrowed as mutable
--> $DIR/arrays.rs:74:24
|
LL | let mut c = || {
| -- mutable borrow occurs here
LL | arr[1] += 10;
| --- first borrow occurs due to use of `arr` in closure
...
LL | println!("{:#?}", &arr[3..2]);
| ^^^ immutable borrow occurs here
...
LL | c();
| - mutable borrow later used here
error: aborting due to 7 previous errors; 1 warning emitted
Some errors have detailed explanations: E0502, E0503, E0506.
For more information about an error, try `rustc --explain E0502`.

View File

@ -0,0 +1,65 @@
// Test borrow checker when we precise capture when using boxes
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
struct MetaData { x: String, name: String }
struct Data { m: MetaData }
struct BoxedData(Box<Data>);
struct EvenMoreBoxedData(Box<BoxedData>);
// Check diagnostics when the same path is mutated both inside and outside the closure
fn box_1() {
let m = MetaData { x: format!("x"), name: format!("name") };
let d = Data { m };
let b = BoxedData(Box::new(d));
let mut e = EvenMoreBoxedData(Box::new(b));
let mut c = || {
e.0.0.m.x = format!("not-x");
};
e.0.0.m.x = format!("not-x");
//~^ ERROR: cannot assign to `e.0.0.m.x` because it is borrowed
c();
}
// Check diagnostics when a path is mutated inside a closure while attempting to read it outside
// the closure.
fn box_2() {
let m = MetaData { x: format!("x"), name: format!("name") };
let d = Data { m };
let b = BoxedData(Box::new(d));
let mut e = EvenMoreBoxedData(Box::new(b));
let mut c = || {
e.0.0.m.x = format!("not-x");
};
println!("{}", e.0.0.m.x);
//~^ ERROR: cannot borrow `e.0.0.m.x` as immutable because it is also borrowed as mutable
c();
}
// Check diagnostics when a path is read inside a closure while attempting to mutate it outside
// the closure.
fn box_3() {
let m = MetaData { x: format!("x"), name: format!("name") };
let d = Data { m };
let b = BoxedData(Box::new(d));
let mut e = EvenMoreBoxedData(Box::new(b));
let c = || {
println!("{}", e.0.0.m.x);
};
e.0.0.m.x = format!("not-x");
//~^ ERROR: cannot assign to `e.0.0.m.x` because it is borrowed
c();
}
fn main() {
box_1();
box_2();
box_3();
}

View File

@ -0,0 +1,55 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/box.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[E0506]: cannot assign to `e.0.0.m.x` because it is borrowed
--> $DIR/box.rs:22:5
|
LL | let mut c = || {
| -- borrow of `e.0.0.m.x` occurs here
LL | e.0.0.m.x = format!("not-x");
| - borrow occurs due to use in closure
...
LL | e.0.0.m.x = format!("not-x");
| ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here
LL |
LL | c();
| - borrow later used here
error[E0502]: cannot borrow `e.0.0.m.x` as immutable because it is also borrowed as mutable
--> $DIR/box.rs:39:20
|
LL | let mut c = || {
| -- mutable borrow occurs here
LL | e.0.0.m.x = format!("not-x");
| - first borrow occurs due to use of `e.0.0.m.x` in closure
...
LL | println!("{}", e.0.0.m.x);
| ^^^^^^^^^ immutable borrow occurs here
LL |
LL | c();
| - mutable borrow later used here
error[E0506]: cannot assign to `e.0.0.m.x` because it is borrowed
--> $DIR/box.rs:56:5
|
LL | let c = || {
| -- borrow of `e.0.0.m.x` occurs here
LL | println!("{}", e.0.0.m.x);
| - borrow occurs due to use in closure
...
LL | e.0.0.m.x = format!("not-x");
| ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here
LL |
LL | c();
| - borrow later used here
error: aborting due to 3 previous errors; 1 warning emitted
Some errors have detailed explanations: E0502, E0506.
For more information about an error, try `rustc --explain E0502`.

View File

@ -0,0 +1,28 @@
// Test that when a borrow checker diagnostics are emitted, it's as precise
// as the capture by the closure.
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
#![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 mut c = || {
w.p.x += 20;
};
let py = &mut w.p.x;
//~^ ERROR: cannot borrow `w.p.x` as mutable more than once at a time
c();
*py = 20
}

View File

@ -0,0 +1,26 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/multilevel-path.rs:4: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[E0499]: cannot borrow `w.p.x` as mutable more than once at a time
--> $DIR/multilevel-path.rs:23:14
|
LL | let mut c = || {
| -- first mutable borrow occurs here
LL | w.p.x += 20;
| - first borrow occurs due to use of `w.p.x` in closure
...
LL | let py = &mut w.p.x;
| ^^^^^^^^^^ second mutable borrow occurs here
LL |
LL | c();
| - first borrow later used here
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0499`.

View File

@ -0,0 +1,26 @@
// Test that borrow checker error is accurate and that min capture pass of the
// closure analysis is working as expected.
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut p = Point { x: 10, y: 20 };
// `p` is captured via mutable borrow.
let mut c = || {
p.x += 10;
println!("{:?}", p);
};
println!("{:?}", p);
//~^ ERROR: cannot borrow `p` as immutable because it is also borrowed as mutable
c();
}

View File

@ -0,0 +1,26 @@
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:4: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[E0502]: cannot borrow `p` as immutable because it is also borrowed as mutable
--> $DIR/simple-struct-min-capture.rs:23:22
|
LL | let mut c = || {
| -- mutable borrow occurs here
LL | p.x += 10;
| - first borrow occurs due to use of `p` in closure
...
LL | println!("{:?}", p);
| ^ immutable borrow occurs here
LL |
LL | c();
| - mutable borrow later used here
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0502`.

View File

@ -0,0 +1,97 @@
// run-pass
// Test precise capture when using boxes
#![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>
struct MetaData { x: String, name: String }
struct Data { m: MetaData }
struct BoxedData(Box<Data>);
struct EvenMoreBoxedData(Box<BoxedData>);
// Mutate disjoint paths, one inside one outside the closure
fn box_1() {
let m = MetaData { x: format!("x"), name: format!("name") };
let d = Data { m };
let b = BoxedData(Box::new(d));
let mut e = EvenMoreBoxedData(Box::new(b));
let mut c = || {
e.0.0.m.x = format!("not-x");
};
e.0.0.m.name = format!("not-name");
c();
}
// Mutate a path inside the closure and read a disjoint path outside the closure
fn box_2() {
let m = MetaData { x: format!("x"), name: format!("name") };
let d = Data { m };
let b = BoxedData(Box::new(d));
let mut e = EvenMoreBoxedData(Box::new(b));
let mut c = || {
e.0.0.m.x = format!("not-x");
};
println!("{}", e.0.0.m.name);
c();
}
// Read a path inside the closure and mutate a disjoint path outside the closure
fn box_3() {
let m = MetaData { x: format!("x"), name: format!("name") };
let d = Data { m };
let b = BoxedData(Box::new(d));
let mut e = EvenMoreBoxedData(Box::new(b));
let c = || {
println!("{}", e.0.0.m.name);
};
e.0.0.m.x = format!("not-x");
c();
}
// Read disjoint paths, one inside the closure and one outside the closure.
fn box_4() {
let m = MetaData { x: format!("x"), name: format!("name") };
let d = Data { m };
let b = BoxedData(Box::new(d));
let e = EvenMoreBoxedData(Box::new(b));
let c = || {
println!("{}", e.0.0.m.name);
};
println!("{}", e.0.0.m.x);
c();
}
// Read the same path, once inside the closure and once outside the closure.
fn box_5() {
let m = MetaData { x: format!("x"), name: format!("name") };
let d = Data { m };
let b = BoxedData(Box::new(d));
let e = EvenMoreBoxedData(Box::new(b));
let c = || {
println!("{}", e.0.0.m.name);
};
println!("{}", e.0.0.m.name);
c();
}
fn main() {
box_1();
box_2();
box_3();
box_4();
box_5();
}

View File

@ -0,0 +1,11 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/box.rs:5: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
warning: 1 warning emitted

View File

@ -0,0 +1,28 @@
// run-pass
// Test that we can immutably borrow field of an instance of a structure from within a closure,
// while having a mutable borrow to another field of the same instance outside the closure.
#![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>
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut p = Point { x: 10, y: 10 };
let c = || {
println!("{}", p.x);
};
// `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,11 @@
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:6: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
warning: 1 warning emitted

View File

@ -0,0 +1,23 @@
// run-pass
// Test that we can mutate an element of a tuple from within a closure
// while immutably borrowing another element of the same tuple outside the closure.
#![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 mut c = || {
let t1 = &mut t.1;
*t1 = 20;
};
// Test that `c` only captures t.1, therefore reading t.0 is allowed.
println!("{}", t.0);
c();
}

View File

@ -0,0 +1,11 @@
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-mut.rs:6: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
warning: 1 warning emitted

View File

@ -0,0 +1,24 @@
// run-pass
// Test that we can immutably borrow an element of a tuple from within a closure,
// while having a mutable borrow to another element of the same tuple outside the closure.
#![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 = || {
println!("{}", t.0);
};
// `c` only captures t.0, therefore mutating t.1 is allowed.
let t1 = &mut t.1;
c();
*t1 = 20;
}

View File

@ -0,0 +1,11 @@
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:6: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
warning: 1 warning emitted

View File

@ -0,0 +1,27 @@
// run-pass
#![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>
// Tests that if a closure uses indivual fields of the same object
// then that case is handled properly.
#![allow(unused)]
struct Struct {
x: i32,
y: i32,
s: String,
}
fn main() {
let mut s = Struct { x: 10, y: 10, s: String::new() };
let mut c = {
s.x += 10;
s.y += 42;
s.s = String::from("new");
};
}

View File

@ -0,0 +1,11 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/disjoint-capture-in-same-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
warning: 1 warning emitted

View File

@ -0,0 +1,41 @@
// run-pass
// Test disjoint capture within an impl block
#![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>
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(
|v| self.filter.allowed(*v),
);
}
}
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,11 @@
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:5: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
warning: 1 warning emitted

View File

@ -0,0 +1,36 @@
// run-pass
// Test that closures can catpure paths that are more precise than just one level
// from the root variable.
//
// If the closures can handle such precison we should be able to mutate one path in the closure
// while being able to mutate another path outside the closure, where the two paths are disjoint
// after applying two projections on the root variable.
#![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>
#![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 mut c = || {
w.p.x += 20;
};
// `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,11 @@
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:10: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
warning: 1 warning emitted

View File

@ -0,0 +1,34 @@
// run-pass
#![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>
#![allow(unused)]
// If the closures can handle such precison we should be able to read one path in the closure
// while being able mutate another path outside the closure, where the two paths are disjoint
// after applying two projections on the root variable.
struct Point {
x: i32,
y: i32,
}
struct Wrapper {
p: Point,
}
fn main() {
let mut w = Wrapper { p: Point { x: 10, y: 10 } };
let c = || {
println!("{}", w.p.x);
};
// `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,11 @@
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
warning: 1 warning emitted

View File

@ -0,0 +1,31 @@
// run-pass
#![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>
#![allow(unused)]
// Test that when `capture_disjoint_fields` is enabled we can read a path
// both inside and outside the closure at the same time.
struct Point {
x: i32,
y: i32,
}
struct Wrapper {
p: Point,
}
fn main() {
let mut w = Wrapper { p: Point { x: 10, y: 10 } };
let c = || {
println!("{}", w.p.x);
};
let px = &w.p.x;
c();
println!("{}", px);
}

View File

@ -0,0 +1,11 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/multilevel-path-3.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
warning: 1 warning emitted

View File

@ -0,0 +1,40 @@
// run-pass
// Test whether if we can do precise capture when using nested clsoure.
#![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>
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut p = Point { x: 5, y: 20 };
// c1 should capture `p.x` via immutable borrow and
// `p.y` via mutable borrow.
let mut c1 = || {
println!("{}", p.x);
let incr = 10;
let mut c2 = || p.y += incr;
c2();
println!("{}", p.y);
};
c1();
// This should not throw an error because `p.x` is borrowed via Immutable borrow,
// and multiple immutable borrow of the same place are allowed.
let px = &p.x;
println!("{}", px);
c1();
}

View File

@ -0,0 +1,11 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/nested-closure.rs:5: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
warning: 1 warning emitted