Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs')
| -rw-r--r-- | crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs | 97 |
1 files changed, 90 insertions, 7 deletions
diff --git a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index 44dc1a63d1..b42127cce6 100644 --- a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -17,13 +17,15 @@ use hir_def::{ use macros::{TypeFoldable, TypeVisitable}; use rustc_type_ir::inherent::{IntoKind, Ty as _}; use smallvec::{SmallVec, smallvec}; +use stdx::impl_from; use syntax::ast::{BinaryOp, UnaryOp}; use tracing::{debug, instrument, trace}; use crate::{ Adjust, Adjustment, AutoBorrow, Span, infer::{ - ByRef, CaptureSourceStack, InferenceContext, UpvarCapture, closure::analysis::BorrowKind, + ByRef, CaptureSourceStack, DerefPatBorrowMode, InferenceContext, PatAdjust, PatAdjustment, + UpvarCapture, closure::analysis::BorrowKind, }, method_resolution::CandidateId, next_solver::{ErrorGuaranteed, StoredTy, Ty, TyKind}, @@ -795,6 +797,11 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { self.cx.result.expr_adjustment(expr).unwrap_or_default().into() } + fn pat_adjustments(&self, pat: PatId) -> SmallVec<[PatAdjustment; 5]> { + // Due to borrowck problems, we cannot borrow the adjustments, unfortunately. + self.cx.result.pat_adjustment(pat).unwrap_or_default().into() + } + /// Invoke the appropriate delegate calls for anything that gets /// consumed or borrowed as part of the automatic adjustment /// process. @@ -877,6 +884,31 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { #[instrument(skip(self), level = "debug")] fn walk_pat(&mut self, discr_place: PlaceWithOrigin, pat: PatId, has_guard: bool) -> Result { self.cat_pattern(discr_place.clone(), pat, &mut |this, place, pat| { + let walk_deref_pat = |this: &mut Self, subpattern: PatId, place: PlaceWithOrigin| { + // A deref pattern is a bit special: the binding mode of its inner bindings + // determines whether to borrow *at the level of the deref pattern* rather than + // borrowing the bound place (since that inner place is inside the temporary that + // stores the result of calling `deref()`/`deref_mut()` so can't be captured). + // Deref patterns on boxes don't borrow, so we ignore them here. + // HACK: this could be a fake pattern corresponding to a deref inserted by match + // ergonomics, in which case `pat.hir_id` will be the id of the subpattern. + if let DerefPatBorrowMode::Borrow(mutability) = + this.cx.deref_pat_borrow_mode(place.place.ty(), subpattern) + { + let bk = BorrowKind::from_mutbl(mutability); + this.delegate.borrow(place, bk, this.cx); + } + }; + + let pat = match pat { + CatPatternPat::PatId(pat) => pat, + CatPatternPat::DerefPat { inner } => { + debug!("walk_pat: Deref {{ inner: {:?} }}", inner); + walk_deref_pat(this, inner, place); + return Ok(()); + } + }; + debug!("walk_pat: pat.kind={:?}", this.cx.store[pat]); let read_discriminant = { let place = place.clone(); @@ -921,6 +953,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { } } } + Pat::Deref { inner: subpattern } => walk_deref_pat(this, subpattern, place), Pat::Path(ref path) => { // A `Path` pattern is just a name like `Foo`. This is either a // named constant or else it refers to an ADT variant @@ -1110,6 +1143,13 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { } } +#[derive(Debug, Clone, Copy)] +enum CatPatternPat { + PatId(PatId), + DerefPat { inner: PatId }, +} +impl_from!(PatId for CatPatternPat); + /// The job of the methods whose name starts with `cat_` is to analyze /// expressions and construct the corresponding [`Place`]s. The `cat` /// stands for "categorize", this is a leftover from long ago when @@ -1460,7 +1500,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { op: &mut F, ) -> Result where - F: FnMut(&mut Self, PlaceWithOrigin, PatId) -> Result, + F: FnMut(&mut Self, PlaceWithOrigin, CatPatternPat) -> Result, { // If (pattern) adjustments are active for this pattern, adjust the `PlaceWithId` correspondingly. // `PlaceWithId`s are constructed differently from patterns. For example, in @@ -1494,11 +1534,26 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { // Then we see that to get the same result, we must start with // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. - let adjustments_len = self.cx.result.pat_adjustment(pat).map_or(0, |it| it.len()); - for _ in 0..adjustments_len { + let adjustments = self.pat_adjustments(pat); + let mut adjusts = adjustments.iter().peekable(); + while let Some(adjust) = adjusts.next() { debug!("applying adjustment to place_with_id={:?}", place_with_id); - // FIXME: We need to adjust this once we implement deref patterns (or pin ergonomics, for that matter). - place_with_id = self.cat_deref(pat.into(), place_with_id)?; + place_with_id = match adjust.kind { + PatAdjust::BuiltinDeref => self.cat_deref(pat.into(), place_with_id)?, + PatAdjust::OverloadedDeref => { + // This adjustment corresponds to an overloaded deref; unless it's on a box, it + // borrows the scrutinee to call `Deref::deref` or `DerefMut::deref_mut`. Invoke + // the callback before setting `place_with_id` to the temporary storing the + // result of the deref. + op(self, place_with_id.clone(), CatPatternPat::DerefPat { inner: pat })?; + let target_ty = match adjusts.peek() { + Some(next_adjust) => next_adjust.source.as_ref(), + // At the end of the deref chain, we get `pat`'s scrutinee. + None => self.pat_ty_unadjusted(pat)?, + }; + self.pat_deref_place(pat.into(), place_with_id, pat, target_ty)? + } + }; } let place_with_id = place_with_id; // lose mutability debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id); @@ -1512,7 +1567,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)` // result in the place `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with // that (where the `ref` on `x` is implied). - op(self, place_with_id.clone(), pat)?; + op(self, place_with_id.clone(), pat.into())?; match self.cx.store[pat] { Pat::Tuple { args: ref subpats, ellipsis: dots_pos } => { @@ -1592,6 +1647,11 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { let subplace = self.cat_deref(pat.into(), place_with_id)?; self.cat_pattern(subplace, subpat, op)?; } + Pat::Deref { inner: subpat } => { + let ty = self.pat_ty_adjusted(subpat)?; + let place = self.pat_deref_place(pat.into(), place_with_id, subpat, ty)?; + self.cat_pattern(place, subpat, op)?; + } Pat::Slice { prefix: ref before, slice, suffix: ref after } => { let Some(element_ty) = self @@ -1642,6 +1702,29 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { Ok(()) } + /// Represents the place matched on by a deref pattern's interior. + fn pat_deref_place( + &mut self, + node: ExprOrPatId, + base_place: PlaceWithOrigin, + inner: PatId, + target_ty: Ty<'db>, + ) -> Result<PlaceWithOrigin> { + match self.cx.deref_pat_borrow_mode(base_place.place.ty(), inner) { + // Deref patterns on boxes are lowered using a built-in deref. + DerefPatBorrowMode::Box => self.cat_deref(node, base_place), + // For other types, we create a temporary to match on. + DerefPatBorrowMode::Borrow(mutability) => { + let re_erased = self.cx.types.regions.erased; + let ty = Ty::new_ref(self.cx.interner(), re_erased, target_ty, mutability); + // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ... + let base = self.cat_rvalue(node, ty); + // ... and the inner pattern matches on the place behind that reference. + self.cat_deref(node, base) + } + } + } + /// Checks whether a type has multiple variants, and therefore, whether a /// read of the discriminant might be necessary. Note that the actual MIR /// builder code does a more specific check, filtering out variants that |