Auto merge of #82536 - sexxi-goose:handle-patterns-take-2, r=nikomatsakis
2229: Handle patterns within closures correctly when `capture_disjoint_fields` is enabled This PR fixes several issues related to handling patterns within closures when `capture_disjoint_fields` is enabled. 1. Matching is always considered a use of the place, even with `_` patterns 2. Compiler ICE when capturing fields in closures through `let` assignments To do so, we - Introduced new Fake Reads - Delayed use of `Place` in favor of `PlaceBuilder` - Ensured that `PlaceBuilder` can be resolved before attempting to extract `Place` in any of the pattern matching code Closes rust-lang/project-rfc-2229/issues/27 Closes rust-lang/project-rfc-2229/issues/24 r? `@nikomatsakis`
This commit is contained in:
commit
f5d8117c33
|
@ -47,6 +47,7 @@ use rustc_hir::{
|
|||
};
|
||||
use rustc_index::vec::{Idx, IndexVec};
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
|
||||
use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames};
|
||||
use rustc_session::lint::{Level, Lint};
|
||||
|
@ -430,6 +431,30 @@ pub struct TypeckResults<'tcx> {
|
|||
/// see `MinCaptureInformationMap` for more details.
|
||||
pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>,
|
||||
|
||||
/// 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
|
||||
/// that are live across the yield of this generator (if a generator).
|
||||
pub generator_interior_types: ty::Binder<Vec<GeneratorInteriorTypeCause<'tcx>>>,
|
||||
|
@ -464,6 +489,7 @@ impl<'tcx> TypeckResults<'tcx> {
|
|||
concrete_opaque_types: Default::default(),
|
||||
closure_captures: Default::default(),
|
||||
closure_min_captures: Default::default(),
|
||||
closure_fake_reads: Default::default(),
|
||||
generator_interior_types: ty::Binder::dummy(Default::default()),
|
||||
treat_byte_string_as_slice: Default::default(),
|
||||
}
|
||||
|
@ -715,6 +741,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
|
|||
ref concrete_opaque_types,
|
||||
ref closure_captures,
|
||||
ref closure_min_captures,
|
||||
ref closure_fake_reads,
|
||||
ref generator_interior_types,
|
||||
ref treat_byte_string_as_slice,
|
||||
} = *self;
|
||||
|
@ -750,6 +777,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
|
|||
concrete_opaque_types.hash_stable(hcx, hasher);
|
||||
closure_captures.hash_stable(hcx, hasher);
|
||||
closure_min_captures.hash_stable(hcx, hasher);
|
||||
closure_fake_reads.hash_stable(hcx, hasher);
|
||||
generator_interior_types.hash_stable(hcx, hasher);
|
||||
treat_byte_string_as_slice.hash_stable(hcx, hasher);
|
||||
})
|
||||
|
|
|
@ -10,6 +10,7 @@ use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
|
|||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::AssertKind::BoundsCheck;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::AdtDef;
|
||||
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::VariantIdx;
|
||||
|
@ -17,7 +18,7 @@ use rustc_target::abi::VariantIdx;
|
|||
use rustc_index::vec::Idx;
|
||||
|
||||
/// The "outermost" place that holds this value.
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
crate enum PlaceBase {
|
||||
/// Denotes the start of a `Place`.
|
||||
Local(Local),
|
||||
|
@ -67,7 +68,7 @@ crate enum PlaceBase {
|
|||
///
|
||||
/// 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)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
crate struct PlaceBuilder<'tcx> {
|
||||
base: PlaceBase,
|
||||
projection: Vec<PlaceElem<'tcx>>,
|
||||
|
@ -83,20 +84,23 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
|
|||
mir_projections: &[PlaceElem<'tcx>],
|
||||
) -> Vec<HirProjectionKind> {
|
||||
let mut hir_projections = Vec::new();
|
||||
let mut variant = None;
|
||||
|
||||
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))
|
||||
let variant = variant.unwrap_or(VariantIdx::new(0));
|
||||
HirProjectionKind::Field(field.index() as u32, variant)
|
||||
}
|
||||
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::Downcast(.., idx) => {
|
||||
// We don't expect to see multi-variant enums here, as earlier
|
||||
// phases will have truncated them already. However, there can
|
||||
// still be downcasts, thanks to single-variant enums.
|
||||
// We keep track of VariantIdx so we can use this information
|
||||
// if the next ProjectionElem is a Field.
|
||||
variant = Some(*idx);
|
||||
continue;
|
||||
}
|
||||
ProjectionElem::Index(..)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
|
@ -106,7 +110,7 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
|
|||
break;
|
||||
}
|
||||
};
|
||||
|
||||
variant = None;
|
||||
hir_projections.push(hir_projection);
|
||||
}
|
||||
|
||||
|
@ -194,12 +198,12 @@ fn find_capture_matching_projections<'a, 'tcx>(
|
|||
/// 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.
|
||||
/// Returns a Result with the error being the PlaceBuilder (`from_builder`) 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> {
|
||||
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
|
||||
match from_builder.base {
|
||||
PlaceBase::Local(_) => Ok(from_builder),
|
||||
PlaceBase::Upvar { var_hir_id, closure_def_id, closure_kind } => {
|
||||
|
@ -230,13 +234,12 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
|
|||
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);
|
||||
return Err(from_builder);
|
||||
};
|
||||
|
||||
let closure_ty = typeck_results
|
||||
|
@ -300,6 +303,25 @@ impl<'tcx> PlaceBuilder<'tcx> {
|
|||
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>(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
|
||||
to_upvars_resolved_place_builder(self, tcx, typeck_results)
|
||||
}
|
||||
|
||||
crate fn base(&self) -> PlaceBase {
|
||||
self.base
|
||||
}
|
||||
|
@ -308,15 +330,22 @@ impl<'tcx> PlaceBuilder<'tcx> {
|
|||
self.project(PlaceElem::Field(f, ty))
|
||||
}
|
||||
|
||||
fn deref(self) -> Self {
|
||||
crate fn deref(self) -> Self {
|
||||
self.project(PlaceElem::Deref)
|
||||
}
|
||||
|
||||
crate fn downcast(self, adt_def: &'tcx AdtDef, variant_index: VariantIdx) -> Self {
|
||||
self.project(PlaceElem::Downcast(
|
||||
Some(adt_def.variants[variant_index].ident.name),
|
||||
variant_index,
|
||||
))
|
||||
}
|
||||
|
||||
fn index(self, index: Local) -> Self {
|
||||
self.project(PlaceElem::Index(index))
|
||||
}
|
||||
|
||||
fn project(mut self, elem: PlaceElem<'tcx>) -> Self {
|
||||
crate fn project(mut self, elem: PlaceElem<'tcx>) -> Self {
|
||||
self.projection.push(elem);
|
||||
self
|
||||
}
|
||||
|
@ -602,13 +631,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// The "retagging" transformation (for Stacked Borrows) relies on this.
|
||||
let idx = unpack!(block = self.as_temp(block, temp_lifetime, index, Mutability::Not,));
|
||||
|
||||
block = self.bounds_check(
|
||||
block,
|
||||
base_place.clone().into_place(self.tcx, self.typeck_results),
|
||||
idx,
|
||||
expr_span,
|
||||
source_info,
|
||||
);
|
||||
block = self.bounds_check(block, base_place.clone(), idx, expr_span, source_info);
|
||||
|
||||
if is_outermost_index {
|
||||
self.read_fake_borrows(block, fake_borrow_temps, source_info)
|
||||
|
@ -629,7 +652,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fn bounds_check(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
slice: Place<'tcx>,
|
||||
slice: PlaceBuilder<'tcx>,
|
||||
index: Local,
|
||||
expr_span: Span,
|
||||
source_info: SourceInfo,
|
||||
|
@ -641,7 +664,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let lt = self.temp(bool_ty, expr_span);
|
||||
|
||||
// len = len(slice)
|
||||
self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice));
|
||||
self.cfg.push_assign(
|
||||
block,
|
||||
source_info,
|
||||
len,
|
||||
Rvalue::Len(slice.into_place(self.tcx, self.typeck_results)),
|
||||
);
|
||||
// lt = idx < len
|
||||
self.cfg.push_assign(
|
||||
block,
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::build::{BlockAnd, BlockAndExtension, Builder};
|
|||
use crate::thir::*;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::AssertKind;
|
||||
use rustc_middle::mir::Place;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, Ty, UpvarSubsts};
|
||||
use rustc_span::Span;
|
||||
|
@ -164,7 +165,41 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
block.and(Rvalue::Aggregate(box AggregateKind::Tuple, fields))
|
||||
}
|
||||
ExprKind::Closure { closure_id, substs, upvars, movability } => {
|
||||
ExprKind::Closure { closure_id, substs, upvars, movability, ref fake_reads } => {
|
||||
// Convert the closure fake reads, if any, from `ExprRef` to mir `Place`
|
||||
// and push the fake reads.
|
||||
// This must come before creating the operands. This is required in case
|
||||
// there is a fake read and a borrow of the same path, since otherwise the
|
||||
// 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
|
||||
let operands: Vec<_> = upvars
|
||||
.into_iter()
|
||||
|
@ -203,6 +238,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let result = match substs {
|
||||
UpvarSubsts::Generator(substs) => {
|
||||
// We implicitly set the discriminant to 0. See
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
|
||||
mod as_constant;
|
||||
mod as_operand;
|
||||
mod as_place;
|
||||
pub mod as_place;
|
||||
mod as_rvalue;
|
||||
mod as_temp;
|
||||
mod category;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
//! This also includes code for pattern bindings in `let` statements and
|
||||
//! function parameters.
|
||||
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::scope::DropKind;
|
||||
use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard};
|
||||
use crate::build::{BlockAnd, BlockAndExtension, Builder};
|
||||
|
@ -96,7 +97,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let scrutinee_place =
|
||||
unpack!(block = self.lower_scrutinee(block, scrutinee, scrutinee_span,));
|
||||
|
||||
let mut arm_candidates = self.create_match_candidates(scrutinee_place, &arms);
|
||||
let mut arm_candidates = self.create_match_candidates(scrutinee_place.clone(), &arms);
|
||||
|
||||
let match_has_guard = arms.iter().any(|arm| arm.guard.is_some());
|
||||
let mut candidates =
|
||||
|
@ -121,8 +122,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
mut block: BasicBlock,
|
||||
scrutinee: &Expr<'_, 'tcx>,
|
||||
scrutinee_span: Span,
|
||||
) -> BlockAnd<Place<'tcx>> {
|
||||
let scrutinee_place = unpack!(block = self.as_place(block, scrutinee));
|
||||
) -> BlockAnd<PlaceBuilder<'tcx>> {
|
||||
let scrutinee_place_builder = unpack!(block = self.as_place_builder(block, scrutinee));
|
||||
// Matching on a `scrutinee_place` with an uninhabited type doesn't
|
||||
// generate any memory reads by itself, and so if the place "expression"
|
||||
// contains unsafe operations like raw pointer dereferences or union
|
||||
|
@ -140,15 +141,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// check safety.
|
||||
let cause_matched_place = FakeReadCause::ForMatchedPlace;
|
||||
let source_info = self.source_info(scrutinee_span);
|
||||
self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
|
||||
|
||||
block.and(scrutinee_place)
|
||||
if let Ok(scrutinee_builder) =
|
||||
scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
let scrutinee_place = scrutinee_builder.into_place(self.tcx, self.typeck_results);
|
||||
self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
|
||||
}
|
||||
|
||||
block.and(scrutinee_place_builder)
|
||||
}
|
||||
|
||||
/// Create the initial `Candidate`s for a `match` expression.
|
||||
fn create_match_candidates<'pat>(
|
||||
&mut self,
|
||||
scrutinee: Place<'tcx>,
|
||||
scrutinee: PlaceBuilder<'tcx>,
|
||||
arms: &'pat [Arm<'pat, 'tcx>],
|
||||
) -> Vec<(&'pat Arm<'pat, 'tcx>, Candidate<'pat, 'tcx>)> {
|
||||
// Assemble a list of candidates: there is one candidate per pattern,
|
||||
|
@ -156,7 +163,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
arms.iter()
|
||||
.map(|arm| {
|
||||
let arm_has_guard = arm.guard.is_some();
|
||||
let arm_candidate = Candidate::new(scrutinee, &arm.pattern, arm_has_guard);
|
||||
let arm_candidate = Candidate::new(scrutinee.clone(), &arm.pattern, arm_has_guard);
|
||||
(arm, arm_candidate)
|
||||
})
|
||||
.collect()
|
||||
|
@ -222,7 +229,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fn lower_match_arms(
|
||||
&mut self,
|
||||
destination: Place<'tcx>,
|
||||
scrutinee_place: Place<'tcx>,
|
||||
scrutinee_place_builder: PlaceBuilder<'tcx>,
|
||||
scrutinee_span: Span,
|
||||
arm_candidates: Vec<(&'_ Arm<'_, 'tcx>, Candidate<'_, 'tcx>)>,
|
||||
outer_source_info: SourceInfo,
|
||||
|
@ -236,12 +243,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let arm_source_info = self.source_info(arm.span);
|
||||
let arm_scope = (arm.scope, arm_source_info);
|
||||
self.in_scope(arm_scope, arm.lint_level, |this| {
|
||||
// `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 scrutinee_place: Place<'tcx>;
|
||||
if let Ok(scrutinee_builder) = scrutinee_place_builder
|
||||
.clone()
|
||||
.try_upvars_resolved(this.tcx, this.typeck_results)
|
||||
{
|
||||
scrutinee_place =
|
||||
scrutinee_builder.into_place(this.tcx, this.typeck_results);
|
||||
opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span));
|
||||
}
|
||||
let scope = this.declare_bindings(
|
||||
None,
|
||||
arm.span,
|
||||
&arm.pattern,
|
||||
ArmHasGuard(arm.guard.is_some()),
|
||||
Some((Some(&scrutinee_place), scrutinee_span)),
|
||||
opt_scrutinee_place,
|
||||
);
|
||||
|
||||
let arm_block = this.bind_pattern(
|
||||
|
@ -446,8 +474,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
_ => {
|
||||
let place = unpack!(block = self.as_place(block, initializer));
|
||||
self.place_into_pattern(block, irrefutable_pat, place, true)
|
||||
let place_builder = unpack!(block = self.as_place_builder(block, initializer));
|
||||
self.place_into_pattern(block, irrefutable_pat, place_builder, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -456,14 +484,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
&mut self,
|
||||
block: BasicBlock,
|
||||
irrefutable_pat: Pat<'tcx>,
|
||||
initializer: Place<'tcx>,
|
||||
initializer: PlaceBuilder<'tcx>,
|
||||
set_match_place: bool,
|
||||
) -> BlockAnd<()> {
|
||||
let mut candidate = Candidate::new(initializer, &irrefutable_pat, false);
|
||||
|
||||
let mut candidate = Candidate::new(initializer.clone(), &irrefutable_pat, false);
|
||||
let fake_borrow_temps =
|
||||
self.lower_match_tree(block, irrefutable_pat.span, false, &mut [&mut candidate]);
|
||||
|
||||
// For matches and function arguments, the place that is being matched
|
||||
// can be set when creating the variables. But the place for
|
||||
// let PATTERN = ... might not even exist until we do the assignment.
|
||||
|
@ -478,7 +504,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
|
||||
)))) = self.local_decls[local].local_info
|
||||
{
|
||||
*match_place = Some(initializer);
|
||||
// `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) =
|
||||
initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
let place =
|
||||
match_pair_resolved.into_place(self.tcx, self.typeck_results);
|
||||
*match_place = Some(place);
|
||||
}
|
||||
} else {
|
||||
bug!("Let binding to non-user variable.")
|
||||
}
|
||||
|
@ -717,7 +763,7 @@ struct Candidate<'pat, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
|
||||
fn new(place: Place<'tcx>, pattern: &'pat Pat<'tcx>, has_guard: bool) -> Self {
|
||||
fn new(place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, has_guard: bool) -> Self {
|
||||
Candidate {
|
||||
span: pattern.span,
|
||||
has_guard,
|
||||
|
@ -791,7 +837,7 @@ struct Ascription<'tcx> {
|
|||
#[derive(Clone, Debug)]
|
||||
crate struct MatchPair<'pat, 'tcx> {
|
||||
// this place...
|
||||
place: Place<'tcx>,
|
||||
place: PlaceBuilder<'tcx>,
|
||||
|
||||
// ... must match this pattern.
|
||||
pattern: &'pat Pat<'tcx>,
|
||||
|
@ -1198,7 +1244,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
&mut otherwise,
|
||||
pats,
|
||||
or_span,
|
||||
place,
|
||||
place.clone(),
|
||||
fake_borrows,
|
||||
);
|
||||
});
|
||||
|
@ -1224,12 +1270,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
otherwise: &mut Option<BasicBlock>,
|
||||
pats: &'pat [Pat<'tcx>],
|
||||
or_span: Span,
|
||||
place: Place<'tcx>,
|
||||
place: PlaceBuilder<'tcx>,
|
||||
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
|
||||
) {
|
||||
debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats);
|
||||
let mut or_candidates: Vec<_> =
|
||||
pats.iter().map(|pat| Candidate::new(place, pat, candidate.has_guard)).collect();
|
||||
let mut or_candidates: Vec<_> = pats
|
||||
.iter()
|
||||
.map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard))
|
||||
.collect();
|
||||
let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect();
|
||||
let otherwise = if candidate.otherwise_block.is_some() {
|
||||
&mut candidate.otherwise_block
|
||||
|
@ -1412,7 +1460,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// extract the match-pair from the highest priority candidate
|
||||
let match_pair = &candidates.first().unwrap().match_pairs[0];
|
||||
let mut test = self.test(match_pair);
|
||||
let match_place = match_pair.place;
|
||||
let match_place = match_pair.place.clone();
|
||||
|
||||
// most of the time, the test to perform is simply a function
|
||||
// of the main candidate; but for a test like SwitchInt, we
|
||||
|
@ -1438,7 +1486,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
// Insert a Shallow borrow of any places that is switched on.
|
||||
if let Some(fb) = fake_borrows {
|
||||
fb.insert(match_place);
|
||||
if let Ok(match_place_resolved) =
|
||||
match_place.clone().try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
let resolved_place = match_place_resolved.into_place(self.tcx, self.typeck_results);
|
||||
fb.insert(resolved_place);
|
||||
}
|
||||
}
|
||||
|
||||
// perform the test, branching to one of N blocks. For each of
|
||||
|
@ -1456,7 +1509,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// encounter a candidate where the test is not relevant; at
|
||||
// that point, we stop sorting.
|
||||
while let Some(candidate) = candidates.first_mut() {
|
||||
if let Some(idx) = self.sort_candidate(&match_place, &test, candidate) {
|
||||
if let Some(idx) = self.sort_candidate(&match_place.clone(), &test, candidate) {
|
||||
let (candidate, rest) = candidates.split_first_mut().unwrap();
|
||||
target_candidates[idx].push(candidate);
|
||||
candidates = rest;
|
||||
|
@ -1753,23 +1806,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
Guard::IfLet(pat, scrutinee) => {
|
||||
let scrutinee_span = scrutinee.span;
|
||||
let scrutinee_place =
|
||||
let scrutinee_place_builder =
|
||||
unpack!(block = self.lower_scrutinee(block, scrutinee, scrutinee_span));
|
||||
let mut guard_candidate = Candidate::new(scrutinee_place, &pat, false);
|
||||
let mut guard_candidate =
|
||||
Candidate::new(scrutinee_place_builder.clone(), &pat, false);
|
||||
let wildcard = Pat::wildcard_from_ty(pat.ty);
|
||||
let mut otherwise_candidate = Candidate::new(scrutinee_place, &wildcard, false);
|
||||
let mut otherwise_candidate =
|
||||
Candidate::new(scrutinee_place_builder.clone(), &wildcard, false);
|
||||
let fake_borrow_temps = self.lower_match_tree(
|
||||
block,
|
||||
pat.span,
|
||||
false,
|
||||
&mut [&mut guard_candidate, &mut otherwise_candidate],
|
||||
);
|
||||
let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None;
|
||||
let scrutinee_place: Place<'tcx>;
|
||||
if let Ok(scrutinee_builder) =
|
||||
scrutinee_place_builder.try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
scrutinee_place =
|
||||
scrutinee_builder.into_place(self.tcx, self.typeck_results);
|
||||
opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span));
|
||||
}
|
||||
self.declare_bindings(
|
||||
None,
|
||||
pat.span.to(arm_span.unwrap()),
|
||||
pat,
|
||||
ArmHasGuard(false),
|
||||
Some((Some(&scrutinee_place), scrutinee.span)),
|
||||
opt_scrutinee_place,
|
||||
);
|
||||
let post_guard_block = self.bind_pattern(
|
||||
self.source_info(pat.span),
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
//! sort of test: for example, testing which variant an enum is, or
|
||||
//! testing a value against a constant.
|
||||
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
|
||||
use crate::build::Builder;
|
||||
use crate::thir::{self, *};
|
||||
use rustc_hir::RangeEnd;
|
||||
use rustc_middle::mir::Place;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_target::abi::{Integer, Size};
|
||||
|
@ -68,11 +68,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let match_pairs = mem::take(&mut candidate.match_pairs);
|
||||
|
||||
if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, place }] =
|
||||
*match_pairs
|
||||
&*match_pairs
|
||||
{
|
||||
existing_bindings.extend_from_slice(&new_bindings);
|
||||
mem::swap(&mut candidate.bindings, &mut existing_bindings);
|
||||
candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats);
|
||||
candidate.subcandidates =
|
||||
self.create_or_subcandidates(candidate, place.clone(), pats);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -125,12 +126,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fn create_or_subcandidates<'pat>(
|
||||
&mut self,
|
||||
candidate: &Candidate<'pat, 'tcx>,
|
||||
place: Place<'tcx>,
|
||||
place: PlaceBuilder<'tcx>,
|
||||
pats: &'pat [Pat<'tcx>],
|
||||
) -> Vec<Candidate<'pat, 'tcx>> {
|
||||
pats.iter()
|
||||
.map(|pat| {
|
||||
let mut candidate = Candidate::new(place, pat, candidate.has_guard);
|
||||
let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard);
|
||||
self.simplify_candidate(&mut candidate);
|
||||
candidate
|
||||
})
|
||||
|
@ -154,11 +155,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
ascription: thir::pattern::Ascription { variance, user_ty, user_ty_span },
|
||||
} => {
|
||||
// Apply the type ascription to the value at `match_pair.place`, which is the
|
||||
// value being matched, taking the variance field into account.
|
||||
candidate.ascriptions.push(Ascription {
|
||||
span: user_ty_span,
|
||||
user_ty,
|
||||
source: match_pair.place,
|
||||
source: match_pair.place.clone().into_place(self.tcx, self.typeck_results),
|
||||
variance,
|
||||
});
|
||||
|
||||
|
@ -177,7 +177,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
name,
|
||||
mutability,
|
||||
span: match_pair.pattern.span,
|
||||
source: match_pair.place,
|
||||
source: match_pair.place.clone().into_place(self.tcx, self.typeck_results),
|
||||
var_id: var,
|
||||
var_ty: ty,
|
||||
binding_mode: mode,
|
||||
|
@ -264,8 +264,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}) && (adt_def.did.is_local()
|
||||
|| !adt_def.is_variant_list_non_exhaustive());
|
||||
if irrefutable {
|
||||
let place = tcx.mk_place_downcast(match_pair.place, adt_def, variant_index);
|
||||
candidate.match_pairs.extend(self.field_match_pairs(place, subpatterns));
|
||||
let place_builder = match_pair.place.downcast(adt_def, variant_index);
|
||||
candidate
|
||||
.match_pairs
|
||||
.extend(self.field_match_pairs(place_builder, subpatterns));
|
||||
Ok(())
|
||||
} else {
|
||||
Err(match_pair)
|
||||
|
@ -290,8 +292,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
PatKind::Deref { ref subpattern } => {
|
||||
let place = tcx.mk_place_deref(match_pair.place);
|
||||
candidate.match_pairs.push(MatchPair::new(place, subpattern));
|
||||
let place_builder = match_pair.place.deref();
|
||||
candidate.match_pairs.push(MatchPair::new(place_builder, subpattern));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// identify what tests are needed, perform the tests, and then filter
|
||||
// the candidates based on the result.
|
||||
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::matches::{Candidate, MatchPair, Test, TestKind};
|
||||
use crate::build::Builder;
|
||||
use crate::thir::pattern::compare_const_vals;
|
||||
|
@ -81,7 +82,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
pub(super) fn add_cases_to_switch<'pat>(
|
||||
&mut self,
|
||||
test_place: &Place<'tcx>,
|
||||
test_place: &PlaceBuilder<'tcx>,
|
||||
candidate: &Candidate<'pat, 'tcx>,
|
||||
switch_ty: Ty<'tcx>,
|
||||
options: &mut FxIndexMap<&'tcx ty::Const<'tcx>, u128>,
|
||||
|
@ -123,7 +124,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
pub(super) fn add_variants_to_switch<'pat>(
|
||||
&mut self,
|
||||
test_place: &Place<'tcx>,
|
||||
test_place: &PlaceBuilder<'tcx>,
|
||||
candidate: &Candidate<'pat, 'tcx>,
|
||||
variants: &mut BitSet<VariantIdx>,
|
||||
) -> bool {
|
||||
|
@ -151,10 +152,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
pub(super) fn perform_test(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
place: Place<'tcx>,
|
||||
place_builder: PlaceBuilder<'tcx>,
|
||||
test: &Test<'tcx>,
|
||||
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
|
||||
) {
|
||||
let place: Place<'tcx>;
|
||||
if let Ok(test_place_builder) =
|
||||
place_builder.try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
place = test_place_builder.into_place(self.tcx, self.typeck_results);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
debug!(
|
||||
"perform_test({:?}, {:?}: {:?}, {:?})",
|
||||
block,
|
||||
|
@ -481,7 +490,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
/// tighter match code if we do something a bit different.
|
||||
pub(super) fn sort_candidate<'pat>(
|
||||
&mut self,
|
||||
test_place: &Place<'tcx>,
|
||||
test_place: &PlaceBuilder<'tcx>,
|
||||
test: &Test<'tcx>,
|
||||
candidate: &mut Candidate<'pat, 'tcx>,
|
||||
) -> Option<usize> {
|
||||
|
@ -728,7 +737,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
candidate: &mut Candidate<'pat, 'tcx>,
|
||||
) {
|
||||
let match_pair = candidate.match_pairs.remove(match_pair_index);
|
||||
let tcx = self.tcx;
|
||||
|
||||
// So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
|
||||
// we want to create a set of derived match-patterns like
|
||||
|
@ -737,10 +745,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
Some(adt_def.variants[variant_index].ident.name),
|
||||
variant_index,
|
||||
);
|
||||
let downcast_place = tcx.mk_place_elem(match_pair.place, elem); // `(x as Variant)`
|
||||
let downcast_place = match_pair.place.project(elem); // `(x as Variant)`
|
||||
let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
|
||||
// e.g., `(x as Variant).0`
|
||||
let place = tcx.mk_place_field(downcast_place, subpattern.field, subpattern.pattern.ty);
|
||||
let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty);
|
||||
// e.g., `(x as Variant).0 @ P1`
|
||||
MatchPair::new(place, &subpattern.pattern)
|
||||
});
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::matches::MatchPair;
|
||||
use crate::build::Builder;
|
||||
use crate::thir::*;
|
||||
|
@ -9,13 +10,13 @@ use std::convert::TryInto;
|
|||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
crate fn field_match_pairs<'pat>(
|
||||
&mut self,
|
||||
place: Place<'tcx>,
|
||||
place: PlaceBuilder<'tcx>,
|
||||
subpatterns: &'pat [FieldPat<'tcx>],
|
||||
) -> Vec<MatchPair<'pat, 'tcx>> {
|
||||
subpatterns
|
||||
.iter()
|
||||
.map(|fieldpat| {
|
||||
let place = self.tcx.mk_place_field(place, fieldpat.field, fieldpat.pattern.ty);
|
||||
let place = place.clone().field(fieldpat.field, fieldpat.pattern.ty);
|
||||
MatchPair::new(place, &fieldpat.pattern)
|
||||
})
|
||||
.collect()
|
||||
|
@ -24,13 +25,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
crate fn prefix_slice_suffix<'pat>(
|
||||
&mut self,
|
||||
match_pairs: &mut SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
|
||||
place: &Place<'tcx>,
|
||||
place: &PlaceBuilder<'tcx>,
|
||||
prefix: &'pat [Pat<'tcx>],
|
||||
opt_slice: Option<&'pat Pat<'tcx>>,
|
||||
suffix: &'pat [Pat<'tcx>],
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
let (min_length, exact_size) = match place.ty(&self.local_decls, tcx).ty.kind() {
|
||||
let (min_length, exact_size) = match place
|
||||
.clone()
|
||||
.into_place(tcx, self.typeck_results)
|
||||
.ty(&self.local_decls, tcx)
|
||||
.ty
|
||||
.kind()
|
||||
{
|
||||
ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true),
|
||||
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
|
||||
};
|
||||
|
@ -38,20 +45,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
|
||||
let elem =
|
||||
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
|
||||
let place = tcx.mk_place_elem(*place, elem);
|
||||
let place = place.clone().project(elem);
|
||||
MatchPair::new(place, subpattern)
|
||||
}));
|
||||
|
||||
if let Some(subslice_pat) = opt_slice {
|
||||
let suffix_len = suffix.len() as u64;
|
||||
let subslice = tcx.mk_place_elem(
|
||||
*place,
|
||||
ProjectionElem::Subslice {
|
||||
from: prefix.len() as u64,
|
||||
to: if exact_size { min_length - suffix_len } else { suffix_len },
|
||||
from_end: !exact_size,
|
||||
},
|
||||
);
|
||||
let subslice = place.clone().project(ProjectionElem::Subslice {
|
||||
from: prefix.len() as u64,
|
||||
to: if exact_size { min_length - suffix_len } else { suffix_len },
|
||||
from_end: !exact_size,
|
||||
});
|
||||
match_pairs.push(MatchPair::new(subslice, subslice_pat));
|
||||
}
|
||||
|
||||
|
@ -62,7 +66,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
min_length,
|
||||
from_end: !exact_size,
|
||||
};
|
||||
let place = tcx.mk_place_elem(*place, elem);
|
||||
let place = place.clone().project(elem);
|
||||
MatchPair::new(place, subpattern)
|
||||
}));
|
||||
}
|
||||
|
@ -91,7 +95,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
|
||||
crate fn new(place: Place<'tcx>, pattern: &'pat Pat<'tcx>) -> MatchPair<'pat, 'tcx> {
|
||||
crate fn new(place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>) -> MatchPair<'pat, 'tcx> {
|
||||
MatchPair { place, pattern }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::build;
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::scope::DropKind;
|
||||
use crate::thir::{build_thir, Arena, BindingMode, Expr, LintLevel, Pat, PatKind};
|
||||
use rustc_attr::{self as attr, UnwindAttr};
|
||||
|
@ -1004,7 +1005,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
matches::ArmHasGuard(false),
|
||||
Some((Some(&place), span)),
|
||||
);
|
||||
unpack!(block = self.place_into_pattern(block, pattern, place, false));
|
||||
let place_builder = PlaceBuilder::from(local);
|
||||
unpack!(
|
||||
block = self.place_into_pattern(block, pattern, place_builder, false)
|
||||
);
|
||||
}
|
||||
}
|
||||
self.source_scope = original_source_scope;
|
||||
|
|
|
@ -5,6 +5,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_index::vec::Idx;
|
||||
use rustc_middle::hir::place::Place as HirPlace;
|
||||
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
|
||||
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
|
||||
use rustc_middle::mir::interpret::Scalar;
|
||||
|
@ -452,7 +453,21 @@ impl<'thir, 'tcx> Cx<'thir, 'tcx> {
|
|||
.zip(substs.upvar_tys())
|
||||
.map(|(captured_place, ty)| self.capture_upvar(expr, captured_place, ty)),
|
||||
);
|
||||
ExprKind::Closure { closure_id: def_id, substs, upvars, movability }
|
||||
|
||||
// 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) {
|
||||
Some(fake_reads) => fake_reads
|
||||
.iter()
|
||||
.map(|(place, cause, hir_id)| {
|
||||
let expr = self.convert_captured_hir_place(expr, place.clone());
|
||||
let expr_ref: &'thir Expr<'thir, 'tcx> = self.arena.alloc(expr);
|
||||
(expr_ref, *cause, *hir_id)
|
||||
})
|
||||
.collect(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
|
||||
ExprKind::Closure { closure_id: def_id, substs, upvars, movability, fake_reads }
|
||||
}
|
||||
|
||||
hir::ExprKind::Path(ref qpath) => {
|
||||
|
@ -1012,22 +1027,20 @@ impl<'thir, 'tcx> Cx<'thir, 'tcx> {
|
|||
ExprKind::Deref { arg: ref_expr }
|
||||
}
|
||||
|
||||
fn capture_upvar(
|
||||
fn convert_captured_hir_place(
|
||||
&mut self,
|
||||
closure_expr: &'tcx hir::Expr<'tcx>,
|
||||
captured_place: &'tcx ty::CapturedPlace<'tcx>,
|
||||
upvar_ty: Ty<'tcx>,
|
||||
place: HirPlace<'tcx>,
|
||||
) -> Expr<'thir, 'tcx> {
|
||||
let upvar_capture = captured_place.info.capture_kind;
|
||||
let temp_lifetime = self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
|
||||
let var_ty = captured_place.place.base_ty;
|
||||
let var_ty = 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 {
|
||||
let var_hir_id = match place.base {
|
||||
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
|
||||
base => bug!("Expected an upvar, found {:?}", base),
|
||||
};
|
||||
|
@ -1039,7 +1052,7 @@ impl<'thir, 'tcx> Cx<'thir, 'tcx> {
|
|||
kind: self.convert_var(var_hir_id),
|
||||
};
|
||||
|
||||
for proj in captured_place.place.projections.iter() {
|
||||
for proj in place.projections.iter() {
|
||||
let kind = match proj.kind {
|
||||
HirProjectionKind::Deref => {
|
||||
ExprKind::Deref { arg: self.arena.alloc(captured_place_expr) }
|
||||
|
@ -1062,6 +1075,20 @@ impl<'thir, 'tcx> Cx<'thir, 'tcx> {
|
|||
Expr { temp_lifetime, ty: proj.ty, span: closure_expr.span, kind };
|
||||
}
|
||||
|
||||
captured_place_expr
|
||||
}
|
||||
|
||||
fn capture_upvar(
|
||||
&mut self,
|
||||
closure_expr: &'tcx hir::Expr<'tcx>,
|
||||
captured_place: &'tcx ty::CapturedPlace<'tcx>,
|
||||
upvar_ty: Ty<'tcx>,
|
||||
) -> Expr<'thir, 'tcx> {
|
||||
let upvar_capture = captured_place.info.capture_kind;
|
||||
let captured_place_expr =
|
||||
self.convert_captured_hir_place(closure_expr, captured_place.place.clone());
|
||||
let temp_lifetime = self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
|
||||
|
||||
match upvar_capture {
|
||||
ty::UpvarCapture::ByValue(_) => captured_place_expr,
|
||||
ty::UpvarCapture::ByRef(upvar_borrow) => {
|
||||
|
|
|
@ -9,7 +9,7 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::infer::canonical::Canonical;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::{BinOp, BorrowKind, Field, UnOp};
|
||||
use rustc_middle::mir::{BinOp, BorrowKind, FakeReadCause, Field, UnOp};
|
||||
use rustc_middle::ty::adjustment::PointerCast;
|
||||
use rustc_middle::ty::subst::SubstsRef;
|
||||
use rustc_middle::ty::{AdtDef, Const, Ty, UpvarSubsts, UserType};
|
||||
|
@ -281,6 +281,7 @@ pub enum ExprKind<'thir, 'tcx> {
|
|||
substs: UpvarSubsts<'tcx>,
|
||||
upvars: &'thir [Expr<'thir, 'tcx>],
|
||||
movability: Option<hir::Movability>,
|
||||
fake_reads: Vec<(&'thir Expr<'thir, 'tcx>, FakeReadCause, hir::HirId)>,
|
||||
},
|
||||
Literal {
|
||||
literal: &'tcx Const<'tcx>,
|
||||
|
|
|
@ -41,6 +41,7 @@ use rustc_hir::def_id::LocalDefId;
|
|||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
use rustc_infer::infer::UpvarRegion;
|
||||
use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind};
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults, UpvarSubsts};
|
||||
use rustc_session::lint;
|
||||
|
@ -145,6 +146,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM,
|
||||
current_origin: None,
|
||||
capture_information: Default::default(),
|
||||
fake_reads: Default::default(),
|
||||
};
|
||||
euv::ExprUseVisitor::new(
|
||||
&mut delegate,
|
||||
|
@ -246,6 +248,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
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);
|
||||
|
||||
let fake_reads = delegate
|
||||
.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);
|
||||
|
||||
// If we are also inferred the closure kind here,
|
||||
// process any deferred resolutions.
|
||||
let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id);
|
||||
|
@ -1148,6 +1157,7 @@ struct InferBorrowKind<'a, 'tcx> {
|
|||
/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
|
||||
/// ```
|
||||
capture_information: InferredCaptureInformation<'tcx>,
|
||||
fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
||||
|
@ -1409,6 +1419,12 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
|
||||
fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId) {
|
||||
if let PlaceBase::Upvar(_) = place.base {
|
||||
self.fake_reads.push((place, cause, diag_expr_id));
|
||||
}
|
||||
}
|
||||
|
||||
fn consume(
|
||||
&mut self,
|
||||
place_with_id: &PlaceWithHirId<'tcx>,
|
||||
|
|
|
@ -4,11 +4,15 @@
|
|||
|
||||
use crate::check::FnCtxt;
|
||||
|
||||
use rustc_data_structures::stable_map::FxHashMap;
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_middle::hir::place::Place as HirPlace;
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
|
||||
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
|
@ -56,6 +60,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
wbcx.visit_body(body);
|
||||
wbcx.visit_min_capture_map();
|
||||
wbcx.visit_fake_reads_map();
|
||||
wbcx.visit_upvar_capture_map();
|
||||
wbcx.visit_closures();
|
||||
wbcx.visit_liberated_fn_sigs();
|
||||
|
@ -363,6 +368,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
self.typeck_results.closure_min_captures = min_captures_wb;
|
||||
}
|
||||
|
||||
fn visit_fake_reads_map(&mut self) {
|
||||
let mut resolved_closure_fake_reads: FxHashMap<
|
||||
DefId,
|
||||
Vec<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>,
|
||||
> = Default::default();
|
||||
for (closure_def_id, fake_reads) in
|
||||
self.fcx.typeck_results.borrow().closure_fake_reads.iter()
|
||||
{
|
||||
let mut resolved_fake_reads = Vec::<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>::new();
|
||||
for (place, cause, hir_id) in fake_reads.iter() {
|
||||
let locatable =
|
||||
self.tcx().hir().local_def_id_to_hir_id(closure_def_id.expect_local());
|
||||
|
||||
let resolved_fake_read = self.resolve(place.clone(), &locatable);
|
||||
resolved_fake_reads.push((resolved_fake_read, *cause, *hir_id));
|
||||
}
|
||||
resolved_closure_fake_reads.insert(*closure_def_id, resolved_fake_reads);
|
||||
}
|
||||
self.typeck_results.closure_fake_reads = resolved_closure_fake_reads;
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
pub use self::ConsumeMode::*;
|
||||
|
||||
// Export these here so that Clippy can use them.
|
||||
pub use rustc_middle::hir::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::def::Res;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
|
@ -14,6 +15,7 @@ use rustc_hir::PatKind;
|
|||
use rustc_index::vec::Idx;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_middle::hir::place::ProjectionKind;
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::ty::{self, adjustment, TyCtxt};
|
||||
use rustc_target::abi::VariantIdx;
|
||||
|
||||
|
@ -51,6 +53,9 @@ pub trait Delegate<'tcx> {
|
|||
// The path at `assignee_place` is being assigned to.
|
||||
// `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);
|
||||
|
||||
// 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)]
|
||||
|
@ -229,7 +234,61 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
|
||||
hir::ExprKind::Match(ref discr, arms, _) => {
|
||||
let discr_place = return_if_err!(self.mc.cat_expr(&discr));
|
||||
self.borrow_expr(&discr, ty::ImmBorrow);
|
||||
|
||||
// Matching should not always be considered a use of the place, hence
|
||||
// discr does not necessarily need to be borrowed.
|
||||
// We only want to borrow discr if the pattern contain something other
|
||||
// than wildcards.
|
||||
let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self;
|
||||
let mut needs_to_be_read = false;
|
||||
for arm in arms.iter() {
|
||||
return_if_err!(mc.cat_pattern(discr_place.clone(), &arm.pat, |place, pat| {
|
||||
match &pat.kind {
|
||||
PatKind::Binding(.., opt_sub_pat) => {
|
||||
// If the opt_sub_pat is None, than the binding does not count as
|
||||
// a wildcard for the purpose of borrowing discr.
|
||||
if opt_sub_pat.is_none() {
|
||||
needs_to_be_read = true;
|
||||
}
|
||||
}
|
||||
PatKind::TupleStruct(..)
|
||||
| PatKind::Path(..)
|
||||
| PatKind::Struct(..)
|
||||
| PatKind::Tuple(..) => {
|
||||
// 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;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if needs_to_be_read {
|
||||
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.
|
||||
for arm in arms {
|
||||
|
@ -518,6 +577,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn walk_arm(&mut self, discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>) {
|
||||
self.delegate.fake_read(
|
||||
discr_place.place.clone(),
|
||||
FakeReadCause::ForMatchedPlace,
|
||||
discr_place.hir_id,
|
||||
);
|
||||
self.walk_pat(discr_place, &arm.pat);
|
||||
|
||||
if let Some(hir::Guard::If(ref e)) = arm.guard {
|
||||
|
@ -530,6 +594,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
/// 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.)
|
||||
fn walk_irrefutable_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
|
||||
self.delegate.fake_read(
|
||||
discr_place.place.clone(),
|
||||
FakeReadCause::ForLet,
|
||||
discr_place.hir_id,
|
||||
);
|
||||
self.walk_pat(discr_place, pat);
|
||||
}
|
||||
|
||||
|
@ -597,6 +666,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
/// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing
|
||||
/// closure as the DefId.
|
||||
fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) {
|
||||
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);
|
||||
|
||||
let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id();
|
||||
|
@ -608,6 +685,46 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
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) {
|
||||
for (fake_read, cause, hir_id) in fake_reads.iter() {
|
||||
match fake_read.base {
|
||||
PlaceBase::Upvar(upvar_id) => {
|
||||
if upvar_is_local_variable(
|
||||
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 only places we want to fake read before creating the parent closure are the ones that
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
bug!(
|
||||
"Do not know how to get HirId out of Rvalue and StaticItem {:?}",
|
||||
fake_read.base
|
||||
);
|
||||
}
|
||||
};
|
||||
self.delegate.fake_read(fake_read.clone(), *cause, *hir_id);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(min_captures) = self.mc.typeck_results.closure_min_captures.get(&closure_def_id)
|
||||
{
|
||||
for (var_hir_id, min_list) in min_captures.iter() {
|
||||
|
|
|
@ -13,12 +13,8 @@ fn main() {
|
|||
let mut point = SingleVariant::Point(10, -10);
|
||||
|
||||
let c = || {
|
||||
// FIXME(project-rfc-2229#24): Change this to be a destructure pattern
|
||||
// once this is fixed, to remove the warning.
|
||||
if let SingleVariant::Point(ref mut x, _) = point {
|
||||
//~^ WARNING: irrefutable `if let` pattern
|
||||
*x += 1;
|
||||
}
|
||||
let SingleVariant::Point(ref mut x, _) = point;
|
||||
*x += 1;
|
||||
};
|
||||
|
||||
let b = c;
|
||||
|
|
|
@ -7,21 +7,8 @@ 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: irrefutable `if let` pattern
|
||||
--> $DIR/closure-origin-single-variant-diagnostics.rs:18:9
|
||||
|
|
||||
LL | / if let SingleVariant::Point(ref mut x, _) = point {
|
||||
LL | |
|
||||
LL | | *x += 1;
|
||||
LL | | }
|
||||
| |_________^
|
||||
|
|
||||
= note: `#[warn(irrefutable_let_patterns)]` on by default
|
||||
= note: this pattern will always match, so the `if let` is useless
|
||||
= help: consider replacing the `if let` with a `let`
|
||||
|
||||
error[E0382]: use of moved value: `c`
|
||||
--> $DIR/closure-origin-single-variant-diagnostics.rs:25:13
|
||||
--> $DIR/closure-origin-single-variant-diagnostics.rs:21:13
|
||||
|
|
||||
LL | let b = c;
|
||||
| - value moved here
|
||||
|
@ -29,11 +16,11 @@ LL | let a = c;
|
|||
| ^ value used here after move
|
||||
|
|
||||
note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment
|
||||
--> $DIR/closure-origin-single-variant-diagnostics.rs:18:53
|
||||
--> $DIR/closure-origin-single-variant-diagnostics.rs:16:50
|
||||
|
|
||||
LL | if let SingleVariant::Point(ref mut x, _) = point {
|
||||
| ^^^^^
|
||||
LL | let SingleVariant::Point(ref mut x, _) = point;
|
||||
| ^^^^^
|
||||
|
||||
error: aborting due to previous error; 2 warnings emitted
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0382`.
|
||||
|
|
|
@ -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`.
|
|
@ -0,0 +1,28 @@
|
|||
//check-pass
|
||||
#![feature(capture_disjoint_fields)]
|
||||
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
|
||||
|
||||
fn test1() {
|
||||
let foo : [Vec<u8>; 3] = ["String".into(), "String".into(), "String".into()];
|
||||
let c = || {
|
||||
match foo { _ => () };
|
||||
};
|
||||
drop(foo);
|
||||
c();
|
||||
}
|
||||
|
||||
fn test2() {
|
||||
let foo : Option<[Vec<u8>; 3]> = Some(["String".into(), "String".into(), "String".into()]);
|
||||
let c = || {
|
||||
match foo {
|
||||
Some(_) => 1,
|
||||
_ => 2
|
||||
};
|
||||
};
|
||||
c();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test1();
|
||||
test2();
|
||||
}
|
|
@ -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_with_wildcard_match.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
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
//check-pass
|
||||
#![feature(capture_disjoint_fields)]
|
||||
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
|
||||
#![warn(unused)]
|
||||
|
||||
fn main() {
|
||||
let t = (String::from("Hello"), String::from("World"));
|
||||
let g = (String::from("Mr"), String::from("Goose"));
|
||||
|
||||
let a = || {
|
||||
let (_, g2) = g;
|
||||
//~^ WARN unused variable: `g2`
|
||||
let c = || {
|
||||
let (_, t2) = t;
|
||||
//~^ WARN unused variable: `t2`
|
||||
};
|
||||
|
||||
c();
|
||||
};
|
||||
|
||||
a();
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/destructure-pattern-closure-within-closure.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: unused variable: `t2`
|
||||
--> $DIR/destructure-pattern-closure-within-closure.rs:14:21
|
||||
|
|
||||
LL | let (_, t2) = t;
|
||||
| ^^ help: if this is intentional, prefix it with an underscore: `_t2`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/destructure-pattern-closure-within-closure.rs:4:9
|
||||
|
|
||||
LL | #![warn(unused)]
|
||||
| ^^^^^^
|
||||
= note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
|
||||
|
||||
warning: unused variable: `g2`
|
||||
--> $DIR/destructure-pattern-closure-within-closure.rs:11:17
|
||||
|
|
||||
LL | let (_, g2) = g;
|
||||
| ^^ help: if this is intentional, prefix it with an underscore: `_g2`
|
||||
|
||||
warning: 3 warnings emitted
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
//check-pass
|
||||
#![feature(capture_disjoint_fields)]
|
||||
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
|
||||
#![warn(unused)]
|
||||
|
||||
struct Point {
|
||||
x: u32,
|
||||
y: u32,
|
||||
}
|
||||
|
||||
fn test1() {
|
||||
let t = (String::from("Hello"), String::from("World"));
|
||||
|
||||
let c = || {
|
||||
let (t1, t2) = t;
|
||||
//~^ WARN unused variable: `t1`
|
||||
//~| WARN unused variable: `t2`
|
||||
};
|
||||
|
||||
c();
|
||||
}
|
||||
|
||||
fn test2() {
|
||||
let t = (String::from("Hello"), String::from("World"));
|
||||
|
||||
let c = || {
|
||||
let (t1, _) = t;
|
||||
//~^ WARN unused variable: `t1`
|
||||
};
|
||||
|
||||
c();
|
||||
}
|
||||
|
||||
fn test3() {
|
||||
let t = (String::from("Hello"), String::from("World"));
|
||||
|
||||
let c = || {
|
||||
let (_, t2) = t;
|
||||
//~^ WARN unused variable: `t2`
|
||||
};
|
||||
|
||||
c();
|
||||
}
|
||||
|
||||
fn test4() {
|
||||
let t = (String::from("Hello"), String::from("World"));
|
||||
//~^ WARN unused variable: `t`
|
||||
|
||||
let c = || {
|
||||
let (_, _) = t;
|
||||
};
|
||||
|
||||
c();
|
||||
}
|
||||
|
||||
fn test5() {
|
||||
let t = (String::new(), String::new());
|
||||
let _c = || {
|
||||
let _a = match t {
|
||||
(t1, _) => t1,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
fn test6() {
|
||||
let t = (String::new(), String::new());
|
||||
let _c = || {
|
||||
let _a = match t {
|
||||
(_, t2) => t2,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
fn test7() {
|
||||
let t = (String::new(), String::new());
|
||||
let _c = || {
|
||||
let _a = match t {
|
||||
(t1, t2) => (t1, t2),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
fn test8() {
|
||||
let x = 0;
|
||||
//~^ WARN unused variable: `x`
|
||||
let tup = (1, 2);
|
||||
//~^ WARN unused variable: `tup`
|
||||
let p = Point { x: 10, y: 20 };
|
||||
|
||||
let c = || {
|
||||
let _ = x;
|
||||
let Point { x, y } = p;
|
||||
//~^ WARN unused variable: `x`
|
||||
println!("{}", y);
|
||||
let (_, _) = tup;
|
||||
};
|
||||
|
||||
c();
|
||||
}
|
||||
|
||||
fn test9() {
|
||||
let _z = 9;
|
||||
let t = (String::from("Hello"), String::from("World"));
|
||||
|
||||
let c = || {
|
||||
let (_, t) = t;
|
||||
println!("{}", t);
|
||||
};
|
||||
|
||||
c();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
test5();
|
||||
test6();
|
||||
test7();
|
||||
test8();
|
||||
test9();
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/destructure_patterns.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: unused variable: `t1`
|
||||
--> $DIR/destructure_patterns.rs:15:14
|
||||
|
|
||||
LL | let (t1, t2) = t;
|
||||
| ^^ help: if this is intentional, prefix it with an underscore: `_t1`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/destructure_patterns.rs:4:9
|
||||
|
|
||||
LL | #![warn(unused)]
|
||||
| ^^^^^^
|
||||
= note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
|
||||
|
||||
warning: unused variable: `t2`
|
||||
--> $DIR/destructure_patterns.rs:15:18
|
||||
|
|
||||
LL | let (t1, t2) = t;
|
||||
| ^^ help: if this is intentional, prefix it with an underscore: `_t2`
|
||||
|
||||
warning: unused variable: `t1`
|
||||
--> $DIR/destructure_patterns.rs:27:14
|
||||
|
|
||||
LL | let (t1, _) = t;
|
||||
| ^^ help: if this is intentional, prefix it with an underscore: `_t1`
|
||||
|
||||
warning: unused variable: `t2`
|
||||
--> $DIR/destructure_patterns.rs:38:17
|
||||
|
|
||||
LL | let (_, t2) = t;
|
||||
| ^^ help: if this is intentional, prefix it with an underscore: `_t2`
|
||||
|
||||
warning: unused variable: `t`
|
||||
--> $DIR/destructure_patterns.rs:46:9
|
||||
|
|
||||
LL | let t = (String::from("Hello"), String::from("World"));
|
||||
| ^ help: if this is intentional, prefix it with an underscore: `_t`
|
||||
|
||||
warning: unused variable: `x`
|
||||
--> $DIR/destructure_patterns.rs:92:21
|
||||
|
|
||||
LL | let Point { x, y } = p;
|
||||
| ^ help: try ignoring the field: `x: _`
|
||||
|
||||
warning: unused variable: `x`
|
||||
--> $DIR/destructure_patterns.rs:84:9
|
||||
|
|
||||
LL | let x = 0;
|
||||
| ^ help: if this is intentional, prefix it with an underscore: `_x`
|
||||
|
||||
warning: unused variable: `tup`
|
||||
--> $DIR/destructure_patterns.rs:86:9
|
||||
|
|
||||
LL | let tup = (1, 2);
|
||||
| ^^^ help: if this is intentional, prefix it with an underscore: `_tup`
|
||||
|
||||
warning: 9 warnings emitted
|
||||
|
|
@ -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 { _ => () }
|
||||
};
|
||||
}
|
|
@ -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/drop_then_use_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
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
//check-pass
|
||||
#![feature(capture_disjoint_fields)]
|
||||
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
|
||||
#![warn(unused)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(btree_drain_filter)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
|
||||
fn main() {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert("a", ());
|
||||
map.insert("b", ());
|
||||
map.insert("c", ());
|
||||
|
||||
{
|
||||
let mut it = map.drain_filter(|_, _| true);
|
||||
catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err();
|
||||
let result = catch_unwind(AssertUnwindSafe(|| it.next()));
|
||||
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)));
|
||||
}
|
||||
|
||||
}
|
|
@ -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/lit-pattern-matching-with-methods.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
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
//check-pass
|
||||
#![feature(capture_disjoint_fields)]
|
||||
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
|
||||
#![warn(unused)]
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum PointType {
|
||||
TwoD { x: u32, y: u32 },
|
||||
|
||||
ThreeD{ x: u32, y: u32, z: u32 }
|
||||
}
|
||||
|
||||
// Testing struct patterns
|
||||
struct Points {
|
||||
points: Vec<PointType>,
|
||||
}
|
||||
|
||||
impl Points {
|
||||
pub fn test1(&mut self) -> Vec<usize> {
|
||||
(0..self.points.len())
|
||||
.filter_map(|i| {
|
||||
let idx = i as usize;
|
||||
match self.test2(idx) {
|
||||
PointType::TwoD { .. } => Some(i),
|
||||
PointType::ThreeD { .. } => None,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn test2(&mut self, i: usize) -> PointType {
|
||||
self.points[i]
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut points = Points {
|
||||
points: Vec::<PointType>::new()
|
||||
};
|
||||
|
||||
points.points.push(PointType::ThreeD { x:0, y:0, z:0 });
|
||||
points.points.push(PointType::TwoD{ x:0, y:0 });
|
||||
points.points.push(PointType::ThreeD{ x:0, y:0, z:0 });
|
||||
points.points.push(PointType::TwoD{ x:0, y:0 });
|
||||
|
||||
println!("{:?}", points.test1());
|
||||
println!("{:?}", points.points);
|
||||
}
|
|
@ -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/struct-pattern-matching-with-methods.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
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
//check-pass
|
||||
#![feature(capture_disjoint_fields)]
|
||||
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum PointType {
|
||||
TwoD(u32, u32),
|
||||
ThreeD(u32, u32, u32)
|
||||
}
|
||||
|
||||
// Testing tuple struct patterns
|
||||
struct Points {
|
||||
points: Vec<PointType>,
|
||||
}
|
||||
|
||||
impl Points {
|
||||
pub fn test1(&mut self) -> Vec<usize> {
|
||||
(0..self.points.len())
|
||||
.filter_map(|i| {
|
||||
match self.test2(i) {
|
||||
PointType::TwoD (..) => Some(i),
|
||||
PointType::ThreeD (..) => None,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn test2(&mut self, i: usize) -> PointType {
|
||||
self.points[i]
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut points = Points {
|
||||
points: Vec::<PointType>::new()
|
||||
};
|
||||
|
||||
points.points.push(PointType::ThreeD(0,0,0));
|
||||
points.points.push(PointType::TwoD(0,0));
|
||||
points.points.push(PointType::ThreeD(0,0,1));
|
||||
points.points.push(PointType::TwoD(0,1));
|
||||
|
||||
println!("{:?}", points.test1());
|
||||
}
|
|
@ -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/tuple-struct-pattern-matching-with-methods.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
|
||||
|
|
@ -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
|
||||
|
|
@ -2,6 +2,7 @@ use rustc_hir::intravisit;
|
|||
use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::ty::{self, TraitRef, Ty};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
|
@ -184,6 +185,8 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) { }
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
|
||||
|
|
|
@ -4,6 +4,7 @@ use if_chain::if_chain;
|
|||
use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
|
||||
|
@ -106,6 +107,8 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _:HirId) { }
|
||||
}
|
||||
|
||||
impl MutatePairDelegate<'_, '_> {
|
||||
|
|
|
@ -11,6 +11,7 @@ use rustc_hir::intravisit::FnKind;
|
|||
use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Node, PatKind, QPath, TyKind};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::ty::{self, TypeFoldable};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::kw;
|
||||
|
@ -333,4 +334,6 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
|
|||
fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {}
|
||||
|
||||
fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
|
||||
|
||||
fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) { }
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
|
|||
use rustc_hir::{Expr, ExprKind, HirId, Path};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty;
|
||||
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
|
||||
|
@ -77,6 +78,8 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
|
|||
fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
|
||||
self.update(&cmt)
|
||||
}
|
||||
|
||||
fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _:HirId) { }
|
||||
}
|
||||
|
||||
pub struct ParamBindingIdCollector {
|
||||
|
|
Loading…
Reference in New Issue