1
Fork 0

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:
bors 2021-03-16 19:19:06 +00:00
commit f5d8117c33
40 changed files with 1473 additions and 127 deletions

View file

@ -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,

View file

@ -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

View file

@ -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;

View file

@ -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),

View file

@ -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(())
}

View file

@ -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)
});

View file

@ -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 }
}
}

View file

@ -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;

View file

@ -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) => {

View file

@ -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>,