Auto merge of #99806 - oli-obk:unconstrained_opaque_type, r=estebank
Allow patterns to constrain the hidden type of opaque types fixes #96572 reverts a revert as original PR was a perf regression that was fixed by reverting it: https://github.com/rust-lang/rust/pull/99368#issuecomment-1186587864) TODO: * check if https://github.com/rust-lang/rust/issues/99685 is avoided
This commit is contained in:
commit
4136b59b7d
40 changed files with 384 additions and 179 deletions
|
@ -69,7 +69,7 @@ pub(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, Debug, PartialEq)]
|
||||
pub(crate) struct PlaceBuilder<'tcx> {
|
||||
pub(in crate::build) struct PlaceBuilder<'tcx> {
|
||||
base: PlaceBase,
|
||||
projection: Vec<PlaceElem<'tcx>>,
|
||||
}
|
||||
|
@ -102,6 +102,8 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
|
|||
variant = Some(*idx);
|
||||
continue;
|
||||
}
|
||||
// These do not affect anything, they just make sure we know the right type.
|
||||
ProjectionElem::OpaqueCast(_) => continue,
|
||||
ProjectionElem::Index(..)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. } => {
|
||||
|
@ -168,22 +170,22 @@ fn find_capture_matching_projections<'a, 'tcx>(
|
|||
/// `PlaceBuilder` now starts from `PlaceBase::Local`.
|
||||
///
|
||||
/// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found.
|
||||
fn to_upvars_resolved_place_builder<'a, 'tcx>(
|
||||
#[instrument(level = "trace", skip(cx), ret)]
|
||||
fn to_upvars_resolved_place_builder<'tcx>(
|
||||
from_builder: PlaceBuilder<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
upvars: &'a CaptureMap<'tcx>,
|
||||
cx: &Builder<'_, 'tcx>,
|
||||
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
|
||||
match from_builder.base {
|
||||
PlaceBase::Local(_) => Ok(from_builder),
|
||||
PlaceBase::Upvar { var_hir_id, closure_def_id } => {
|
||||
let Some((capture_index, capture)) =
|
||||
find_capture_matching_projections(
|
||||
upvars,
|
||||
&cx.upvars,
|
||||
var_hir_id,
|
||||
&from_builder.projection,
|
||||
) else {
|
||||
let closure_span = tcx.def_span(closure_def_id);
|
||||
if !enable_precise_capture(tcx, closure_span) {
|
||||
let closure_span = cx.tcx.def_span(closure_def_id);
|
||||
if !enable_precise_capture(cx.tcx, closure_span) {
|
||||
bug!(
|
||||
"No associated capture found for {:?}[{:#?}] even though \
|
||||
capture_disjoint_fields isn't enabled",
|
||||
|
@ -200,12 +202,13 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
|
|||
};
|
||||
|
||||
// Access the capture by accessing the field within the Closure struct.
|
||||
let capture_info = &upvars[capture_index];
|
||||
let capture_info = &cx.upvars[capture_index];
|
||||
|
||||
let mut upvar_resolved_place_builder = PlaceBuilder::from(capture_info.use_place);
|
||||
|
||||
// We used some of the projections to build the capture itself,
|
||||
// now we apply the remaining to the upvar resolved place.
|
||||
trace!(?capture.captured_place, ?from_builder.projection);
|
||||
let remaining_projections = strip_prefix(
|
||||
capture.captured_place.place.base_ty,
|
||||
from_builder.projection,
|
||||
|
@ -230,16 +233,21 @@ fn strip_prefix<'tcx>(
|
|||
prefix_projections: &[HirProjection<'tcx>],
|
||||
) -> impl Iterator<Item = PlaceElem<'tcx>> {
|
||||
let mut iter = projections.into_iter();
|
||||
let mut next = || match iter.next()? {
|
||||
// Filter out opaque casts, they are unnecessary in the prefix.
|
||||
ProjectionElem::OpaqueCast(..) => iter.next(),
|
||||
other => Some(other),
|
||||
};
|
||||
for projection in prefix_projections {
|
||||
match projection.kind {
|
||||
HirProjectionKind::Deref => {
|
||||
assert!(matches!(iter.next(), Some(ProjectionElem::Deref)));
|
||||
assert!(matches!(next(), Some(ProjectionElem::Deref)));
|
||||
}
|
||||
HirProjectionKind::Field(..) => {
|
||||
if base_ty.is_enum() {
|
||||
assert!(matches!(iter.next(), Some(ProjectionElem::Downcast(..))));
|
||||
assert!(matches!(next(), Some(ProjectionElem::Downcast(..))));
|
||||
}
|
||||
assert!(matches!(iter.next(), Some(ProjectionElem::Field(..))));
|
||||
assert!(matches!(next(), Some(ProjectionElem::Field(..))));
|
||||
}
|
||||
HirProjectionKind::Index | HirProjectionKind::Subslice => {
|
||||
bug!("unexpected projection kind: {:?}", projection);
|
||||
|
@ -251,24 +259,16 @@ fn strip_prefix<'tcx>(
|
|||
}
|
||||
|
||||
impl<'tcx> PlaceBuilder<'tcx> {
|
||||
pub(in crate::build) fn into_place<'a>(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
upvars: &'a CaptureMap<'tcx>,
|
||||
) -> Place<'tcx> {
|
||||
pub(in crate::build) fn into_place(self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> {
|
||||
if let PlaceBase::Local(local) = self.base {
|
||||
Place { local, projection: tcx.intern_place_elems(&self.projection) }
|
||||
Place { local, projection: cx.tcx.intern_place_elems(&self.projection) }
|
||||
} else {
|
||||
self.expect_upvars_resolved(tcx, upvars).into_place(tcx, upvars)
|
||||
self.expect_upvars_resolved(cx).into_place(cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_upvars_resolved<'a>(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
upvars: &'a CaptureMap<'tcx>,
|
||||
) -> PlaceBuilder<'tcx> {
|
||||
to_upvars_resolved_place_builder(self, tcx, upvars).unwrap()
|
||||
fn expect_upvars_resolved(self, cx: &Builder<'_, 'tcx>) -> PlaceBuilder<'tcx> {
|
||||
to_upvars_resolved_place_builder(self, cx).unwrap()
|
||||
}
|
||||
|
||||
/// Attempts to resolve the `PlaceBuilder`.
|
||||
|
@ -282,18 +282,21 @@ impl<'tcx> PlaceBuilder<'tcx> {
|
|||
/// 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.
|
||||
pub(in crate::build) fn try_upvars_resolved<'a>(
|
||||
pub(in crate::build) fn try_upvars_resolved(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
upvars: &'a CaptureMap<'tcx>,
|
||||
cx: &Builder<'_, 'tcx>,
|
||||
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
|
||||
to_upvars_resolved_place_builder(self, tcx, upvars)
|
||||
to_upvars_resolved_place_builder(self, cx)
|
||||
}
|
||||
|
||||
pub(crate) fn base(&self) -> PlaceBase {
|
||||
self.base
|
||||
}
|
||||
|
||||
pub(crate) fn projection(&self) -> &[PlaceElem<'tcx>] {
|
||||
&self.projection
|
||||
}
|
||||
|
||||
pub(crate) fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
|
||||
self.project(PlaceElem::Field(f, ty))
|
||||
}
|
||||
|
@ -353,7 +356,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
expr: &Expr<'tcx>,
|
||||
) -> BlockAnd<Place<'tcx>> {
|
||||
let place_builder = unpack!(block = self.as_place_builder(block, expr));
|
||||
block.and(place_builder.into_place(self.tcx, &self.upvars))
|
||||
block.and(place_builder.into_place(self))
|
||||
}
|
||||
|
||||
/// This is used when constructing a compound `Place`, so that we can avoid creating
|
||||
|
@ -377,7 +380,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
expr: &Expr<'tcx>,
|
||||
) -> BlockAnd<Place<'tcx>> {
|
||||
let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
|
||||
block.and(place_builder.into_place(self.tcx, &self.upvars))
|
||||
block.and(place_builder.into_place(self))
|
||||
}
|
||||
|
||||
/// This is used when constructing a compound `Place`, so that we can avoid creating
|
||||
|
@ -472,7 +475,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
inferred_ty: expr.ty,
|
||||
});
|
||||
|
||||
let place = place_builder.clone().into_place(this.tcx, &this.upvars);
|
||||
let place = place_builder.clone().into_place(this);
|
||||
this.cfg.push(
|
||||
block,
|
||||
Statement {
|
||||
|
@ -610,7 +613,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
if is_outermost_index {
|
||||
self.read_fake_borrows(block, fake_borrow_temps, source_info)
|
||||
} else {
|
||||
base_place = base_place.expect_upvars_resolved(self.tcx, &self.upvars);
|
||||
base_place = base_place.expect_upvars_resolved(self);
|
||||
self.add_fake_borrows_of_base(
|
||||
&base_place,
|
||||
block,
|
||||
|
@ -638,12 +641,7 @@ 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.into_place(self.tcx, &self.upvars)),
|
||||
);
|
||||
self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.into_place(self)));
|
||||
// lt = idx < len
|
||||
self.cfg.push_assign(
|
||||
block,
|
||||
|
@ -723,6 +721,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
ProjectionElem::Field(..)
|
||||
| ProjectionElem::Downcast(..)
|
||||
| ProjectionElem::OpaqueCast(..)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. } => (),
|
||||
}
|
||||
|
|
|
@ -329,10 +329,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let place_builder =
|
||||
unpack!(block = this.as_place_builder(block, &this.thir[*thir_place]));
|
||||
|
||||
if let Ok(place_builder_resolved) =
|
||||
place_builder.try_upvars_resolved(this.tcx, &this.upvars)
|
||||
{
|
||||
let mir_place = place_builder_resolved.into_place(this.tcx, &this.upvars);
|
||||
if let Ok(place_builder_resolved) = place_builder.try_upvars_resolved(this) {
|
||||
let mir_place = place_builder_resolved.into_place(this);
|
||||
this.cfg.push_fake_read(
|
||||
block,
|
||||
this.source_info(this.tcx.hir().span(*hir_id)),
|
||||
|
@ -623,8 +621,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// by the parent itself. The mutability of the current capture
|
||||
// is same as that of the capture in the parent closure.
|
||||
PlaceBase::Upvar { .. } => {
|
||||
let enclosing_upvars_resolved =
|
||||
arg_place_builder.clone().into_place(this.tcx, &this.upvars);
|
||||
let enclosing_upvars_resolved = arg_place_builder.clone().into_place(this);
|
||||
|
||||
match enclosing_upvars_resolved.as_ref() {
|
||||
PlaceRef {
|
||||
|
@ -661,7 +658,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
|
||||
};
|
||||
|
||||
let arg_place = arg_place_builder.into_place(this.tcx, &this.upvars);
|
||||
let arg_place = arg_place_builder.into_place(this);
|
||||
|
||||
this.cfg.push_assign(
|
||||
block,
|
||||
|
|
|
@ -23,6 +23,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability))
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn as_temp_inner(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
|
@ -30,10 +31,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
expr: &Expr<'tcx>,
|
||||
mutability: Mutability,
|
||||
) -> BlockAnd<Local> {
|
||||
debug!(
|
||||
"as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
|
||||
block, temp_lifetime, expr, mutability
|
||||
);
|
||||
let this = self;
|
||||
|
||||
let expr_span = expr.span;
|
||||
|
|
|
@ -15,14 +15,13 @@ use std::iter;
|
|||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
/// Compile `expr`, storing the result into `destination`, which
|
||||
/// is assumed to be uninitialized.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn expr_into_dest(
|
||||
&mut self,
|
||||
destination: Place<'tcx>,
|
||||
mut block: BasicBlock,
|
||||
expr: &Expr<'tcx>,
|
||||
) -> BlockAnd<()> {
|
||||
debug!("expr_into_dest(destination={:?}, block={:?}, expr={:?})", destination, block, expr);
|
||||
|
||||
// since we frequently have to reference `self` from within a
|
||||
// closure, where `self` would be shadowed, it's easier to
|
||||
// just use the name `this` uniformly
|
||||
|
@ -366,7 +365,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
None => {
|
||||
let place_builder = place_builder.clone();
|
||||
this.consume_by_copy_or_move(
|
||||
place_builder.field(n, *ty).into_place(this.tcx, &this.upvars),
|
||||
place_builder.field(n, *ty).into_place(this),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -220,10 +220,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
|
||||
let source_info = self.source_info(scrutinee_span);
|
||||
|
||||
if let Ok(scrutinee_builder) =
|
||||
scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, &self.upvars)
|
||||
{
|
||||
let scrutinee_place = scrutinee_builder.into_place(self.tcx, &self.upvars);
|
||||
if let Ok(scrutinee_builder) = scrutinee_place_builder.clone().try_upvars_resolved(self) {
|
||||
let scrutinee_place = scrutinee_builder.into_place(self);
|
||||
self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
|
||||
}
|
||||
|
||||
|
@ -246,7 +244,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
.map(|arm| {
|
||||
let arm = &self.thir[arm];
|
||||
let arm_has_guard = arm.guard.is_some();
|
||||
let arm_candidate = Candidate::new(scrutinee.clone(), &arm.pattern, arm_has_guard);
|
||||
let arm_candidate =
|
||||
Candidate::new(scrutinee.clone(), &arm.pattern, arm_has_guard, self);
|
||||
(arm, arm_candidate)
|
||||
})
|
||||
.collect()
|
||||
|
@ -349,9 +348,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
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.upvars)
|
||||
scrutinee_place_builder.clone().try_upvars_resolved(this)
|
||||
{
|
||||
scrutinee_place = scrutinee_builder.into_place(this.tcx, &this.upvars);
|
||||
scrutinee_place = scrutinee_builder.into_place(this);
|
||||
opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span));
|
||||
}
|
||||
let scope = this.declare_bindings(
|
||||
|
@ -584,7 +583,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
initializer: PlaceBuilder<'tcx>,
|
||||
set_match_place: bool,
|
||||
) -> BlockAnd<()> {
|
||||
let mut candidate = Candidate::new(initializer.clone(), &irrefutable_pat, false);
|
||||
let mut candidate = Candidate::new(initializer.clone(), &irrefutable_pat, false, self);
|
||||
let fake_borrow_temps = self.lower_match_tree(
|
||||
block,
|
||||
irrefutable_pat.span,
|
||||
|
@ -601,12 +600,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
while let Some(next) = {
|
||||
for binding in &candidate_ref.bindings {
|
||||
let local = self.var_local_id(binding.var_id, OutsideGuard);
|
||||
|
||||
let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
|
||||
VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
|
||||
)))) = self.local_decls[local].local_info else {
|
||||
bug!("Let binding to non-user variable.")
|
||||
};
|
||||
// `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
|
||||
|
@ -621,10 +614,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// let (v1, v2) = foo;
|
||||
// };
|
||||
// ```
|
||||
if let Ok(match_pair_resolved) =
|
||||
initializer.clone().try_upvars_resolved(self.tcx, &self.upvars)
|
||||
{
|
||||
let place = match_pair_resolved.into_place(self.tcx, &self.upvars);
|
||||
if let Ok(match_pair_resolved) = initializer.clone().try_upvars_resolved(self) {
|
||||
let place = match_pair_resolved.into_place(self);
|
||||
|
||||
let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
|
||||
VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
|
||||
)))) = self.local_decls[local].local_info else {
|
||||
bug!("Let binding to non-user variable.")
|
||||
};
|
||||
*match_place = Some(place);
|
||||
}
|
||||
}
|
||||
|
@ -654,6 +651,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
/// scope for the bindings in these patterns, if such a scope had to be
|
||||
/// created. NOTE: Declaring the bindings should always be done in their
|
||||
/// drop scope.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub(crate) fn declare_bindings(
|
||||
&mut self,
|
||||
mut visibility_scope: Option<SourceScope>,
|
||||
|
@ -662,7 +660,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
has_guard: ArmHasGuard,
|
||||
opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
|
||||
) -> Option<SourceScope> {
|
||||
debug!("declare_bindings: pattern={:?}", pattern);
|
||||
self.visit_primary_bindings(
|
||||
&pattern,
|
||||
UserTypeProjections::none(),
|
||||
|
@ -868,11 +865,16 @@ struct Candidate<'pat, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
|
||||
fn new(place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, has_guard: bool) -> Self {
|
||||
fn new(
|
||||
place: PlaceBuilder<'tcx>,
|
||||
pattern: &'pat Pat<'tcx>,
|
||||
has_guard: bool,
|
||||
cx: &Builder<'_, 'tcx>,
|
||||
) -> Self {
|
||||
Candidate {
|
||||
span: pattern.span,
|
||||
has_guard,
|
||||
match_pairs: smallvec![MatchPair { place, pattern }],
|
||||
match_pairs: smallvec![MatchPair::new(place, pattern, cx)],
|
||||
bindings: Vec::new(),
|
||||
ascriptions: Vec::new(),
|
||||
subcandidates: Vec::new(),
|
||||
|
@ -1048,6 +1050,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
/// if `x.0` matches `false` (for the third arm). In the (impossible at
|
||||
/// runtime) case when `x.0` is now `true`, we branch to
|
||||
/// `otherwise_block`.
|
||||
#[instrument(skip(self, fake_borrows), level = "debug")]
|
||||
fn match_candidates<'pat>(
|
||||
&mut self,
|
||||
span: Span,
|
||||
|
@ -1057,11 +1060,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
candidates: &mut [&mut Candidate<'pat, 'tcx>],
|
||||
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
|
||||
) {
|
||||
debug!(
|
||||
"matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})",
|
||||
span, candidates, start_block, otherwise_block,
|
||||
);
|
||||
|
||||
// Start by simplifying candidates. Once this process is complete, all
|
||||
// the match pairs which remain require some form of test, whether it
|
||||
// be a switch or pattern comparison.
|
||||
|
@ -1380,6 +1378,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
)
|
||||
}
|
||||
|
||||
#[instrument(
|
||||
skip(self, otherwise, or_span, place, fake_borrows, candidate, pats),
|
||||
level = "debug"
|
||||
)]
|
||||
fn test_or_pattern<'pat>(
|
||||
&mut self,
|
||||
candidate: &mut Candidate<'pat, 'tcx>,
|
||||
|
@ -1389,10 +1391,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
place: PlaceBuilder<'tcx>,
|
||||
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
|
||||
) {
|
||||
debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats);
|
||||
debug!("candidate={:#?}\npats={:#?}", candidate, pats);
|
||||
let mut or_candidates: Vec<_> = pats
|
||||
.iter()
|
||||
.map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard))
|
||||
.map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard, self))
|
||||
.collect();
|
||||
let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect();
|
||||
let otherwise = if candidate.otherwise_block.is_some() {
|
||||
|
@ -1605,9 +1607,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
// Insert a Shallow borrow of any places that is switched on.
|
||||
if let Some(fb) = fake_borrows && let Ok(match_place_resolved) =
|
||||
match_place.clone().try_upvars_resolved(self.tcx, &self.upvars)
|
||||
match_place.clone().try_upvars_resolved(self)
|
||||
{
|
||||
let resolved_place = match_place_resolved.into_place(self.tcx, &self.upvars);
|
||||
let resolved_place = match_place_resolved.into_place(self);
|
||||
fb.insert(resolved_place);
|
||||
}
|
||||
|
||||
|
@ -1634,9 +1636,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
candidates = rest;
|
||||
}
|
||||
// at least the first candidate ought to be tested
|
||||
assert!(total_candidate_count > candidates.len());
|
||||
debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len());
|
||||
debug!("test_candidates: untested_candidates: {}", candidates.len());
|
||||
assert!(
|
||||
total_candidate_count > candidates.len(),
|
||||
"{}, {:#?}",
|
||||
total_candidate_count,
|
||||
candidates
|
||||
);
|
||||
debug!("tested_candidates: {}", total_candidate_count - candidates.len());
|
||||
debug!("untested_candidates: {}", candidates.len());
|
||||
|
||||
// HACK(matthewjasper) This is a closure so that we can let the test
|
||||
// create its blocks before the rest of the match. This currently
|
||||
|
@ -1783,8 +1790,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let expr_span = expr.span;
|
||||
let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr, expr_span));
|
||||
let wildcard = Pat::wildcard_from_ty(pat.ty);
|
||||
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), &pat, false);
|
||||
let mut otherwise_candidate = Candidate::new(expr_place_builder.clone(), &wildcard, false);
|
||||
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), &pat, false, self);
|
||||
let mut otherwise_candidate =
|
||||
Candidate::new(expr_place_builder.clone(), &wildcard, false, self);
|
||||
let fake_borrow_temps = self.lower_match_tree(
|
||||
block,
|
||||
pat.span,
|
||||
|
@ -1794,8 +1802,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
);
|
||||
let mut opt_expr_place: Option<(Option<&Place<'tcx>>, Span)> = None;
|
||||
let expr_place: Place<'tcx>;
|
||||
if let Ok(expr_builder) = expr_place_builder.try_upvars_resolved(self.tcx, &self.upvars) {
|
||||
expr_place = expr_builder.into_place(self.tcx, &self.upvars);
|
||||
if let Ok(expr_builder) = expr_place_builder.try_upvars_resolved(self) {
|
||||
expr_place = expr_builder.into_place(self);
|
||||
opt_expr_place = Some((Some(&expr_place), expr_span));
|
||||
}
|
||||
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
|
||||
|
@ -2209,6 +2217,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
/// first local is a binding for occurrences of `var` in the guard, which
|
||||
/// will have type `&T`. The second local is a binding for occurrences of
|
||||
/// `var` in the arm body, which will have type `T`.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn declare_binding(
|
||||
&mut self,
|
||||
source_info: SourceInfo,
|
||||
|
@ -2223,19 +2232,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
|
||||
pat_span: Span,
|
||||
) {
|
||||
debug!(
|
||||
"declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \
|
||||
visibility_scope={:?}, source_info={:?})",
|
||||
var_id, name, mode, var_ty, visibility_scope, source_info
|
||||
);
|
||||
|
||||
let tcx = self.tcx;
|
||||
let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope };
|
||||
let binding_mode = match mode {
|
||||
BindingMode::ByValue => ty::BindingMode::BindByValue(mutability),
|
||||
BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability),
|
||||
};
|
||||
debug!("declare_binding: user_ty={:?}", user_ty);
|
||||
let local = LocalDecl::<'tcx> {
|
||||
mutability,
|
||||
ty: var_ty,
|
||||
|
@ -2285,7 +2287,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
} else {
|
||||
LocalsForNode::One(for_arm_body)
|
||||
};
|
||||
debug!("declare_binding: vars={:?}", locals);
|
||||
debug!(?locals);
|
||||
self.var_indices.insert(var_id, locals);
|
||||
}
|
||||
|
||||
|
@ -2302,8 +2304,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let (matching, failure) = self.in_if_then_scope(*let_else_scope, |this| {
|
||||
let scrutinee = unpack!(block = this.lower_scrutinee(block, init, initializer_span));
|
||||
let pat = Pat { ty: init.ty, span: else_block_span, kind: PatKind::Wild };
|
||||
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
|
||||
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false);
|
||||
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false, this);
|
||||
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false, this);
|
||||
let fake_borrow_temps = this.lower_match_tree(
|
||||
block,
|
||||
initializer_span,
|
||||
|
|
|
@ -37,12 +37,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
///
|
||||
/// only generates a single switch. If this happens this method returns
|
||||
/// `true`.
|
||||
#[instrument(skip(self, candidate), level = "debug")]
|
||||
pub(super) fn simplify_candidate<'pat>(
|
||||
&mut self,
|
||||
candidate: &mut Candidate<'pat, 'tcx>,
|
||||
) -> bool {
|
||||
// repeatedly simplify match pairs until fixed point is reached
|
||||
debug!(?candidate, "simplify_candidate");
|
||||
debug!("{candidate:#?}");
|
||||
|
||||
// existing_bindings and new_bindings exists to keep the semantics in order.
|
||||
// Reversing the binding order for bindings after `@` changes the binding order in places
|
||||
|
@ -131,7 +132,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
) -> Vec<Candidate<'pat, 'tcx>> {
|
||||
pats.iter()
|
||||
.map(|box pat| {
|
||||
let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard);
|
||||
let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard, self);
|
||||
self.simplify_candidate(&mut candidate);
|
||||
candidate
|
||||
})
|
||||
|
@ -155,17 +156,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
ascription: thir::Ascription { ref annotation, variance },
|
||||
} => {
|
||||
// Apply the type ascription to the value at `match_pair.place`, which is the
|
||||
if let Ok(place_resolved) =
|
||||
match_pair.place.clone().try_upvars_resolved(self.tcx, &self.upvars)
|
||||
{
|
||||
if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) {
|
||||
candidate.ascriptions.push(Ascription {
|
||||
annotation: annotation.clone(),
|
||||
source: place_resolved.into_place(self.tcx, &self.upvars),
|
||||
source: place_resolved.into_place(self),
|
||||
variance,
|
||||
});
|
||||
}
|
||||
|
||||
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
|
||||
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -184,12 +183,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
ref subpattern,
|
||||
is_primary: _,
|
||||
} => {
|
||||
if let Ok(place_resolved) =
|
||||
match_pair.place.clone().try_upvars_resolved(self.tcx, &self.upvars)
|
||||
{
|
||||
if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) {
|
||||
candidate.bindings.push(Binding {
|
||||
span: match_pair.pattern.span,
|
||||
source: place_resolved.into_place(self.tcx, &self.upvars),
|
||||
source: place_resolved.into_place(self),
|
||||
var_id: var,
|
||||
binding_mode: mode,
|
||||
});
|
||||
|
@ -197,7 +194,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
if let Some(subpattern) = subpattern.as_ref() {
|
||||
// this is the `x @ P` case; have to keep matching against `P` now
|
||||
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
|
||||
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -308,7 +305,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
PatKind::Deref { ref subpattern } => {
|
||||
let place_builder = match_pair.place.deref();
|
||||
candidate.match_pairs.push(MatchPair::new(place_builder, subpattern));
|
||||
candidate.match_pairs.push(MatchPair::new(place_builder, subpattern, self));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -144,6 +144,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, make_target_blocks, place_builder), level = "debug")]
|
||||
pub(super) fn perform_test(
|
||||
&mut self,
|
||||
match_start_span: Span,
|
||||
|
@ -153,19 +154,9 @@ impl<'a, 'tcx> Builder<'a, '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.upvars) {
|
||||
place = test_place_builder.into_place(self.tcx, &self.upvars);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
debug!(
|
||||
"perform_test({:?}, {:?}: {:?}, {:?})",
|
||||
block,
|
||||
place,
|
||||
place.ty(&self.local_decls, self.tcx),
|
||||
test
|
||||
);
|
||||
let place = place_builder.into_place(self);
|
||||
let place_ty = place.ty(&self.local_decls, self.tcx);
|
||||
debug!(?place, ?place_ty,);
|
||||
|
||||
let source_info = self.source_info(test.span);
|
||||
match test.kind {
|
||||
|
@ -733,14 +724,12 @@ impl<'a, 'tcx> Builder<'a, '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
|
||||
// `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
|
||||
let elem =
|
||||
ProjectionElem::Downcast(Some(adt_def.variant(variant_index).name), variant_index);
|
||||
let downcast_place = match_pair.place.project(elem); // `(x as Variant)`
|
||||
let downcast_place = match_pair.place.downcast(adt_def, variant_index); // `(x as Variant)`
|
||||
let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
|
||||
// e.g., `(x as Variant).0`
|
||||
let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty);
|
||||
// e.g., `(x as Variant).0 @ P1`
|
||||
MatchPair::new(place, &subpattern.pattern)
|
||||
MatchPair::new(place, &subpattern.pattern, self)
|
||||
});
|
||||
|
||||
candidate.match_pairs.extend(consequent_match_pairs);
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use crate::build::expr::as_place::PlaceBase;
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::matches::MatchPair;
|
||||
use crate::build::Builder;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::TypeVisitable;
|
||||
use smallvec::SmallVec;
|
||||
use std::convert::TryInto;
|
||||
|
||||
|
@ -17,7 +19,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
.iter()
|
||||
.map(|fieldpat| {
|
||||
let place = place.clone().field(fieldpat.field, fieldpat.pattern.ty);
|
||||
MatchPair::new(place, &fieldpat.pattern)
|
||||
MatchPair::new(place, &fieldpat.pattern, self)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
@ -31,23 +33,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
suffix: &'pat [Box<Pat<'tcx>>],
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
let (min_length, exact_size) = if let Ok(place_resolved) =
|
||||
place.clone().try_upvars_resolved(tcx, &self.upvars)
|
||||
{
|
||||
match place_resolved.into_place(tcx, &self.upvars).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),
|
||||
}
|
||||
} else {
|
||||
((prefix.len() + suffix.len()).try_into().unwrap(), false)
|
||||
};
|
||||
let (min_length, exact_size) =
|
||||
if let Ok(place_resolved) = place.clone().try_upvars_resolved(self) {
|
||||
match place_resolved.into_place(self).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),
|
||||
}
|
||||
} else {
|
||||
((prefix.len() + suffix.len()).try_into().unwrap(), false)
|
||||
};
|
||||
|
||||
match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
|
||||
let elem =
|
||||
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
|
||||
let place = place.clone().project(elem);
|
||||
MatchPair::new(place, subpattern)
|
||||
MatchPair::new(place, subpattern, self)
|
||||
}));
|
||||
|
||||
if let Some(subslice_pat) = opt_slice {
|
||||
|
@ -57,7 +57,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
to: if exact_size { min_length - suffix_len } else { suffix_len },
|
||||
from_end: !exact_size,
|
||||
});
|
||||
match_pairs.push(MatchPair::new(subslice, subslice_pat));
|
||||
match_pairs.push(MatchPair::new(subslice, subslice_pat, self));
|
||||
}
|
||||
|
||||
match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
|
||||
|
@ -68,7 +68,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
from_end: !exact_size,
|
||||
};
|
||||
let place = place.clone().project(elem);
|
||||
MatchPair::new(place, subpattern)
|
||||
MatchPair::new(place, subpattern, self)
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -96,10 +96,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
|
||||
pub(crate) fn new(
|
||||
pub(in crate::build) fn new(
|
||||
place: PlaceBuilder<'tcx>,
|
||||
pattern: &'pat Pat<'tcx>,
|
||||
cx: &Builder<'_, 'tcx>,
|
||||
) -> MatchPair<'pat, 'tcx> {
|
||||
// Force the place type to the pattern's type.
|
||||
// FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
|
||||
let mut place = match place.try_upvars_resolved(cx) {
|
||||
Ok(val) | Err(val) => val,
|
||||
};
|
||||
|
||||
// Only add the OpaqueCast projection if the given place is an opaque type and the
|
||||
// expected type from the pattern is not.
|
||||
let may_need_cast = match place.base() {
|
||||
PlaceBase::Local(local) => {
|
||||
let ty = Place::ty_from(local, place.projection(), &cx.local_decls, cx.tcx).ty;
|
||||
ty != pattern.ty && ty.has_opaque_types()
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
if may_need_cast {
|
||||
place = place.project(ProjectionElem::OpaqueCast(pattern.ty));
|
||||
}
|
||||
MatchPair { place, pattern }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -553,6 +553,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
/// Convenience wrapper that pushes a scope and then executes `f`
|
||||
/// to build its contents, popping the scope afterwards.
|
||||
#[instrument(skip(self, f), level = "debug")]
|
||||
pub(crate) fn in_scope<F, R>(
|
||||
&mut self,
|
||||
region_scope: (region::Scope, SourceInfo),
|
||||
|
@ -562,7 +563,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
where
|
||||
F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
|
||||
{
|
||||
debug!("in_scope(region_scope={:?})", region_scope);
|
||||
let source_scope = self.source_scope;
|
||||
let tcx = self.tcx;
|
||||
if let LintLevel::Explicit(current_hir_id) = lint_level {
|
||||
|
@ -589,7 +589,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let rv = unpack!(block = f(self));
|
||||
unpack!(block = self.pop_scope(region_scope, block));
|
||||
self.source_scope = source_scope;
|
||||
debug!("in_scope: exiting region_scope={:?} block={:?}", region_scope, block);
|
||||
debug!(?block);
|
||||
block.and(rv)
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,8 @@ impl<'tcx> Cx<'tcx> {
|
|||
_ => None,
|
||||
};
|
||||
|
||||
trace!(?expr.ty);
|
||||
|
||||
// Now apply adjustments, if any.
|
||||
for adjustment in self.typeck_results.expr_adjustments(hir_expr) {
|
||||
trace!(?expr, ?adjustment);
|
||||
|
@ -56,6 +58,8 @@ impl<'tcx> Cx<'tcx> {
|
|||
self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span));
|
||||
}
|
||||
|
||||
trace!(?expr.ty, "after adjustments");
|
||||
|
||||
// Next, wrap this up in the expr's scope.
|
||||
expr = Expr {
|
||||
temp_lifetime,
|
||||
|
|
|
@ -200,6 +200,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
|
||||
let mut ty = self.typeck_results.node_type(pat.hir_id);
|
||||
let mut span = pat.span;
|
||||
|
|
|
@ -842,7 +842,15 @@ fn is_useful<'p, 'tcx>(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
let ty = v.head().ty();
|
||||
let mut ty = v.head().ty();
|
||||
|
||||
// Opaque types can't get destructured/split, but the patterns can
|
||||
// actually hint at hidden types, so we use the patterns' types instead.
|
||||
if let ty::Opaque(..) = ty.kind() {
|
||||
if let Some(row) = rows.first() {
|
||||
ty = row.head().ty();
|
||||
}
|
||||
}
|
||||
let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
|
||||
debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span());
|
||||
let pcx = &PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue