Add comments with examples and tests
This commit is contained in:
parent
fb3b77a8c8
commit
22eaffe71a
32 changed files with 788 additions and 136 deletions
|
@ -431,7 +431,29 @@ pub struct TypeckResults<'tcx> {
|
||||||
/// see `MinCaptureInformationMap` for more details.
|
/// see `MinCaptureInformationMap` for more details.
|
||||||
pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>,
|
pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>,
|
||||||
|
|
||||||
pub closure_fake_reads: FxHashMap<DefId, Vec<(HirPlace<'tcx>, FakeReadCause)>>,
|
/// Tracks the fake reads required for a closure and the reason for the fake read.
|
||||||
|
/// When performing pattern matching for closures, there are times we don't end up
|
||||||
|
/// reading places that are mentioned in a closure (because of _ patterns). However,
|
||||||
|
/// to ensure the places are initialized, we introduce fake reads.
|
||||||
|
/// Consider these two examples:
|
||||||
|
/// ``` (discriminant matching with only wildcard arm)
|
||||||
|
/// let x: u8;
|
||||||
|
/// let c = || match x { _ => () };
|
||||||
|
/// ```
|
||||||
|
/// In this example, we don't need to actually read/borrow `x` in `c`, and so we don't
|
||||||
|
/// want to capture it. However, we do still want an error here, because `x` should have
|
||||||
|
/// to be initialized at the point where c is created. Therefore, we add a "fake read"
|
||||||
|
/// instead.
|
||||||
|
/// ``` (destructured assignments)
|
||||||
|
/// let c = || {
|
||||||
|
/// let (t1, t2) = t;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// In the second example, we capture the disjoint fields of `t` (`t.0` & `t.1`), but
|
||||||
|
/// we never capture `t`. This becomes an issue when we build MIR as we require
|
||||||
|
/// information on `t` in order to create place `t.0` and `t.1`. We can solve this
|
||||||
|
/// issue by fake reading `t`.
|
||||||
|
pub closure_fake_reads: FxHashMap<DefId, Vec<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>>,
|
||||||
|
|
||||||
/// 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).
|
||||||
|
|
|
@ -90,15 +90,13 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
|
||||||
let hir_projection = match mir_projection {
|
let hir_projection = match mir_projection {
|
||||||
ProjectionElem::Deref => HirProjectionKind::Deref,
|
ProjectionElem::Deref => HirProjectionKind::Deref,
|
||||||
ProjectionElem::Field(field, _) => {
|
ProjectionElem::Field(field, _) => {
|
||||||
// We will never encouter this for multivariant enums,
|
|
||||||
// read the comment for `Downcast`.
|
|
||||||
let variant = variant.unwrap_or(VariantIdx::new(0));
|
let variant = variant.unwrap_or(VariantIdx::new(0));
|
||||||
HirProjectionKind::Field(field.index() as u32, variant)
|
HirProjectionKind::Field(field.index() as u32, variant)
|
||||||
}
|
}
|
||||||
ProjectionElem::Downcast(.., idx) => {
|
ProjectionElem::Downcast(.., idx) => {
|
||||||
// This projections exist for enums that have
|
// We don't expect to see multi-variant enums here, as earlier
|
||||||
// single and multiple variants.
|
// phases will have truncated them already. However, there can
|
||||||
// For single variants, enums are not captured completely.
|
// still be downcasts, thanks to single-variant enums.
|
||||||
// We keep track of VariantIdx so we can use this information
|
// We keep track of VariantIdx so we can use this information
|
||||||
// if the next ProjectionElem is a Field.
|
// if the next ProjectionElem is a Field.
|
||||||
variant = Some(*idx);
|
variant = Some(*idx);
|
||||||
|
@ -200,7 +198,7 @@ fn find_capture_matching_projections<'a, 'tcx>(
|
||||||
/// Takes a PlaceBuilder and resolves the upvar (if any) within it, so that the
|
/// Takes a PlaceBuilder and resolves the upvar (if any) within it, so that the
|
||||||
/// `PlaceBuilder` now starts from `PlaceBase::Local`.
|
/// `PlaceBuilder` now starts from `PlaceBase::Local`.
|
||||||
///
|
///
|
||||||
/// Returns a Result with the error being the HirId of the Upvar that was not found.
|
/// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found.
|
||||||
fn to_upvars_resolved_place_builder<'a, 'tcx>(
|
fn to_upvars_resolved_place_builder<'a, 'tcx>(
|
||||||
from_builder: PlaceBuilder<'tcx>,
|
from_builder: PlaceBuilder<'tcx>,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
|
@ -305,15 +303,23 @@ impl<'tcx> PlaceBuilder<'tcx> {
|
||||||
to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap()
|
to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to resolve the `PlaceBuilder`.
|
||||||
|
/// On success, it will return the resolved `PlaceBuilder`.
|
||||||
|
/// On failure, it will return itself.
|
||||||
|
///
|
||||||
|
/// Upvars resolve may fail for a `PlaceBuilder` when attempting to
|
||||||
|
/// resolve a disjoint field whose root variable is not captured
|
||||||
|
/// (destructured assignments) or when attempting to resolve a root
|
||||||
|
/// variable (discriminant matching with only wildcard arm) that is
|
||||||
|
/// not captured. This can happen because the final mir that will be
|
||||||
|
/// generated doesn't require a read for this place. Failures will only
|
||||||
|
/// happen inside closures.
|
||||||
crate fn try_upvars_resolved<'a>(
|
crate fn try_upvars_resolved<'a>(
|
||||||
self,
|
self,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||||
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
|
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
|
||||||
match to_upvars_resolved_place_builder(self, tcx, typeck_results) {
|
to_upvars_resolved_place_builder(self, tcx, typeck_results)
|
||||||
Ok(upvars_resolved) => Ok(upvars_resolved),
|
|
||||||
Err(upvars_unresolved) => Err(upvars_unresolved),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
crate fn base(&self) -> PlaceBase {
|
crate fn base(&self) -> PlaceBase {
|
||||||
|
@ -662,7 +668,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
block,
|
block,
|
||||||
source_info,
|
source_info,
|
||||||
len,
|
len,
|
||||||
Rvalue::Len(slice.clone().into_place(self.tcx, self.typeck_results)),
|
Rvalue::Len(slice.into_place(self.tcx, self.typeck_results)),
|
||||||
);
|
);
|
||||||
// lt = idx < len
|
// lt = idx < len
|
||||||
self.cfg.push_assign(
|
self.cfg.push_assign(
|
||||||
|
|
|
@ -165,13 +165,42 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
|
|
||||||
block.and(Rvalue::Aggregate(box AggregateKind::Tuple, fields))
|
block.and(Rvalue::Aggregate(box AggregateKind::Tuple, fields))
|
||||||
}
|
}
|
||||||
ExprKind::Closure {
|
ExprKind::Closure { closure_id, substs, upvars, movability, fake_reads } => {
|
||||||
closure_id,
|
// Convert the closure fake reads, if any, from `ExprRef` to mir `Place`
|
||||||
substs,
|
// and push the fake reads.
|
||||||
upvars,
|
// This must come before creating the operands. This is required in case
|
||||||
movability,
|
// there is a fake read and a borrow of the same path, since otherwise the
|
||||||
fake_reads: opt_fake_reads,
|
// fake read might interfere with the borrow. Consider an example like this
|
||||||
} => {
|
// one:
|
||||||
|
// ```
|
||||||
|
// let mut x = 0;
|
||||||
|
// let c = || {
|
||||||
|
// &mut x; // mutable borrow of `x`
|
||||||
|
// match x { _ => () } // fake read of `x`
|
||||||
|
// };
|
||||||
|
// ```
|
||||||
|
|
||||||
|
// FIXME(RFC2229): Remove feature gate once diagnostics are improved
|
||||||
|
if this.tcx.features().capture_disjoint_fields {
|
||||||
|
for (thir_place, cause, hir_id) in fake_reads.into_iter() {
|
||||||
|
let place_builder =
|
||||||
|
unpack!(block = this.as_place_builder(block, thir_place));
|
||||||
|
|
||||||
|
if let Ok(place_builder_resolved) =
|
||||||
|
place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
|
||||||
|
{
|
||||||
|
let mir_place =
|
||||||
|
place_builder_resolved.into_place(this.tcx, this.typeck_results);
|
||||||
|
this.cfg.push_fake_read(
|
||||||
|
block,
|
||||||
|
this.source_info(this.tcx.hir().span(hir_id)),
|
||||||
|
cause,
|
||||||
|
mir_place,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// see (*) above
|
// see (*) above
|
||||||
let operands: Vec<_> = upvars
|
let operands: Vec<_> = upvars
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -210,21 +239,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
if let Some(fake_reads) = opt_fake_reads {
|
|
||||||
for (thir_place, cause) in fake_reads.into_iter() {
|
|
||||||
let place_builder =
|
|
||||||
unpack!(block = this.as_place_builder(block, thir_place));
|
|
||||||
|
|
||||||
if let Ok(place_builder_resolved) =
|
|
||||||
place_builder.clone().try_upvars_resolved(this.tcx, this.typeck_results)
|
|
||||||
{
|
|
||||||
let mir_place = place_builder_resolved
|
|
||||||
.clone()
|
|
||||||
.into_place(this.tcx, this.typeck_results);
|
|
||||||
this.cfg.push_fake_read(block, source_info, cause, mir_place);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = match substs {
|
let result = match substs {
|
||||||
UpvarSubsts::Generator(substs) => {
|
UpvarSubsts::Generator(substs) => {
|
||||||
|
|
|
@ -146,8 +146,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
if let Ok(scrutinee_builder) =
|
if let Ok(scrutinee_builder) =
|
||||||
scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
||||||
{
|
{
|
||||||
let scrutinee_place =
|
let scrutinee_place = scrutinee_builder.into_place(self.tcx, self.typeck_results);
|
||||||
scrutinee_builder.clone().into_place(self.tcx, self.typeck_results);
|
|
||||||
self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
|
self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +244,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
let arm_source_info = self.source_info(arm.span);
|
let arm_source_info = self.source_info(arm.span);
|
||||||
let arm_scope = (arm.scope, arm_source_info);
|
let arm_scope = (arm.scope, arm_source_info);
|
||||||
self.in_scope(arm_scope, arm.lint_level, |this| {
|
self.in_scope(arm_scope, arm.lint_level, |this| {
|
||||||
let body = arm.body.clone();
|
let body = arm.body;
|
||||||
|
|
||||||
|
// `try_upvars_resolved` may fail if it is unable to resolve the given
|
||||||
|
// `PlaceBuilder` inside a closure. In this case, we don't want to include
|
||||||
|
// a scrutinee place. `scrutinee_place_builder` will fail to be resolved
|
||||||
|
// if the only match arm is a wildcard (`_`).
|
||||||
|
// Example:
|
||||||
|
// ```
|
||||||
|
// let foo = (0, 1);
|
||||||
|
// let c = || {
|
||||||
|
// match foo { _ => () };
|
||||||
|
// };
|
||||||
|
// ```
|
||||||
let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None;
|
let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None;
|
||||||
let scrutinee_place: Place<'tcx>;
|
let scrutinee_place: Place<'tcx>;
|
||||||
if let Ok(scrutinee_builder) = scrutinee_place_builder
|
if let Ok(scrutinee_builder) = scrutinee_place_builder
|
||||||
|
@ -496,6 +507,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
|
VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
|
||||||
)))) = self.local_decls[local].local_info
|
)))) = self.local_decls[local].local_info
|
||||||
{
|
{
|
||||||
|
// `try_upvars_resolved` may fail if it is unable to resolve the given
|
||||||
|
// `PlaceBuilder` inside a closure. In this case, we don't want to include
|
||||||
|
// a scrutinee place. `scrutinee_place_builder` will fail for destructured
|
||||||
|
// assignments. This is because a closure only captures the precise places
|
||||||
|
// that it will read and as a result a closure may not capture the entire
|
||||||
|
// tuple/struct and rather have individual places that will be read in the
|
||||||
|
// final MIR.
|
||||||
|
// Example:
|
||||||
|
// ```
|
||||||
|
// let foo = (0, 1);
|
||||||
|
// let c = || {
|
||||||
|
// let (v1, v2) = foo;
|
||||||
|
// };
|
||||||
|
// ```
|
||||||
if let Ok(match_pair_resolved) =
|
if let Ok(match_pair_resolved) =
|
||||||
initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
||||||
{
|
{
|
||||||
|
|
|
@ -160,7 +160,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
if let Ok(test_place_builder) =
|
if let Ok(test_place_builder) =
|
||||||
place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
||||||
{
|
{
|
||||||
place = test_place_builder.clone().into_place(self.tcx, self.typeck_results);
|
place = test_place_builder.into_place(self.tcx, self.typeck_results);
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -454,20 +454,20 @@ impl<'thir, 'tcx> Cx<'thir, 'tcx> {
|
||||||
.map(|(captured_place, ty)| self.capture_upvar(expr, captured_place, ty)),
|
.map(|(captured_place, ty)| self.capture_upvar(expr, captured_place, ty)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Convert the closure fake reads, if any, from hir `Place` to ExprRef
|
||||||
let fake_reads = match self.typeck_results().closure_fake_reads.get(&def_id) {
|
let fake_reads = match self.typeck_results().closure_fake_reads.get(&def_id) {
|
||||||
Some(vals) => Some(
|
Some(fake_reads) => fake_reads
|
||||||
vals.iter()
|
.iter()
|
||||||
.map(|(place, cause)| {
|
.map(|(place, cause, hir_id)| {
|
||||||
(
|
(
|
||||||
self.arena.alloc(
|
self.arena
|
||||||
self.convert_captured_hir_place(expr, place.clone()),
|
.alloc(self.convert_captured_hir_place(expr, place.clone())),
|
||||||
),
|
*cause,
|
||||||
*cause,
|
*hir_id,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
),
|
None => Vec::new(),
|
||||||
None => None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ExprKind::Closure {
|
ExprKind::Closure {
|
||||||
|
|
|
@ -281,7 +281,7 @@ pub enum ExprKind<'thir, 'tcx> {
|
||||||
substs: UpvarSubsts<'tcx>,
|
substs: UpvarSubsts<'tcx>,
|
||||||
upvars: &'thir [Expr<'thir, 'tcx>],
|
upvars: &'thir [Expr<'thir, 'tcx>],
|
||||||
movability: Option<hir::Movability>,
|
movability: Option<hir::Movability>,
|
||||||
fake_reads: Option<Vec<(&'thir mut Expr<'thir, 'tcx>, FakeReadCause)>>,
|
fake_reads: Vec<(&'thir mut Expr<'thir, 'tcx>, FakeReadCause, hir::HirId)>,
|
||||||
},
|
},
|
||||||
Literal {
|
Literal {
|
||||||
literal: &'tcx Const<'tcx>,
|
literal: &'tcx Const<'tcx>,
|
||||||
|
|
|
@ -248,8 +248,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let final_tupled_upvars_type = self.tcx.mk_tup(final_upvar_tys.iter());
|
let final_tupled_upvars_type = self.tcx.mk_tup(final_upvar_tys.iter());
|
||||||
self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type);
|
self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type);
|
||||||
|
|
||||||
let fake_reads =
|
let fake_reads = delegate
|
||||||
delegate.fake_reads.into_iter().map(|(place, cause)| (place, cause)).collect();
|
.fake_reads
|
||||||
|
.into_iter()
|
||||||
|
.map(|(place, cause, hir_id)| (place, cause, hir_id))
|
||||||
|
.collect();
|
||||||
self.typeck_results.borrow_mut().closure_fake_reads.insert(closure_def_id, fake_reads);
|
self.typeck_results.borrow_mut().closure_fake_reads.insert(closure_def_id, fake_reads);
|
||||||
|
|
||||||
// If we are also inferred the closure kind here,
|
// If we are also inferred the closure kind here,
|
||||||
|
@ -1154,7 +1157,7 @@ struct InferBorrowKind<'a, 'tcx> {
|
||||||
/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
|
/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
|
||||||
/// ```
|
/// ```
|
||||||
capture_information: InferredCaptureInformation<'tcx>,
|
capture_information: InferredCaptureInformation<'tcx>,
|
||||||
fake_reads: Vec<(Place<'tcx>, FakeReadCause)>,
|
fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
||||||
|
@ -1416,9 +1419,9 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
|
impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
|
||||||
fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause) {
|
fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId) {
|
||||||
if let PlaceBase::Upvar(_) = place.base {
|
if let PlaceBase::Upvar(_) = place.base {
|
||||||
self.fake_reads.push((place, cause));
|
self.fake_reads.push((place, cause, diag_expr_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -371,18 +371,18 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
||||||
fn visit_fake_reads_map(&mut self) {
|
fn visit_fake_reads_map(&mut self) {
|
||||||
let mut resolved_closure_fake_reads: FxHashMap<
|
let mut resolved_closure_fake_reads: FxHashMap<
|
||||||
DefId,
|
DefId,
|
||||||
Vec<(HirPlace<'tcx>, FakeReadCause)>,
|
Vec<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>,
|
||||||
> = Default::default();
|
> = Default::default();
|
||||||
for (closure_def_id, fake_reads) in
|
for (closure_def_id, fake_reads) in
|
||||||
self.fcx.typeck_results.borrow().closure_fake_reads.iter()
|
self.fcx.typeck_results.borrow().closure_fake_reads.iter()
|
||||||
{
|
{
|
||||||
let mut resolved_fake_reads = Vec::<(HirPlace<'tcx>, FakeReadCause)>::new();
|
let mut resolved_fake_reads = Vec::<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>::new();
|
||||||
for (place, cause) in fake_reads.iter() {
|
for (place, cause, hir_id) in fake_reads.iter() {
|
||||||
let locatable =
|
let locatable =
|
||||||
self.tcx().hir().local_def_id_to_hir_id(closure_def_id.expect_local());
|
self.tcx().hir().local_def_id_to_hir_id(closure_def_id.expect_local());
|
||||||
|
|
||||||
let resolved_fake_read = self.resolve(place.clone(), &locatable);
|
let resolved_fake_read = self.resolve(place.clone(), &locatable);
|
||||||
resolved_fake_reads.push((resolved_fake_read, *cause));
|
resolved_fake_reads.push((resolved_fake_read, *cause, *hir_id));
|
||||||
}
|
}
|
||||||
resolved_closure_fake_reads.insert(*closure_def_id, resolved_fake_reads);
|
resolved_closure_fake_reads.insert(*closure_def_id, resolved_fake_reads);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,11 @@ pub use self::ConsumeMode::*;
|
||||||
// Export these here so that Clippy can use them.
|
// Export these here so that Clippy can use them.
|
||||||
pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
|
pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
|
||||||
|
|
||||||
|
use rustc_data_structures::fx::FxIndexMap;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_hir::PatKind;
|
use rustc_hir::PatKind;
|
||||||
//use rustc_hir::QPath;
|
|
||||||
use rustc_index::vec::Idx;
|
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;
|
||||||
|
@ -54,7 +54,8 @@ pub trait Delegate<'tcx> {
|
||||||
// `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
|
// `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
|
||||||
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);
|
||||||
|
|
||||||
fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause);
|
// The `place` should be a fake read because of specified `cause`.
|
||||||
|
fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
@ -241,20 +242,33 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self;
|
let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self;
|
||||||
let mut needs_to_be_read = false;
|
let mut needs_to_be_read = false;
|
||||||
for arm in arms.iter() {
|
for arm in arms.iter() {
|
||||||
return_if_err!(mc.cat_pattern(discr_place.clone(), &arm.pat, |_place, pat| {
|
return_if_err!(mc.cat_pattern(discr_place.clone(), &arm.pat, |place, pat| {
|
||||||
match &pat.kind {
|
match &pat.kind {
|
||||||
PatKind::Binding(_, _, _, opt_sub_pat) => {
|
PatKind::Binding(.., opt_sub_pat) => {
|
||||||
// If the opt_sub_pat is None, than the binding does not count as
|
// If the opt_sub_pat is None, than the binding does not count as
|
||||||
// a wildcard for the purpose of borrowing discr
|
// a wildcard for the purpose of borrowing discr.
|
||||||
if let None = opt_sub_pat {
|
if opt_sub_pat.is_none() {
|
||||||
needs_to_be_read = true;
|
needs_to_be_read = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PatKind::TupleStruct(_, _, _)
|
PatKind::TupleStruct(..)
|
||||||
| PatKind::Struct(_, _, _)
|
| PatKind::Path(..)
|
||||||
| PatKind::Lit(_) => {
|
| PatKind::Struct(..)
|
||||||
// If the PatKind is a TupleStruct, Struct, or Lit then we want
|
| PatKind::Tuple(..) => {
|
||||||
// to borrow discr
|
// If the PatKind is a TupleStruct, Struct or Tuple then we want to check
|
||||||
|
// whether the Variant is a MultiVariant or a SingleVariant. We only want
|
||||||
|
// to borrow discr if it is a MultiVariant.
|
||||||
|
// If it is a SingleVariant and creates a binding we will handle that when
|
||||||
|
// this callback gets called again.
|
||||||
|
if let ty::Adt(def, _) = place.place.base_ty.kind() {
|
||||||
|
if def.variants.len() > 1 {
|
||||||
|
needs_to_be_read = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PatKind::Lit(_) => {
|
||||||
|
// If the PatKind is a Lit then we want
|
||||||
|
// to borrow discr.
|
||||||
needs_to_be_read = true;
|
needs_to_be_read = true;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -264,6 +278,16 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
|
|
||||||
if needs_to_be_read {
|
if needs_to_be_read {
|
||||||
self.borrow_expr(&discr, ty::ImmBorrow);
|
self.borrow_expr(&discr, ty::ImmBorrow);
|
||||||
|
} else {
|
||||||
|
self.delegate.fake_read(
|
||||||
|
discr_place.place.clone(),
|
||||||
|
FakeReadCause::ForMatchedPlace,
|
||||||
|
discr_place.hir_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// We always want to walk the discriminant. We want to make sure, for instance,
|
||||||
|
// that the discriminant has been initialized.
|
||||||
|
self.walk_expr(&discr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// treatment of the discriminant is handled while walking the arms.
|
// treatment of the discriminant is handled while walking the arms.
|
||||||
|
@ -553,7 +577,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_arm(&mut self, discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>) {
|
fn walk_arm(&mut self, discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>) {
|
||||||
self.delegate.fake_read(discr_place.place.clone(), FakeReadCause::ForMatchedPlace);
|
self.delegate.fake_read(
|
||||||
|
discr_place.place.clone(),
|
||||||
|
FakeReadCause::ForMatchedPlace,
|
||||||
|
discr_place.hir_id,
|
||||||
|
);
|
||||||
self.walk_pat(discr_place, &arm.pat);
|
self.walk_pat(discr_place, &arm.pat);
|
||||||
|
|
||||||
if let Some(hir::Guard::If(ref e)) = arm.guard {
|
if let Some(hir::Guard::If(ref e)) = arm.guard {
|
||||||
|
@ -566,7 +594,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
/// Walks a pat that occurs in isolation (i.e., top-level of fn argument or
|
/// Walks a pat that occurs in isolation (i.e., top-level of fn argument or
|
||||||
/// let binding, and *not* a match arm or nested pat.)
|
/// let binding, and *not* a match arm or nested pat.)
|
||||||
fn walk_irrefutable_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
|
fn walk_irrefutable_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
|
||||||
self.delegate.fake_read(discr_place.place.clone(), FakeReadCause::ForLet);
|
self.delegate.fake_read(
|
||||||
|
discr_place.place.clone(),
|
||||||
|
FakeReadCause::ForLet,
|
||||||
|
discr_place.hir_id,
|
||||||
|
);
|
||||||
self.walk_pat(discr_place, pat);
|
self.walk_pat(discr_place, pat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,6 +666,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
/// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing
|
/// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing
|
||||||
/// closure as the DefId.
|
/// closure as the DefId.
|
||||||
fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) {
|
fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) {
|
||||||
|
fn upvar_is_local_variable(
|
||||||
|
upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>,
|
||||||
|
upvar_id: &hir::HirId,
|
||||||
|
body_owner_is_closure: bool,
|
||||||
|
) -> bool {
|
||||||
|
upvars.map(|upvars| !upvars.contains_key(upvar_id)).unwrap_or(body_owner_is_closure)
|
||||||
|
}
|
||||||
|
|
||||||
debug!("walk_captures({:?})", closure_expr);
|
debug!("walk_captures({:?})", closure_expr);
|
||||||
|
|
||||||
let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id();
|
let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id();
|
||||||
|
@ -645,16 +685,32 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
ty::Closure(..) | ty::Generator(..)
|
ty::Closure(..) | ty::Generator(..)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// If we have a nested closure, we want to include the fake reads present in the nested closure.
|
||||||
if let Some(fake_reads) = self.mc.typeck_results.closure_fake_reads.get(&closure_def_id) {
|
if let Some(fake_reads) = self.mc.typeck_results.closure_fake_reads.get(&closure_def_id) {
|
||||||
for (fake_read, cause) in fake_reads.iter() {
|
for (fake_read, cause, hir_id) in fake_reads.iter() {
|
||||||
match fake_read.base {
|
match fake_read.base {
|
||||||
PlaceBase::Upvar(upvar_id) => {
|
PlaceBase::Upvar(upvar_id) => {
|
||||||
if upvars.map_or(body_owner_is_closure, |upvars| {
|
if upvar_is_local_variable(
|
||||||
!upvars.contains_key(&upvar_id.var_path.hir_id)
|
upvars,
|
||||||
}) {
|
&upvar_id.var_path.hir_id,
|
||||||
|
body_owner_is_closure,
|
||||||
|
) {
|
||||||
// The nested closure might be fake reading the current (enclosing) closure's local variables.
|
// The nested closure might be fake reading the current (enclosing) closure's local variables.
|
||||||
// We check if the root variable is ever mentioned within the enclosing closure, if not
|
// The only places we want to fake read before creating the parent closure are the ones that
|
||||||
// then for the current body (if it's a closure) these do not require fake_read, we will ignore them.
|
// are not local to it/ defined by it.
|
||||||
|
//
|
||||||
|
// ```rust,ignore(cannot-test-this-because-pseduo-code)
|
||||||
|
// let v1 = (0, 1);
|
||||||
|
// let c = || { // fake reads: v1
|
||||||
|
// let v2 = (0, 1);
|
||||||
|
// let e = || { // fake reads: v1, v2
|
||||||
|
// let (_, t1) = v1;
|
||||||
|
// let (_, t2) = v2;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
// This check is performed when visiting the body of the outermost closure (`c`) and ensures
|
||||||
|
// that we don't add a fake read of v2 in c.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -665,7 +721,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.delegate.fake_read(fake_read.clone(), *cause);
|
self.delegate.fake_read(fake_read.clone(), *cause, *hir_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
#![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(never_type)]
|
||||||
|
|
||||||
|
// Should fake read the discriminant and throw an error
|
||||||
|
fn test1() {
|
||||||
|
let x: !;
|
||||||
|
let c1 = || match x { };
|
||||||
|
//~^ ERROR: use of possibly-uninitialized variable: `x`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should fake read the discriminant and throw an error
|
||||||
|
fn test2() {
|
||||||
|
let x: !;
|
||||||
|
let c2 = || match x { _ => () };
|
||||||
|
//~^ ERROR: borrow of possibly-uninitialized variable: `x`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Testing single variant patterns
|
||||||
|
enum SingleVariant {
|
||||||
|
Points(u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should fake read the discriminant and throw an error
|
||||||
|
fn test3() {
|
||||||
|
let variant: !;
|
||||||
|
let c = || {
|
||||||
|
//~^ ERROR: borrow of possibly-uninitialized variable: `variant`
|
||||||
|
match variant {
|
||||||
|
SingleVariant::Points(_) => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
c();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should fake read the discriminant and throw an error
|
||||||
|
fn test4() {
|
||||||
|
let variant: !;
|
||||||
|
let c = || {
|
||||||
|
//~^ ERROR: borrow of possibly-uninitialized variable: `variant`
|
||||||
|
match variant {
|
||||||
|
SingleVariant::Points(a) => {
|
||||||
|
println!("{:?}", a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
c();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test5() {
|
||||||
|
let t: !;
|
||||||
|
let g: !;
|
||||||
|
|
||||||
|
let a = || {
|
||||||
|
match g { };
|
||||||
|
//~^ ERROR: use of possibly-uninitialized variable: `g`
|
||||||
|
let c = || {
|
||||||
|
match t { };
|
||||||
|
//~^ ERROR: use of possibly-uninitialized variable: `t`
|
||||||
|
};
|
||||||
|
|
||||||
|
c();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should fake read the discriminant and throw an error
|
||||||
|
fn test6() {
|
||||||
|
let x: u8;
|
||||||
|
let c1 = || match x { };
|
||||||
|
//~^ ERROR: use of possibly-uninitialized variable: `x`
|
||||||
|
//~| ERROR: non-exhaustive patterns: type `u8` is non-empty
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test1();
|
||||||
|
test2();
|
||||||
|
test3();
|
||||||
|
test4();
|
||||||
|
test5();
|
||||||
|
test6();
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/pattern-matching-should-fail.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[E0004]: non-exhaustive patterns: type `u8` is non-empty
|
||||||
|
--> $DIR/pattern-matching-should-fail.rs:72:23
|
||||||
|
|
|
||||||
|
LL | let c1 = || match x { };
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
|
||||||
|
= note: the matched value is of type `u8`
|
||||||
|
|
||||||
|
error[E0381]: use of possibly-uninitialized variable: `x`
|
||||||
|
--> $DIR/pattern-matching-should-fail.rs:10:23
|
||||||
|
|
|
||||||
|
LL | let c1 = || match x { };
|
||||||
|
| ^ use of possibly-uninitialized `x`
|
||||||
|
|
||||||
|
error[E0381]: borrow of possibly-uninitialized variable: `x`
|
||||||
|
--> $DIR/pattern-matching-should-fail.rs:17:14
|
||||||
|
|
|
||||||
|
LL | let c2 = || match x { _ => () };
|
||||||
|
| ^^ - borrow occurs due to use in closure
|
||||||
|
| |
|
||||||
|
| use of possibly-uninitialized `x`
|
||||||
|
|
||||||
|
error[E0381]: borrow of possibly-uninitialized variable: `variant`
|
||||||
|
--> $DIR/pattern-matching-should-fail.rs:29:13
|
||||||
|
|
|
||||||
|
LL | let c = || {
|
||||||
|
| ^^ use of possibly-uninitialized `variant`
|
||||||
|
LL |
|
||||||
|
LL | match variant {
|
||||||
|
| ------- borrow occurs due to use in closure
|
||||||
|
|
||||||
|
error[E0381]: borrow of possibly-uninitialized variable: `variant`
|
||||||
|
--> $DIR/pattern-matching-should-fail.rs:41:13
|
||||||
|
|
|
||||||
|
LL | let c = || {
|
||||||
|
| ^^ use of possibly-uninitialized `variant`
|
||||||
|
LL |
|
||||||
|
LL | match variant {
|
||||||
|
| ------- borrow occurs due to use in closure
|
||||||
|
|
||||||
|
error[E0381]: use of possibly-uninitialized variable: `g`
|
||||||
|
--> $DIR/pattern-matching-should-fail.rs:57:15
|
||||||
|
|
|
||||||
|
LL | match g { };
|
||||||
|
| ^ use of possibly-uninitialized `g`
|
||||||
|
|
||||||
|
error[E0381]: use of possibly-uninitialized variable: `t`
|
||||||
|
--> $DIR/pattern-matching-should-fail.rs:60:19
|
||||||
|
|
|
||||||
|
LL | match t { };
|
||||||
|
| ^ use of possibly-uninitialized `t`
|
||||||
|
|
||||||
|
error[E0381]: use of possibly-uninitialized variable: `x`
|
||||||
|
--> $DIR/pattern-matching-should-fail.rs:72:23
|
||||||
|
|
|
||||||
|
LL | let c1 = || match x { };
|
||||||
|
| ^ use of possibly-uninitialized `x`
|
||||||
|
|
||||||
|
error: aborting due to 8 previous errors; 1 warning emitted
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0004, E0381.
|
||||||
|
For more information about an error, try `rustc --explain E0004`.
|
|
@ -0,0 +1,139 @@
|
||||||
|
#![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)]
|
||||||
|
|
||||||
|
// Should capture the discriminant since a variant of a multivariant enum is
|
||||||
|
// mentioned in the match arm; the discriminant is captured by the closure regardless
|
||||||
|
// of if it creates a binding
|
||||||
|
fn test_1_should_capture() {
|
||||||
|
let variant = Some(2229);
|
||||||
|
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:
|
||||||
|
match variant {
|
||||||
|
//~^ NOTE: Capturing variant[] -> ImmBorrow
|
||||||
|
//~| NOTE: Min Capture variant[] -> ImmBorrow
|
||||||
|
Some(_) => {}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
c();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not capture the discriminant since only a wildcard is mentioned in the
|
||||||
|
// match arm
|
||||||
|
fn test_2_should_not_capture() {
|
||||||
|
let variant = Some(2229);
|
||||||
|
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:
|
||||||
|
match variant {
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
c();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Testing single variant patterns
|
||||||
|
enum SingleVariant {
|
||||||
|
Points(u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not capture the discriminant since the single variant mentioned
|
||||||
|
// in the match arm does not trigger a binding
|
||||||
|
fn test_3_should_not_capture_single_variant() {
|
||||||
|
let variant = SingleVariant::Points(1);
|
||||||
|
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:
|
||||||
|
match variant {
|
||||||
|
SingleVariant::Points(_) => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
c();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not capture the discriminant since the single variant mentioned
|
||||||
|
// in the match arm does not trigger a binding
|
||||||
|
fn test_6_should_capture_single_variant() {
|
||||||
|
let variant = SingleVariant::Points(1);
|
||||||
|
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:
|
||||||
|
match variant {
|
||||||
|
//~^ NOTE: Capturing variant[] -> ImmBorrow
|
||||||
|
//~| NOTE: Capturing variant[(0, 0)] -> ImmBorrow
|
||||||
|
//~| NOTE: Min Capture variant[] -> ImmBorrow
|
||||||
|
SingleVariant::Points(a) => {
|
||||||
|
println!("{:?}", a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
c();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not capture the discriminant since only wildcards are mentioned in the
|
||||||
|
// match arm
|
||||||
|
fn test_4_should_not_capture_array() {
|
||||||
|
let array: [i32; 3] = [0; 3];
|
||||||
|
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:
|
||||||
|
match array {
|
||||||
|
[_,_,_] => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
c();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Testing MultiVariant patterns
|
||||||
|
enum MVariant {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should capture the discriminant since a variant of the multi variant enum is
|
||||||
|
// mentioned in the match arm; the discriminant is captured by the closure
|
||||||
|
// regardless of if it creates a binding
|
||||||
|
fn test_5_should_capture_multi_variant() {
|
||||||
|
let variant = MVariant::A;
|
||||||
|
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:
|
||||||
|
match variant {
|
||||||
|
//~^ NOTE: Capturing variant[] -> ImmBorrow
|
||||||
|
//~| NOTE: Min Capture variant[] -> ImmBorrow
|
||||||
|
MVariant::A => {}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
c();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_1_should_capture();
|
||||||
|
test_2_should_not_capture();
|
||||||
|
test_3_should_not_capture_single_variant();
|
||||||
|
test_6_should_capture_single_variant();
|
||||||
|
test_4_should_not_capture_array();
|
||||||
|
test_5_should_capture_multi_variant();
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
error[E0658]: attributes on expressions are experimental
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:12:14
|
||||||
|
|
|
||||||
|
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/patterns-capture-analysis.rs:33:14
|
||||||
|
|
|
||||||
|
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/patterns-capture-analysis.rs:54:14
|
||||||
|
|
|
||||||
|
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/patterns-capture-analysis.rs:70:14
|
||||||
|
|
|
||||||
|
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/patterns-capture-analysis.rs:92:14
|
||||||
|
|
|
||||||
|
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/patterns-capture-analysis.rs:116:14
|
||||||
|
|
|
||||||
|
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/patterns-capture-analysis.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/patterns-capture-analysis.rs:16:5
|
||||||
|
|
|
||||||
|
LL | / || {
|
||||||
|
LL | |
|
||||||
|
LL | |
|
||||||
|
LL | | match variant {
|
||||||
|
... |
|
||||||
|
LL | | }
|
||||||
|
LL | | };
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
note: Capturing variant[] -> ImmBorrow
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:19:15
|
||||||
|
|
|
||||||
|
LL | match variant {
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: Min Capture analysis includes:
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:16:5
|
||||||
|
|
|
||||||
|
LL | / || {
|
||||||
|
LL | |
|
||||||
|
LL | |
|
||||||
|
LL | | match variant {
|
||||||
|
... |
|
||||||
|
LL | | }
|
||||||
|
LL | | };
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
note: Min Capture variant[] -> ImmBorrow
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:19:15
|
||||||
|
|
|
||||||
|
LL | match variant {
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: First Pass analysis includes:
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:36:5
|
||||||
|
|
|
||||||
|
LL | / || {
|
||||||
|
LL | |
|
||||||
|
LL | | match variant {
|
||||||
|
LL | | _ => {}
|
||||||
|
LL | | }
|
||||||
|
LL | | };
|
||||||
|
| |_____^
|
||||||
|
|
||||||
|
error: First Pass analysis includes:
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:57:5
|
||||||
|
|
|
||||||
|
LL | / || {
|
||||||
|
LL | |
|
||||||
|
LL | | match variant {
|
||||||
|
LL | | SingleVariant::Points(_) => {}
|
||||||
|
LL | | }
|
||||||
|
LL | | };
|
||||||
|
| |_____^
|
||||||
|
|
||||||
|
error: First Pass analysis includes:
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:73:5
|
||||||
|
|
|
||||||
|
LL | / || {
|
||||||
|
LL | |
|
||||||
|
LL | |
|
||||||
|
LL | | match variant {
|
||||||
|
... |
|
||||||
|
LL | | }
|
||||||
|
LL | | };
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
note: Capturing variant[] -> ImmBorrow
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:76:15
|
||||||
|
|
|
||||||
|
LL | match variant {
|
||||||
|
| ^^^^^^^
|
||||||
|
note: Capturing variant[(0, 0)] -> ImmBorrow
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:76:15
|
||||||
|
|
|
||||||
|
LL | match variant {
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: Min Capture analysis includes:
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:73:5
|
||||||
|
|
|
||||||
|
LL | / || {
|
||||||
|
LL | |
|
||||||
|
LL | |
|
||||||
|
LL | | match variant {
|
||||||
|
... |
|
||||||
|
LL | | }
|
||||||
|
LL | | };
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
note: Min Capture variant[] -> ImmBorrow
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:76:15
|
||||||
|
|
|
||||||
|
LL | match variant {
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: First Pass analysis includes:
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:95:5
|
||||||
|
|
|
||||||
|
LL | / || {
|
||||||
|
LL | |
|
||||||
|
LL | | match array {
|
||||||
|
LL | | [_,_,_] => {}
|
||||||
|
LL | | }
|
||||||
|
LL | | };
|
||||||
|
| |_____^
|
||||||
|
|
||||||
|
error: First Pass analysis includes:
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:119:5
|
||||||
|
|
|
||||||
|
LL | / || {
|
||||||
|
LL | |
|
||||||
|
LL | |
|
||||||
|
LL | | match variant {
|
||||||
|
... |
|
||||||
|
LL | | }
|
||||||
|
LL | | };
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
note: Capturing variant[] -> ImmBorrow
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:122:15
|
||||||
|
|
|
||||||
|
LL | match variant {
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: Min Capture analysis includes:
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:119:5
|
||||||
|
|
|
||||||
|
LL | / || {
|
||||||
|
LL | |
|
||||||
|
LL | |
|
||||||
|
LL | | match variant {
|
||||||
|
... |
|
||||||
|
LL | | }
|
||||||
|
LL | | };
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
note: Min Capture variant[] -> ImmBorrow
|
||||||
|
--> $DIR/patterns-capture-analysis.rs:122:15
|
||||||
|
|
|
||||||
|
LL | match variant {
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 15 previous errors; 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
|
@ -3,20 +3,23 @@
|
||||||
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
|
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
|
||||||
|
|
||||||
fn test1() {
|
fn test1() {
|
||||||
let foo = [1, 2, 3];
|
let foo : [Vec<u8>; 3] = ["String".into(), "String".into(), "String".into()];
|
||||||
let c = || {
|
let c = || {
|
||||||
match foo { _ => () };
|
match foo { _ => () };
|
||||||
};
|
};
|
||||||
|
drop(foo);
|
||||||
|
c();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test2() {
|
fn test2() {
|
||||||
let foo = Some([1, 2, 3]);
|
let foo : Option<[Vec<u8>; 3]> = Some(["String".into(), "String".into(), "String".into()]);
|
||||||
let c = || {
|
let c = || {
|
||||||
match foo {
|
match foo {
|
||||||
Some(_) => 1,
|
Some(_) => 1,
|
||||||
_ => 2
|
_ => 2
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
c();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
|
@ -1,5 +1,5 @@
|
||||||
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
|
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
--> $DIR/no_capture_with_wildcard_match.rs:2:12
|
--> $DIR/capture_with_wildcard_match.rs:2:12
|
||||||
|
|
|
|
||||||
LL | #![feature(capture_disjoint_fields)]
|
LL | #![feature(capture_disjoint_fields)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
|
@ -4,7 +4,6 @@
|
||||||
#![warn(unused)]
|
#![warn(unused)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _z = 9;
|
|
||||||
let t = (String::from("Hello"), String::from("World"));
|
let t = (String::from("Hello"), String::from("World"));
|
||||||
let g = (String::from("Mr"), String::from("Goose"));
|
let g = (String::from("Mr"), String::from("Goose"));
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ LL | #![feature(capture_disjoint_fields)]
|
||||||
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
|
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
|
||||||
|
|
||||||
warning: unused variable: `t2`
|
warning: unused variable: `t2`
|
||||||
--> $DIR/destructure-pattern-closure-within-closure.rs:15:21
|
--> $DIR/destructure-pattern-closure-within-closure.rs:14:21
|
||||||
|
|
|
|
||||||
LL | let (_, t2) = t;
|
LL | let (_, t2) = t;
|
||||||
| ^^ help: if this is intentional, prefix it with an underscore: `_t2`
|
| ^^ help: if this is intentional, prefix it with an underscore: `_t2`
|
||||||
|
@ -21,7 +21,7 @@ LL | #![warn(unused)]
|
||||||
= note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
|
= note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
|
||||||
|
|
||||||
warning: unused variable: `g2`
|
warning: unused variable: `g2`
|
||||||
--> $DIR/destructure-pattern-closure-within-closure.rs:12:17
|
--> $DIR/destructure-pattern-closure-within-closure.rs:11:17
|
||||||
|
|
|
|
||||||
LL | let (_, g2) = g;
|
LL | let (_, g2) = g;
|
||||||
| ^^ help: if this is intentional, prefix it with an underscore: `_g2`
|
| ^^ help: if this is intentional, prefix it with an underscore: `_g2`
|
||||||
|
|
|
@ -80,7 +80,6 @@ fn test7() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// [FIXME] RFC2229 Add an explanation for test
|
|
||||||
fn test8() {
|
fn test8() {
|
||||||
let x = 0;
|
let x = 0;
|
||||||
//~^ WARN unused variable: `x`
|
//~^ WARN unused variable: `x`
|
||||||
|
@ -90,10 +89,10 @@ fn test8() {
|
||||||
|
|
||||||
let c = || {
|
let c = || {
|
||||||
let _ = x;
|
let _ = x;
|
||||||
let Point { x, y } = p; // 1
|
let Point { x, y } = p;
|
||||||
//~^ WARN unused variable: `x`
|
//~^ WARN unused variable: `x`
|
||||||
println!("{}", y);
|
println!("{}", y);
|
||||||
let (_, _) = tup; // 2
|
let (_, _) = tup;
|
||||||
};
|
};
|
||||||
|
|
||||||
c();
|
c();
|
||||||
|
|
|
@ -45,19 +45,19 @@ LL | let t = (String::from("Hello"), String::from("World"));
|
||||||
| ^ help: if this is intentional, prefix it with an underscore: `_t`
|
| ^ help: if this is intentional, prefix it with an underscore: `_t`
|
||||||
|
|
||||||
warning: unused variable: `x`
|
warning: unused variable: `x`
|
||||||
--> $DIR/destructure_patterns.rs:93:21
|
--> $DIR/destructure_patterns.rs:92:21
|
||||||
|
|
|
|
||||||
LL | let Point { x, y } = p; // 1
|
LL | let Point { x, y } = p;
|
||||||
| ^ help: try ignoring the field: `x: _`
|
| ^ help: try ignoring the field: `x: _`
|
||||||
|
|
||||||
warning: unused variable: `x`
|
warning: unused variable: `x`
|
||||||
--> $DIR/destructure_patterns.rs:85:9
|
--> $DIR/destructure_patterns.rs:84:9
|
||||||
|
|
|
|
||||||
LL | let x = 0;
|
LL | let x = 0;
|
||||||
| ^ help: if this is intentional, prefix it with an underscore: `_x`
|
| ^ help: if this is intentional, prefix it with an underscore: `_x`
|
||||||
|
|
||||||
warning: unused variable: `tup`
|
warning: unused variable: `tup`
|
||||||
--> $DIR/destructure_patterns.rs:87:9
|
--> $DIR/destructure_patterns.rs:86:9
|
||||||
|
|
|
|
||||||
LL | let tup = (1, 2);
|
LL | let tup = (1, 2);
|
||||||
| ^^^ help: if this is intentional, prefix it with an underscore: `_tup`
|
| ^^^ help: if this is intentional, prefix it with an underscore: `_tup`
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
//check-pass
|
||||||
|
#![feature(capture_disjoint_fields)]
|
||||||
|
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut x = 1;
|
||||||
|
let c = || {
|
||||||
|
drop(&mut x);
|
||||||
|
match x { _ => () }
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
|
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
--> $DIR/struct_update_syntax.rs:2:12
|
--> $DIR/drop_then_use_fake_reads.rs:2:12
|
||||||
|
|
|
|
||||||
LL | #![feature(capture_disjoint_fields)]
|
LL | #![feature(capture_disjoint_fields)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
|
@ -21,4 +21,11 @@ fn main() {
|
||||||
assert!(matches!(result, Ok(None)));
|
assert!(matches!(result, Ok(None)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut it = map.drain_filter(|_, _| true);
|
||||||
|
catch_unwind(AssertUnwindSafe(|| while let Some(_) = it.next() {})).unwrap_err();
|
||||||
|
let result = catch_unwind(AssertUnwindSafe(|| it.next()));
|
||||||
|
assert!(matches!(result, Ok(None)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ enum PointType {
|
||||||
ThreeD{ x: u32, y: u32, z: u32 }
|
ThreeD{ x: u32, y: u32, z: u32 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Testing struct patterns
|
||||||
struct Points {
|
struct Points {
|
||||||
points: Vec<PointType>,
|
points: Vec<PointType>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
//check-pass
|
|
||||||
#![feature(capture_disjoint_fields)]
|
|
||||||
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
|
|
||||||
#![feature(rustc_attrs)]
|
|
||||||
|
|
||||||
struct S {
|
|
||||||
a: String,
|
|
||||||
b: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let s = S {
|
|
||||||
a: String::new(),
|
|
||||||
b: String::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let c = || {
|
|
||||||
let s2 = S {
|
|
||||||
a: format!("New a"),
|
|
||||||
..s
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
c();
|
|
||||||
}
|
|
|
@ -8,6 +8,7 @@ enum PointType {
|
||||||
ThreeD(u32, u32, u32)
|
ThreeD(u32, u32, u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Testing tuple struct patterns
|
||||||
struct Points {
|
struct Points {
|
||||||
points: Vec<PointType>,
|
points: Vec<PointType>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
//check-pass
|
||||||
|
#![feature(capture_disjoint_fields)]
|
||||||
|
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut x = 0;
|
||||||
|
let c = || {
|
||||||
|
&mut x; // mutable borrow of `x`
|
||||||
|
match x { _ => () } // fake read of `x`
|
||||||
|
};
|
||||||
|
}
|
|
@ -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/use_of_mutable_borrow_and_fake_reads.rs:2: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
|
||||||
|
|
|
@ -189,19 +189,18 @@ LL | drop(_x3);
|
||||||
error[E0382]: use of moved value: `tup`
|
error[E0382]: use of moved value: `tup`
|
||||||
--> $DIR/borrowck-move-ref-pattern.rs:43:14
|
--> $DIR/borrowck-move-ref-pattern.rs:43:14
|
||||||
|
|
|
|
||||||
LL | let mut tup = (U, U, U);
|
LL | let mut tup = (U, U, U);
|
||||||
| ------- move occurs because `tup` has type `(U, U, U)`, which does not implement the `Copy` trait
|
| ------- move occurs because `tup` has type `(U, U, U)`, which does not implement the `Copy` trait
|
||||||
LL | let c1 = || {
|
LL | let c1 = || {
|
||||||
| -- value moved into closure here
|
| -- value moved into closure here
|
||||||
LL | let (ref _x0, _x1, _) = tup;
|
LL | let (ref _x0, _x1, _) = tup;
|
||||||
| --- variable moved due to use in closure
|
| --- variable moved due to use in closure
|
||||||
LL | };
|
LL | };
|
||||||
LL | let c2 = || {
|
LL | let c2 = || {
|
||||||
| ______________^
|
| ^^ value used here after move
|
||||||
LL | |
|
LL |
|
||||||
LL | | let (ref mut _x0, _, _x2) = tup;
|
LL | let (ref mut _x0, _, _x2) = tup;
|
||||||
LL | | };
|
| --- use occurs due to use in closure
|
||||||
| |_____^ value used here after move
|
|
||||||
|
|
||||||
error: aborting due to 18 previous errors
|
error: aborting due to 18 previous errors
|
||||||
|
|
||||||
|
|
|
@ -186,7 +186,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause) { }
|
fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
|
impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
|
||||||
|
|
|
@ -335,5 +335,5 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
|
||||||
|
|
||||||
fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
|
fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
|
||||||
|
|
||||||
fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause) { }
|
fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
|
||||||
self.update(&cmt)
|
self.update(&cmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause) { }
|
fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _:HirId) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ParamBindingIdCollector {
|
pub struct ParamBindingIdCollector {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue