hir_ty/infer/closure/analysis/expr_use_visitor.rs
1//! A different sort of visitor for walking fn bodies. Unlike the
2//! normal visitor, which just walks the entire body in one shot, the
3//! `ExprUseVisitor` determines how expressions are being used.
4//!
5//! This is only used for upvar inference.
6
7use either::Either;
8use hir_def::{
9 AdtId, HasModule, VariantId,
10 attrs::AttrFlags,
11 hir::{
12 Array, AsmOperand, BindingId, Expr, ExprId, ExprOrPatId, ExprOrPatIdPacked, MatchArm, Pat,
13 PatId, RecordLitField, RecordSpread, Statement,
14 },
15 resolver::ValueNs,
16};
17use macros::{TypeFoldable, TypeVisitable};
18use rustc_type_ir::inherent::{IntoKind, Ty as _};
19use smallvec::{SmallVec, smallvec};
20use stdx::impl_from;
21use syntax::ast::{BinaryOp, UnaryOp};
22use tracing::{debug, instrument, trace};
23
24use crate::{
25 Adjust, Adjustment, AutoBorrow, Span,
26 infer::{
27 ByRef, CaptureSourceStack, DerefPatBorrowMode, InferenceContext, PatAdjust, PatAdjustment,
28 UpvarCapture, closure::analysis::BorrowKind,
29 },
30 method_resolution::CandidateId,
31 next_solver::{ErrorGuaranteed, StoredTy, Ty, TyKind},
32 upvars::UpvarsRef,
33 utils::EnumerateAndAdjustIterator,
34};
35
36type Result<T = (), E = ErrorGuaranteed> = std::result::Result<T, E>;
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
39pub enum ProjectionKind {
40 /// A dereference of a pointer, reference or `Box<T>` of the given type.
41 Deref,
42
43 /// `B.F` where `B` is the base expression and `F` is
44 /// the field. The field is identified by which variant
45 /// it appears in along with a field index. The variant
46 /// is used for enums.
47 Field { field_idx: u32, variant_idx: u32 },
48
49 /// Some index like `B[x]`, where `B` is the base
50 /// expression. We don't preserve the index `x` because
51 /// we won't need it.
52 Index,
53
54 /// A subslice covering a range of values like `B[x..y]`.
55 Subslice,
56
57 /// `unwrap_binder!(expr)`
58 UnwrapUnsafeBinder,
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
62pub enum PlaceBase {
63 /// A temporary variable.
64 Rvalue,
65 /// A named `static` item.
66 StaticItem,
67 /// A named local variable.
68 Local(BindingId),
69 /// An upvar referenced by closure env.
70 Upvar { closure: ExprId, var_id: BindingId },
71}
72
73#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
74pub struct Projection {
75 /// Type after the projection is applied.
76 pub ty: StoredTy,
77
78 /// Defines the kind of access made by the projection.
79 #[type_visitable(ignore)]
80 pub kind: ProjectionKind,
81}
82
83/// A `Place` represents how a value is located in memory. This does not
84/// always correspond to a syntactic place expression. For example, when
85/// processing a pattern, a `Place` can be used to refer to the sub-value
86/// currently being inspected.
87#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
88pub struct Place {
89 /// The type of the `PlaceBase`
90 pub base_ty: StoredTy,
91 /// The "outermost" place that holds this value.
92 #[type_visitable(ignore)]
93 pub base: PlaceBase,
94 /// How this place is derived from the base place.
95 pub projections: Vec<Projection>,
96}
97
98impl Place {
99 /// Returns an iterator of the types that have to be dereferenced to access
100 /// the `Place`.
101 ///
102 /// The types are in the reverse order that they are applied. So if
103 /// `x: &*const u32` and the `Place` is `**x`, then the types returned are
104 ///`*const u32` then `&*const u32`.
105 pub fn deref_tys<'db>(&self) -> impl Iterator<Item = Ty<'db>> {
106 self.projections.iter().enumerate().rev().filter_map(move |(index, proj)| {
107 if ProjectionKind::Deref == proj.kind {
108 Some(self.ty_before_projection(index))
109 } else {
110 None
111 }
112 })
113 }
114
115 /// Returns the type of this `Place` after all projections have been applied.
116 pub fn ty<'db>(&self) -> Ty<'db> {
117 self.projections.last().map_or(self.base_ty.as_ref(), |proj| proj.ty.as_ref())
118 }
119
120 /// Returns the type of this `Place` immediately before `projection_index`th projection
121 /// is applied.
122 pub fn ty_before_projection<'db>(&self, projection_index: usize) -> Ty<'db> {
123 assert!(projection_index < self.projections.len());
124 if projection_index == 0 {
125 self.base_ty.as_ref()
126 } else {
127 self.projections[projection_index - 1].ty.as_ref()
128 }
129 }
130}
131
132/// A `PlaceWithOrigin` represents how a value is located in memory. This does not
133/// always correspond to a syntactic place expression. For example, when
134/// processing a pattern, a `Place` can be used to refer to the sub-value
135/// currently being inspected.
136#[derive(Clone, Debug, PartialEq, Eq, Hash)]
137pub(crate) struct PlaceWithOrigin {
138 /// `ExprId`s or `PatId`s of the expressions or patterns producing this value.
139 pub origins: SmallVec<[CaptureSourceStack; 2]>,
140
141 /// Information about the `Place`.
142 pub place: Place,
143}
144
145impl PlaceWithOrigin {
146 fn new_no_projections<'db>(
147 origin: impl Into<ExprOrPatIdPacked>,
148 base_ty: Ty<'db>,
149 base: PlaceBase,
150 ) -> PlaceWithOrigin {
151 Self::new(
152 smallvec![CaptureSourceStack::from_single(origin.into())],
153 base_ty,
154 base,
155 Vec::new(),
156 )
157 }
158
159 fn new<'db>(
160 origins: SmallVec<[CaptureSourceStack; 2]>,
161 base_ty: Ty<'db>,
162 base: PlaceBase,
163 projections: Vec<Projection>,
164 ) -> PlaceWithOrigin {
165 debug_assert!(origins.iter().all(|origin| origin.len() == projections.len() + 1));
166 PlaceWithOrigin { origins, place: Place { base_ty: base_ty.store(), base, projections } }
167 }
168
169 fn push_projection(&mut self, projection: Projection, origin: ExprOrPatIdPacked) {
170 self.place.projections.push(projection);
171 for origin_stack in &mut self.origins {
172 origin_stack.push(origin);
173 }
174 }
175
176 pub(crate) fn span(&self) -> Span {
177 match self.origins.first() {
178 Some(origin) => origin.final_source().into(),
179 None => Span::Dummy,
180 }
181 }
182}
183
184/// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists.
185#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
186pub enum FakeReadCause {
187 /// A fake read injected into a match guard to ensure that the discriminants
188 /// that are being matched on aren't modified while the match guard is being
189 /// evaluated.
190 ///
191 /// At the beginning of each match guard, a fake borrow is
192 /// inserted for each discriminant accessed in the entire `match` statement.
193 ///
194 /// Then, at the end of the match guard, a `FakeRead(ForMatchGuard)` is
195 /// inserted to keep the fake borrows alive until that point.
196 ///
197 /// This should ensure that you cannot change the variant for an enum while
198 /// you are in the midst of matching on it.
199 ForMatchGuard,
200
201 /// Fake read of the scrutinee of a `match` or destructuring `let`
202 /// (i.e. `let` with non-trivial pattern).
203 ///
204 /// In `match x { ... }`, we generate a `FakeRead(ForMatchedPlace, x)`
205 /// and insert it into the `otherwise_block` (which is supposed to be
206 /// unreachable for irrefutable pattern-matches like `match` or `let`).
207 ///
208 /// This is necessary because `let x: !; match x {}` doesn't generate any
209 /// actual read of x, so we need to generate a `FakeRead` to check that it
210 /// is initialized.
211 ///
212 /// If the `FakeRead(ForMatchedPlace)` is being performed with a closure
213 /// that doesn't capture the required upvars, the `FakeRead` within the
214 /// closure is omitted entirely.
215 ///
216 /// To make sure that this is still sound, if a closure matches against
217 /// a Place starting with an Upvar, we hoist the `FakeRead` to the
218 /// definition point of the closure.
219 ///
220 /// If the `FakeRead` comes from being hoisted out of a closure like this,
221 /// we record the `ExprId` of the closure. Otherwise, the `Option` will be `None`.
222 //
223 // We can use LocalDefId here since fake read statements are removed
224 // before codegen in the `CleanupNonCodegenStatements` pass.
225 ForMatchedPlace(Option<ExprId>),
226
227 /// A fake read injected into a match guard to ensure that the places
228 /// bound by the pattern are immutable for the duration of the match guard.
229 ///
230 /// Within a match guard, references are created for each place that the
231 /// pattern creates a binding for — this is known as the `RefWithinGuard`
232 /// version of the variables. To make sure that the references stay
233 /// alive until the end of the match guard, and properly prevent the
234 /// places in question from being modified, a `FakeRead(ForGuardBinding)`
235 /// is inserted at the end of the match guard.
236 ///
237 /// For details on how these references are created, see the extensive
238 /// documentation on `bind_matched_candidate_for_guard` in
239 /// `rustc_mir_build`.
240 ForGuardBinding,
241
242 /// Officially, the semantics of
243 ///
244 /// `let pattern = <expr>;`
245 ///
246 /// is that `<expr>` is evaluated into a temporary and then this temporary is
247 /// into the pattern.
248 ///
249 /// However, if we see the simple pattern `let var = <expr>`, we optimize this to
250 /// evaluate `<expr>` directly into the variable `var`. This is mostly unobservable,
251 /// but in some cases it can affect the borrow checker, as in #53695.
252 ///
253 /// Therefore, we insert a `FakeRead(ForLet)` immediately after each `let`
254 /// with a trivial pattern.
255 ///
256 /// FIXME: `ExprUseVisitor` has an entirely different opinion on what `FakeRead(ForLet)`
257 /// is supposed to mean. If it was accurate to what MIR lowering does,
258 /// would it even make sense to hoist these out of closures like
259 /// `ForMatchedPlace`?
260 ForLet(Option<ExprId>),
261
262 /// Currently, index expressions overloaded through the `Index` trait
263 /// get lowered differently than index expressions with builtin semantics
264 /// for arrays and slices — the latter will emit code to perform
265 /// bound checks, and then return a MIR place that will only perform the
266 /// indexing "for real" when it gets incorporated into an instruction.
267 ///
268 /// This is observable in the fact that the following compiles:
269 ///
270 /// ```
271 /// fn f(x: &mut [&mut [u32]], i: usize) {
272 /// x[i][x[i].len() - 1] += 1;
273 /// }
274 /// ```
275 ///
276 /// However, we need to be careful to not let the user invalidate the
277 /// bound check with an expression like
278 ///
279 /// `(*x)[1][{ x = y; 4}]`
280 ///
281 /// Here, the first bounds check would be invalidated when we evaluate the
282 /// second index expression. To make sure that this doesn't happen, we
283 /// create a fake borrow of `x` and hold it while we evaluate the second
284 /// index.
285 ///
286 /// This borrow is kept alive by a `FakeRead(ForIndex)` at the end of its
287 /// scope.
288 ForIndex,
289}
290
291/// This trait defines the callbacks you can expect to receive when
292/// employing the ExprUseVisitor.
293pub(crate) trait Delegate<'db> {
294 /// The value found at `place` is moved, depending
295 /// on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`.
296 ///
297 /// If the value is `Copy`, [`copy`][Self::copy] is called instead, which
298 /// by default falls back to [`borrow`][Self::borrow].
299 ///
300 /// The parameter `diag_expr_id` indicates the HIR id that ought to be used for
301 /// diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic
302 /// id will be the id of the expression `expr` but the place itself will have
303 /// the id of the binding in the pattern `pat`.
304 fn consume(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'db>);
305
306 /// The value found at `place` is used, depending
307 /// on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`.
308 ///
309 /// Use of a `Copy` type in a ByUse context is considered a use
310 /// by `ImmBorrow` and `borrow` is called instead. This is because
311 /// a shared borrow is the "minimum access" that would be needed
312 /// to perform a copy.
313 ///
314 ///
315 /// The parameter `diag_expr_id` indicates the HIR id that ought to be used for
316 /// diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic
317 /// id will be the id of the expression `expr` but the place itself will have
318 /// the id of the binding in the pattern `pat`.
319 fn use_cloned(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'db>);
320
321 /// The value found at `place` is being borrowed with kind `bk`.
322 /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
323 fn borrow(
324 &mut self,
325 place_with_id: PlaceWithOrigin,
326 bk: BorrowKind,
327 ctx: &mut InferenceContext<'db>,
328 );
329
330 /// The value found at `place` is being copied.
331 /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
332 ///
333 /// If an implementation is not provided, use of a `Copy` type in a ByValue context is instead
334 /// considered a use by `ImmBorrow` and `borrow` is called instead. This is because a shared
335 /// borrow is the "minimum access" that would be needed to perform a copy.
336 fn copy(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'db>) {
337 // In most cases, copying data from `x` is equivalent to doing `*&x`, so by default
338 // we treat a copy of `x` as a borrow of `x`.
339 self.borrow(place_with_id, BorrowKind::Immutable, ctx)
340 }
341
342 /// The path at `assignee_place` is being assigned to.
343 /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
344 fn mutate(&mut self, assignee_place: PlaceWithOrigin, ctx: &mut InferenceContext<'db>);
345
346 /// The path at `binding_place` is a binding that is being initialized.
347 ///
348 /// This covers cases such as `let x = 42;`
349 fn bind(&mut self, binding_place: PlaceWithOrigin, ctx: &mut InferenceContext<'db>) {
350 // Bindings can normally be treated as a regular assignment, so by default we
351 // forward this to the mutate callback.
352 self.mutate(binding_place, ctx)
353 }
354
355 /// The `place` should be a fake read because of specified `cause`.
356 fn fake_read(
357 &mut self,
358 place_with_id: PlaceWithOrigin,
359 cause: FakeReadCause,
360 ctx: &mut InferenceContext<'db>,
361 );
362}
363
364impl<'db, D: Delegate<'db>> Delegate<'db> for &mut D {
365 fn consume(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'db>) {
366 (**self).consume(place_with_id, ctx)
367 }
368
369 fn use_cloned(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'db>) {
370 (**self).use_cloned(place_with_id, ctx)
371 }
372
373 fn borrow(
374 &mut self,
375 place_with_id: PlaceWithOrigin,
376 bk: BorrowKind,
377 ctx: &mut InferenceContext<'db>,
378 ) {
379 (**self).borrow(place_with_id, bk, ctx)
380 }
381
382 fn copy(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'db>) {
383 (**self).copy(place_with_id, ctx)
384 }
385
386 fn mutate(&mut self, assignee_place: PlaceWithOrigin, ctx: &mut InferenceContext<'db>) {
387 (**self).mutate(assignee_place, ctx)
388 }
389
390 fn bind(&mut self, binding_place: PlaceWithOrigin, ctx: &mut InferenceContext<'db>) {
391 (**self).bind(binding_place, ctx)
392 }
393
394 fn fake_read(
395 &mut self,
396 place_with_id: PlaceWithOrigin,
397 cause: FakeReadCause,
398 ctx: &mut InferenceContext<'db>,
399 ) {
400 (**self).fake_read(place_with_id, cause, ctx)
401 }
402}
403
404/// A visitor that reports how each expression is being used.
405///
406/// See [module-level docs][self] and [`Delegate`] for details.
407pub(crate) struct ExprUseVisitor<'a, 'db, D: Delegate<'db>> {
408 cx: &'a mut InferenceContext<'db>,
409 delegate: D,
410 closure_expr: ExprId,
411 upvars: UpvarsRef<'db>,
412}
413
414impl<'a, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'db, D> {
415 /// Creates the ExprUseVisitor, configuring it with the various options provided:
416 ///
417 /// - `delegate` -- who receives the callbacks
418 /// - `param_env` --- parameter environment for trait lookups (esp. pertaining to `Copy`)
419 /// - `typeck_results` --- typeck results for the code being analyzed
420 pub(crate) fn new(
421 cx: &'a mut InferenceContext<'db>,
422 closure_expr: ExprId,
423 upvars: UpvarsRef<'db>,
424 delegate: D,
425 ) -> Self {
426 ExprUseVisitor { delegate, closure_expr, upvars, cx }
427 }
428
429 pub(crate) fn consume_closure_body(&mut self, params: &[PatId], body: ExprId) -> Result {
430 for ¶m in params {
431 let param_ty = self.pat_ty_adjusted(param)?;
432 debug!("consume_body: param_ty = {:?}", param_ty);
433
434 let param_place = self.cat_rvalue(param.into(), param_ty);
435
436 self.fake_read_scrutinee(param_place.clone(), false);
437 self.walk_pat(param_place, param, false)?;
438 }
439
440 self.consume_expr(body)?;
441
442 Ok(())
443 }
444
445 #[instrument(skip(self), level = "debug")]
446 fn consume_or_copy(&mut self, place_with_id: PlaceWithOrigin) {
447 if self.cx.table.type_is_copy_modulo_regions(place_with_id.place.ty()) {
448 self.delegate.copy(place_with_id, self.cx);
449 } else {
450 self.delegate.consume(place_with_id, self.cx);
451 }
452 }
453
454 #[instrument(skip(self), level = "debug")]
455 pub(crate) fn consume_clone_or_copy(&mut self, place_with_id: PlaceWithOrigin) {
456 // `x.use` will do one of the following
457 // * if it implements `Copy`, it will be a copy
458 // * if it implements `UseCloned`, it will be a call to `clone`
459 // * otherwise, it is a move
460 //
461 // we do a conservative approximation of this, treating it as a move unless we know that it implements copy or `UseCloned`
462 if self.cx.table.type_is_copy_modulo_regions(place_with_id.place.ty()) {
463 self.delegate.copy(place_with_id, self.cx);
464 } else if self.cx.table.type_is_use_cloned_modulo_regions(place_with_id.place.ty()) {
465 self.delegate.use_cloned(place_with_id, self.cx);
466 } else {
467 self.delegate.consume(place_with_id, self.cx);
468 }
469 }
470
471 fn consume_exprs(&mut self, exprs: &[ExprId]) -> Result {
472 for &expr in exprs {
473 self.consume_expr(expr)?;
474 }
475 Ok(())
476 }
477
478 #[instrument(skip(self), level = "debug")]
479 pub(crate) fn consume_expr(&mut self, expr: ExprId) -> Result {
480 let place_with_id = self.cat_expr(expr)?;
481 self.consume_or_copy(place_with_id);
482 self.walk_expr(expr)?;
483 Ok(())
484 }
485
486 fn mutate_expr(&mut self, expr: ExprId) -> Result {
487 let place_with_id = self.cat_expr(expr)?;
488 self.delegate.mutate(place_with_id, self.cx);
489 self.walk_expr(expr)?;
490 Ok(())
491 }
492
493 #[instrument(skip(self), level = "debug")]
494 fn borrow_expr(&mut self, expr: ExprId, bk: BorrowKind) -> Result {
495 let place_with_id = self.cat_expr(expr)?;
496 self.delegate.borrow(place_with_id, bk, self.cx);
497 self.walk_expr(expr)?;
498 Ok(())
499 }
500
501 #[instrument(skip(self), level = "debug")]
502 pub(crate) fn walk_expr(&mut self, expr: ExprId) -> Result {
503 self.walk_adjustment(expr)?;
504
505 match self.cx.store[expr] {
506 Expr::Path(_) => {}
507
508 Expr::UnaryOp { op: UnaryOp::Deref, expr: base } => {
509 // *base
510 self.walk_expr(base)?;
511 }
512
513 Expr::Field { expr: base, .. } => {
514 // base.f
515 self.walk_expr(base)?;
516 }
517
518 Expr::Index { base: lhs, index: rhs } => {
519 // lhs[rhs]
520 self.walk_expr(lhs)?;
521 self.consume_expr(rhs)?;
522 }
523
524 Expr::Call { callee, ref args } => {
525 // callee(args)
526 self.consume_expr(callee)?;
527 self.consume_exprs(args)?;
528 }
529
530 Expr::MethodCall { receiver, ref args, .. } => {
531 // callee.m(args)
532 self.consume_expr(receiver)?;
533 self.consume_exprs(args)?;
534 }
535
536 Expr::RecordLit { ref fields, spread, .. } => {
537 self.walk_struct_expr(fields, spread)?;
538 }
539
540 Expr::Tuple { ref exprs } => {
541 self.consume_exprs(exprs)?;
542 }
543
544 Expr::If {
545 condition: cond_expr,
546 then_branch: then_expr,
547 else_branch: opt_else_expr,
548 } => {
549 self.consume_expr(cond_expr)?;
550 self.consume_expr(then_expr)?;
551 if let Some(else_expr) = opt_else_expr {
552 self.consume_expr(else_expr)?;
553 }
554 }
555
556 Expr::Let { pat, expr: init } => {
557 self.walk_local(init, pat, None, |this| {
558 this.borrow_expr(init, BorrowKind::Immutable)
559 })?;
560 }
561
562 Expr::Match { expr: discr, ref arms } => {
563 let discr_place = self.cat_expr(discr)?;
564 self.fake_read_scrutinee(discr_place.clone(), true);
565 self.walk_expr(discr)?;
566
567 for arm in arms {
568 self.walk_arm(discr_place.clone(), arm)?;
569 }
570 }
571
572 Expr::Array(Array::ElementList { elements: ref exprs }) => {
573 self.consume_exprs(exprs)?;
574 }
575
576 Expr::Ref { expr: base, mutability: m, .. } => {
577 // &base
578 // make sure that the thing we are pointing out stays valid
579 // for the lifetime `scope_r` of the resulting ptr:
580 let bk = BorrowKind::from_hir_mutbl(m);
581 self.borrow_expr(base, bk)?;
582 }
583
584 Expr::InlineAsm(ref asm) => {
585 for (_, op) in &asm.operands {
586 match *op {
587 AsmOperand::In { expr, .. } => {
588 self.consume_expr(expr)?;
589 }
590 AsmOperand::Out { expr: Some(expr), .. }
591 | AsmOperand::InOut { expr, .. } => {
592 self.mutate_expr(expr)?;
593 }
594 AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
595 self.consume_expr(in_expr)?;
596 if let Some(out_expr) = out_expr {
597 self.mutate_expr(out_expr)?;
598 }
599 }
600 AsmOperand::Out { expr: None, .. }
601 | AsmOperand::Const { .. }
602 | AsmOperand::Sym { .. } => {}
603 AsmOperand::Label(block) => {
604 self.walk_expr(block)?;
605 }
606 }
607 }
608 }
609
610 Expr::Continue { .. }
611 | Expr::Literal(..)
612 | Expr::Const(..)
613 | Expr::OffsetOf(..)
614 | Expr::Missing
615 | Expr::Underscore => {}
616
617 Expr::Loop { body: blk, .. } => {
618 self.walk_expr(blk)?;
619 }
620
621 Expr::UnaryOp { expr: lhs, .. } => {
622 self.consume_expr(lhs)?;
623 }
624
625 Expr::BinaryOp {
626 lhs,
627 rhs,
628 op: Some(BinaryOp::ArithOp(..) | BinaryOp::CmpOp(..) | BinaryOp::LogicOp(..)),
629 } => {
630 self.consume_expr(lhs)?;
631 self.consume_expr(rhs)?;
632 }
633
634 Expr::Block { ref statements, tail, .. }
635 | Expr::Unsafe { ref statements, tail, .. } => {
636 for stmt in statements {
637 self.walk_stmt(stmt)?;
638 }
639
640 if let Some(tail_expr) = tail {
641 self.consume_expr(tail_expr)?;
642 }
643 }
644
645 Expr::Break { expr: opt_expr, .. } | Expr::Return { expr: opt_expr } => {
646 if let Some(expr) = opt_expr {
647 self.consume_expr(expr)?;
648 }
649 }
650
651 Expr::Become { expr } | Expr::Await { expr } | Expr::Box { expr } => {
652 self.consume_expr(expr)?;
653 }
654
655 Expr::Assignment { target, value } => {
656 self.walk_expr(value)?;
657 let expr_place = self.cat_expr(value)?;
658 let update_guard =
659 self.cx.resolver.update_to_inner_scope(self.cx.db, self.cx.store_owner, expr);
660 self.walk_pat(expr_place, target, false)?;
661 self.cx.resolver.reset_to_guard(update_guard);
662 }
663
664 Expr::Cast { expr: base, .. } => {
665 self.consume_expr(base)?;
666 }
667
668 Expr::BinaryOp { lhs, rhs, op: None | Some(BinaryOp::Assignment { .. }) } => {
669 self.consume_expr(lhs)?;
670 self.consume_expr(rhs)?;
671 }
672
673 Expr::Array(Array::Repeat { initializer: base, .. }) => {
674 self.consume_expr(base)?;
675 }
676
677 Expr::Closure { .. } => {
678 self.walk_captures(expr);
679 }
680
681 Expr::Yield { expr: value } | Expr::Yeet { expr: value } => {
682 if let Some(value) = value {
683 self.consume_expr(value)?;
684 }
685 }
686
687 Expr::Range { lhs, rhs, .. } => {
688 if let Some(lhs) = lhs {
689 self.consume_expr(lhs)?;
690 }
691 if let Some(rhs) = rhs {
692 self.consume_expr(rhs)?;
693 }
694 }
695
696 Expr::IncludeBytes => {}
697 }
698 Ok(())
699 }
700
701 fn walk_stmt(&mut self, stmt: &Statement) -> Result {
702 match *stmt {
703 Statement::Let { pat, initializer: Some(expr), else_branch: els, .. } => {
704 self.walk_local(expr, pat, els, |_| Ok(()))?;
705 }
706
707 Statement::Let { .. } => {}
708
709 Statement::Item(_) => {
710 // We don't visit nested items in this visitor,
711 // only the fn body we were given.
712 }
713
714 Statement::Expr { expr, .. } => {
715 self.consume_expr(expr)?;
716 }
717 }
718 Ok(())
719 }
720
721 #[instrument(skip(self), level = "debug")]
722 fn fake_read_scrutinee(&mut self, discr_place: PlaceWithOrigin, refutable: bool) {
723 let closure_def_id = match discr_place.place.base {
724 PlaceBase::Upvar { closure, var_id: _ } => Some(closure),
725 _ => None,
726 };
727
728 let cause = if refutable {
729 FakeReadCause::ForMatchedPlace(closure_def_id)
730 } else {
731 FakeReadCause::ForLet(closure_def_id)
732 };
733
734 self.delegate.fake_read(discr_place, cause, self.cx);
735 }
736
737 fn walk_local<F>(&mut self, expr: ExprId, pat: PatId, els: Option<ExprId>, mut f: F) -> Result
738 where
739 F: FnMut(&mut Self) -> Result,
740 {
741 self.walk_expr(expr)?;
742 let expr_place = self.cat_expr(expr)?;
743 f(self)?;
744 self.fake_read_scrutinee(expr_place.clone(), els.is_some());
745 self.walk_pat(expr_place, pat, false)?;
746 if let Some(els) = els {
747 self.walk_expr(els)?;
748 }
749 Ok(())
750 }
751
752 fn walk_struct_expr(&mut self, fields: &[RecordLitField], spread: RecordSpread) -> Result {
753 // Consume the expressions supplying values for each field.
754 for field in fields {
755 self.consume_expr(field.expr)?;
756 }
757
758 let RecordSpread::Expr(with_expr) = spread else { return Ok(()) };
759
760 let with_place = self.cat_expr(with_expr)?;
761
762 // Select just those fields of the `with`
763 // expression that will actually be used
764 match self.cx.structurally_resolve_type(with_expr.into(), with_place.place.ty()).kind() {
765 TyKind::Adt(adt, args) if adt.is_struct() => {
766 let AdtId::StructId(adt) = adt.def_id() else { unreachable!() };
767 let adt_fields = VariantId::from(adt).fields(self.cx.db).fields();
768 let adt_field_types = self.cx.db.field_types(adt.into());
769 // Consume those fields of the with expression that are needed.
770 for (f_index, with_field) in adt_fields.iter() {
771 let is_mentioned = fields.iter().any(|f| f.name == with_field.name);
772 if !is_mentioned {
773 let field_place = self.cat_projection(
774 with_expr.into(),
775 with_place.clone(),
776 adt_field_types[f_index]
777 .ty()
778 .instantiate(self.cx.interner(), args)
779 .skip_norm_wip(),
780 ProjectionKind::Field {
781 field_idx: f_index.into_raw().into_u32(),
782 variant_idx: 0,
783 },
784 );
785 self.consume_or_copy(field_place);
786 }
787 }
788 }
789 _ => {}
790 }
791
792 // walk the with expression so that complex expressions
793 // are properly handled.
794 self.walk_expr(with_expr)?;
795
796 Ok(())
797 }
798
799 fn expr_adjustments(&self, expr: ExprId) -> SmallVec<[Adjustment; 5]> {
800 // Due to borrowck problems, we cannot borrow the adjustments, unfortunately.
801 self.cx.result.expr_adjustment(expr).unwrap_or_default().into()
802 }
803
804 fn pat_adjustments(&self, pat: PatId) -> SmallVec<[PatAdjustment; 5]> {
805 // Due to borrowck problems, we cannot borrow the adjustments, unfortunately.
806 self.cx.result.pat_adjustment(pat).unwrap_or_default().into()
807 }
808
809 /// Invoke the appropriate delegate calls for anything that gets
810 /// consumed or borrowed as part of the automatic adjustment
811 /// process.
812 fn walk_adjustment(&mut self, expr: ExprId) -> Result {
813 let adjustments = self.expr_adjustments(expr);
814 let mut place_with_id = self.cat_expr_unadjusted(expr)?;
815 for adjustment in &adjustments {
816 debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment);
817 match adjustment.kind {
818 Adjust::NeverToAny | Adjust::Pointer(_) => {
819 // Creating a closure/fn-pointer or unsizing consumes
820 // the input and stores it into the resulting rvalue.
821 self.consume_or_copy(place_with_id.clone());
822 }
823
824 Adjust::Deref(None) => {}
825
826 // Autoderefs for overloaded Deref calls in fact reference
827 // their receiver. That is, if we have `(*x)` where `x`
828 // is of type `Rc<T>`, then this in fact is equivalent to
829 // `x.deref()`. Since `deref()` is declared with `&self`,
830 // this is an autoref of `x`.
831 Adjust::Deref(Some(ref deref)) => {
832 let bk = BorrowKind::from_mutbl(deref.0);
833 self.delegate.borrow(place_with_id.clone(), bk, self.cx);
834 }
835
836 Adjust::Borrow(ref autoref) => {
837 self.walk_autoref(expr, place_with_id.clone(), autoref);
838 }
839 }
840 place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?;
841 }
842 Ok(())
843 }
844
845 /// Walks the autoref `autoref` applied to the autoderef'd
846 /// `expr`. `base_place` is `expr` represented as a place,
847 /// after all relevant autoderefs have occurred.
848 fn walk_autoref(&mut self, expr: ExprId, base_place: PlaceWithOrigin, autoref: &AutoBorrow) {
849 debug!("walk_autoref(expr={:?} base_place={:?} autoref={:?})", expr, base_place, autoref);
850
851 match *autoref {
852 AutoBorrow::Ref(m) => {
853 self.delegate.borrow(base_place, BorrowKind::from_mutbl(m.into()), self.cx);
854 }
855
856 AutoBorrow::RawPtr(m) => {
857 debug!("walk_autoref: expr={:?} base_place={:?}", expr, base_place);
858
859 self.delegate.borrow(base_place, BorrowKind::from_mutbl(m), self.cx);
860 }
861 }
862 }
863
864 fn walk_arm(&mut self, discr_place: PlaceWithOrigin, arm: &MatchArm) -> Result {
865 self.walk_pat(discr_place, arm.pat, arm.guard.is_some())?;
866
867 if let Some(e) = arm.guard {
868 self.consume_expr(e)?;
869 }
870
871 self.consume_expr(arm.expr)
872 }
873
874 /// The core driver for walking a pattern
875 ///
876 /// This should mirror how pattern-matching gets lowered to MIR, as
877 /// otherwise lowering will ICE when trying to resolve the upvars.
878 ///
879 /// However, it is okay to approximate it here by doing *more* accesses than
880 /// the actual MIR builder will, which is useful when some checks are too
881 /// cumbersome to perform here. For example, if after typeck it becomes
882 /// clear that only one variant of an enum is inhabited, and therefore a
883 /// read of the discriminant is not necessary, `walk_pat` will have
884 /// over-approximated the necessary upvar capture granularity.
885 ///
886 /// Do note that discrepancies like these do still create obscure corners
887 /// in the semantics of the language, and should be avoided if possible.
888 #[instrument(skip(self), level = "debug")]
889 fn walk_pat(&mut self, discr_place: PlaceWithOrigin, pat: PatId, has_guard: bool) -> Result {
890 self.cat_pattern(discr_place.clone(), pat, &mut |this, place, pat| {
891 let walk_deref_pat = |this: &mut Self, subpattern: PatId, place: PlaceWithOrigin| {
892 // A deref pattern is a bit special: the binding mode of its inner bindings
893 // determines whether to borrow *at the level of the deref pattern* rather than
894 // borrowing the bound place (since that inner place is inside the temporary that
895 // stores the result of calling `deref()`/`deref_mut()` so can't be captured).
896 // Deref patterns on boxes don't borrow, so we ignore them here.
897 // HACK: this could be a fake pattern corresponding to a deref inserted by match
898 // ergonomics, in which case `pat.hir_id` will be the id of the subpattern.
899 if let DerefPatBorrowMode::Borrow(mutability) =
900 this.cx.deref_pat_borrow_mode(place.place.ty(), subpattern)
901 {
902 let bk = BorrowKind::from_mutbl(mutability);
903 this.delegate.borrow(place, bk, this.cx);
904 }
905 };
906
907 let pat = match pat {
908 CatPatternPat::PatId(pat) => pat,
909 CatPatternPat::DerefPat { inner } => {
910 debug!("walk_pat: Deref {{ inner: {:?} }}", inner);
911 walk_deref_pat(this, inner, place);
912 return Ok(());
913 }
914 };
915
916 debug!("walk_pat: pat.kind={:?}", this.cx.store[pat]);
917 let read_discriminant = {
918 let place = place.clone();
919 |this: &mut Self| {
920 this.delegate.borrow(place, BorrowKind::Immutable, this.cx);
921 }
922 };
923
924 match this.cx.store[pat] {
925 Pat::Bind { id, .. } => {
926 debug!("walk_pat: binding place={:?} pat={:?}", place, pat);
927 let bm = this.cx.result.binding_modes[pat];
928 debug!("walk_pat: pat.hir_id={:?} bm={:?}", pat, bm);
929
930 // pat_ty: the type of the binding being produced.
931 let pat_ty = this.node_ty(pat.into())?;
932 debug!("walk_pat: pat_ty={:?}", pat_ty);
933
934 if let Ok(binding_place) = this.cat_local(pat.into(), pat_ty, id) {
935 this.delegate.bind(binding_place, this.cx);
936 }
937
938 // Subtle: MIR desugaring introduces immutable borrows for each pattern
939 // binding when lowering pattern guards to ensure that the guard does not
940 // modify the scrutinee.
941 if has_guard {
942 read_discriminant(this);
943 }
944
945 // It is also a borrow or copy/move of the value being matched.
946 // In a cases of pattern like `let pat = upvar`, don't use the span
947 // of the pattern, as this just looks confusing, instead use the span
948 // of the discriminant.
949 match this.cx.result.binding_mode(pat).ok_or(ErrorGuaranteed)?.0 {
950 ByRef::Yes(m) => {
951 let bk = BorrowKind::from_mutbl(m);
952 this.delegate.borrow(place, bk, this.cx);
953 }
954 ByRef::No => {
955 debug!("walk_pat binding consuming pat");
956 this.consume_or_copy(place);
957 }
958 }
959 }
960 Pat::Deref { inner: subpattern } => walk_deref_pat(this, subpattern, place),
961 Pat::Path(ref path) => {
962 // A `Path` pattern is just a name like `Foo`. This is either a
963 // named constant or else it refers to an ADT variant
964
965 let is_assoc_const = this
966 .cx
967 .result
968 .assoc_resolutions_for_pat(pat)
969 .is_some_and(|it| matches!(it.0, CandidateId::ConstId(_)));
970 let resolution = this.cx.resolver.resolve_path_in_value_ns_fully(
971 this.cx.db,
972 path,
973 this.cx.store.pat_path_hygiene(pat),
974 );
975 let is_normal_const = matches!(resolution, Some(ValueNs::ConstId(_)));
976 if is_assoc_const || is_normal_const {
977 // Named constants have to be equated with the value
978 // being matched, so that's a read of the value being matched.
979 //
980 // FIXME: Does the MIR code skip this read when matching on a ZST?
981 // If so, we can also skip it here.
982 read_discriminant(this);
983 } else if this.is_multivariant_adt(pat.into(), place.place.ty()) {
984 // Otherwise, this is a struct/enum variant, and so it's
985 // only a read if we need to read the discriminant.
986 read_discriminant(this);
987 }
988 }
989 Pat::Lit(_) | Pat::ConstBlock(_) | Pat::Range { .. } => {
990 // When matching against a literal or range, we need to
991 // borrow the place to compare it against the pattern.
992 //
993 // Note that we do this read even if the range matches all
994 // possible values, such as 0..=u8::MAX. This is because
995 // we don't want to depend on consteval here.
996 //
997 // FIXME: What if the type being matched only has one
998 // possible value?
999 read_discriminant(this);
1000 }
1001 Pat::Record { .. } | Pat::TupleStruct { .. } => {
1002 if this.is_multivariant_adt(pat.into(), place.place.ty()) {
1003 read_discriminant(this);
1004 }
1005 }
1006 Pat::Slice { prefix: ref lhs, slice: wild, suffix: ref rhs } => {
1007 // We don't need to test the length if the pattern is `[..]`
1008 if matches!((&**lhs, wild, &**rhs), (&[], Some(_), &[]))
1009 // Arrays have a statically known size, so
1010 // there is no need to read their length
1011 || place.place.ty().strip_references().is_array()
1012 {
1013 // No read necessary
1014 } else {
1015 read_discriminant(this);
1016 }
1017 }
1018 Pat::Expr(expr) => {
1019 this.mutate_expr(expr)?;
1020 // Destructuring assignment moves
1021 this.consume_or_copy(place);
1022 }
1023 Pat::Or(_)
1024 | Pat::Box { .. }
1025 | Pat::Ref { .. }
1026 | Pat::Tuple { .. }
1027 | Pat::Wild
1028 | Pat::Missing
1029 | Pat::NotNull
1030 | Pat::Rest => {
1031 // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses
1032 // are made later as these patterns contains subpatterns.
1033 // If the PatKind is Missing, Wild or Err, any relevant accesses are made when processing
1034 // the other patterns that are part of the match
1035 }
1036 }
1037
1038 Ok(())
1039 })
1040 }
1041
1042 /// Handle the case where the current body contains a closure.
1043 ///
1044 /// When the current body being handled is a closure, then we must make sure that
1045 /// - The parent closure only captures Places from the nested closure that are not local to it.
1046 ///
1047 /// In the following example the closures `c` only captures `p.x` even though `incr`
1048 /// is a capture of the nested closure
1049 ///
1050 /// ```
1051 /// struct P { x: i32 }
1052 /// let mut p = P { x: 4 };
1053 /// let c = || {
1054 /// let incr = 10;
1055 /// let nested = || p.x += incr;
1056 /// };
1057 /// ```
1058 ///
1059 /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing
1060 /// closure as the DefId.
1061 #[instrument(skip(self), level = "debug")]
1062 fn walk_captures(&mut self, closure_expr: ExprId) {
1063 fn upvar_is_local_variable(upvars: UpvarsRef<'_>, var_id: BindingId) -> bool {
1064 upvars.contains(var_id)
1065 }
1066
1067 // If we have a nested closure, we want to include the fake reads present in the nested
1068 // closure.
1069 // `remove()` then re-insert and not `get()` due to borrowck errors.
1070 if let Some(closure_data) = self.cx.result.closures_data.remove(&closure_expr) {
1071 for (fake_read, cause, origins) in closure_data.fake_reads.iter() {
1072 match fake_read.base {
1073 PlaceBase::Upvar { var_id, closure: _ } => {
1074 if upvar_is_local_variable(self.upvars, var_id) {
1075 // The nested closure might be fake reading the current (enclosing) closure's local variables.
1076 // The only places we want to fake read before creating the parent closure are the ones that
1077 // are not local to it/ defined by it.
1078 //
1079 // ```rust,ignore(cannot-test-this-because-pseudo-code)
1080 // let v1 = (0, 1);
1081 // let c = || { // fake reads: v1
1082 // let v2 = (0, 1);
1083 // let e = || { // fake reads: v1, v2
1084 // let (_, t1) = v1;
1085 // let (_, t2) = v2;
1086 // }
1087 // }
1088 // ```
1089 // This check is performed when visiting the body of the outermost closure (`c`) and ensures
1090 // that we don't add a fake read of v2 in c.
1091 continue;
1092 }
1093 }
1094 _ => {
1095 panic!(
1096 "Do not know how to get ExprId out of Rvalue and StaticItem {:?}",
1097 fake_read.base
1098 );
1099 }
1100 };
1101 self.delegate.fake_read(
1102 PlaceWithOrigin { place: fake_read.clone(), origins: origins.clone() },
1103 *cause,
1104 self.cx,
1105 );
1106 }
1107
1108 for (var_id, min_list) in closure_data.min_captures.iter() {
1109 if !self.upvars.contains(*var_id) {
1110 // The nested closure might be capturing the current (enclosing) closure's local variables.
1111 // We check if the root variable is ever mentioned within the enclosing closure, if not
1112 // then for the current body (if it's a closure) these aren't captures, we will ignore them.
1113 continue;
1114 }
1115 for captured_place in min_list {
1116 let place = &captured_place.place;
1117 let capture_info = &captured_place.info;
1118
1119 // Mark the place to be captured by the enclosing closure
1120 let place_base =
1121 PlaceBase::Upvar { var_id: *var_id, closure: self.closure_expr };
1122 let place_with_id = PlaceWithOrigin::new(
1123 capture_info.sources.clone(),
1124 place.base_ty.as_ref(),
1125 place_base,
1126 place.projections.clone(),
1127 );
1128
1129 match capture_info.capture_kind {
1130 UpvarCapture::ByValue => {
1131 self.consume_or_copy(place_with_id);
1132 }
1133 UpvarCapture::ByUse => {
1134 self.consume_clone_or_copy(place_with_id);
1135 }
1136 UpvarCapture::ByRef(upvar_borrow) => {
1137 self.delegate.borrow(place_with_id, upvar_borrow, self.cx);
1138 }
1139 }
1140 }
1141 }
1142
1143 self.cx.result.closures_data.insert(closure_expr, closure_data);
1144 }
1145 }
1146
1147 fn error_reported_in_ty(&self, ty: Ty<'db>) -> Result {
1148 if ty.is_ty_error() { Err(ErrorGuaranteed) } else { Ok(()) }
1149 }
1150}
1151
1152#[derive(Debug, Clone, Copy)]
1153enum CatPatternPat {
1154 PatId(PatId),
1155 DerefPat { inner: PatId },
1156}
1157impl_from!(PatId for CatPatternPat);
1158
1159/// The job of the methods whose name starts with `cat_` is to analyze
1160/// expressions and construct the corresponding [`Place`]s. The `cat`
1161/// stands for "categorize", this is a leftover from long ago when
1162/// places were called "categorizations".
1163///
1164/// Note that a [`Place`] differs somewhat from the expression itself. For
1165/// example, auto-derefs are explicit. Also, an index `a[b]` is decomposed into
1166/// two operations: a dereference to reach the array data and then an index to
1167/// jump forward to the relevant item.
1168impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, 'db, D> {
1169 fn expect_and_resolve_type(&mut self, ty: Option<Ty<'db>>) -> Result<Ty<'db>> {
1170 match ty {
1171 Some(ty) => {
1172 let ty = self.cx.infcx().resolve_vars_if_possible(ty);
1173 self.error_reported_in_ty(ty)?;
1174 Ok(ty)
1175 }
1176 None => Err(ErrorGuaranteed),
1177 }
1178 }
1179
1180 fn node_ty(&mut self, id: ExprOrPatId) -> Result<Ty<'db>> {
1181 self.expect_and_resolve_type(self.cx.result.type_of_expr_or_pat(id))
1182 }
1183
1184 fn expr_ty(&mut self, expr: ExprId) -> Result<Ty<'db>> {
1185 self.node_ty(expr.into())
1186 }
1187
1188 fn expr_ty_adjusted(&mut self, expr: ExprId) -> Result<Ty<'db>> {
1189 self.expect_and_resolve_type(self.cx.result.type_of_expr_with_adjust(expr))
1190 }
1191
1192 /// Returns the type of value that this pattern matches against.
1193 /// Some non-obvious cases:
1194 ///
1195 /// - a `ref x` binding matches against a value of type `T` and gives
1196 /// `x` the type `&T`; we return `T`.
1197 /// - a pattern with implicit derefs (thanks to default binding
1198 /// modes #42640) may look like `Some(x)` but in fact have
1199 /// implicit deref patterns attached (e.g., it is really
1200 /// `&Some(x)`). In that case, we return the "outermost" type
1201 /// (e.g., `&Option<T>`).
1202 fn pat_ty_adjusted(&mut self, pat: PatId) -> Result<Ty<'db>> {
1203 // Check for implicit `&` types wrapping the pattern; note
1204 // that these are never attached to binding patterns, so
1205 // actually this is somewhat "disjoint" from the code below
1206 // that aims to account for `ref x`.
1207 if let Some(vec) = self.cx.result.pat_adjustment(pat) {
1208 if let Some(first_adjust) = vec.first() {
1209 debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust);
1210 return Ok(first_adjust.source.as_ref());
1211 }
1212 } else if let Pat::Ref { pat: subpat, .. } = self.cx.store[pat]
1213 && self.cx.result.is_skipped_ref_pat(pat)
1214 {
1215 return self.pat_ty_adjusted(subpat);
1216 }
1217
1218 self.pat_ty_unadjusted(pat)
1219 }
1220
1221 /// Like [`Self::pat_ty_adjusted`], but ignores implicit `&` patterns.
1222 fn pat_ty_unadjusted(&mut self, pat: PatId) -> Result<Ty<'db>> {
1223 let base_ty = self.node_ty(pat.into())?;
1224 trace!(?base_ty);
1225
1226 // This code detects whether we are looking at a `ref x`,
1227 // and if so, figures out what the type *being borrowed* is.
1228 match self.cx.store[pat] {
1229 Pat::Bind { .. } => {
1230 let bm = self.cx.result.binding_mode(pat).ok_or(ErrorGuaranteed)?;
1231
1232 if let ByRef::Yes(_) = bm.0 {
1233 // a bind-by-ref means that the base_ty will be the type of the ident itself,
1234 // but what we want here is the type of the underlying value being borrowed.
1235 // So peel off one-level, turning the &T into T.
1236 match self
1237 .cx
1238 .structurally_resolve_type(pat.into(), base_ty)
1239 .builtin_deref(false)
1240 {
1241 Some(ty) => Ok(ty),
1242 None => {
1243 debug!("By-ref binding of non-derefable type: {base_ty:?}");
1244 Err(ErrorGuaranteed)
1245 }
1246 }
1247 } else {
1248 Ok(base_ty)
1249 }
1250 }
1251 _ => Ok(base_ty),
1252 }
1253 }
1254
1255 fn cat_expr(&mut self, expr: ExprId) -> Result<PlaceWithOrigin> {
1256 self.cat_expr_(expr, &self.expr_adjustments(expr))
1257 }
1258
1259 /// This recursion helper avoids going through *too many*
1260 /// adjustments, since *only* non-overloaded deref recurses.
1261 fn cat_expr_(&mut self, expr: ExprId, adjustments: &[Adjustment]) -> Result<PlaceWithOrigin> {
1262 match adjustments.split_last() {
1263 None => self.cat_expr_unadjusted(expr),
1264 Some((adjustment, previous)) => {
1265 self.cat_expr_adjusted_with(expr, |this| this.cat_expr_(expr, previous), adjustment)
1266 }
1267 }
1268 }
1269
1270 fn cat_expr_adjusted(
1271 &mut self,
1272 expr: ExprId,
1273 previous: PlaceWithOrigin,
1274 adjustment: &Adjustment,
1275 ) -> Result<PlaceWithOrigin> {
1276 self.cat_expr_adjusted_with(expr, |_this| Ok(previous), adjustment)
1277 }
1278
1279 fn cat_expr_adjusted_with<F>(
1280 &mut self,
1281 expr: ExprId,
1282 previous: F,
1283 adjustment: &Adjustment,
1284 ) -> Result<PlaceWithOrigin>
1285 where
1286 F: FnOnce(&mut Self) -> Result<PlaceWithOrigin>,
1287 {
1288 let target = self.cx.infcx().resolve_vars_if_possible(adjustment.target.as_ref());
1289 match adjustment.kind {
1290 Adjust::Deref(overloaded) => {
1291 // Equivalent to *expr or something similar.
1292 let base = if let Some(deref) = overloaded {
1293 let ref_ty = Ty::new_ref(
1294 self.cx.interner(),
1295 self.cx.types.regions.erased,
1296 target,
1297 deref.0,
1298 );
1299 self.cat_rvalue(expr.into(), ref_ty)
1300 } else {
1301 previous(self)?
1302 };
1303 self.cat_deref(expr.into(), base)
1304 }
1305
1306 Adjust::NeverToAny | Adjust::Pointer(_) | Adjust::Borrow(_) => {
1307 // Result is an rvalue.
1308 Ok(self.cat_rvalue(expr.into(), target))
1309 }
1310 }
1311 }
1312
1313 fn cat_expr_unadjusted(&mut self, expr: ExprId) -> Result<PlaceWithOrigin> {
1314 let expr_ty = self.expr_ty(expr)?;
1315 match self.cx.store[expr] {
1316 Expr::UnaryOp { expr: e_base, op: UnaryOp::Deref } => {
1317 if self.cx.result.method_resolutions.contains_key(&expr) {
1318 self.cat_overloaded_place(expr, e_base)
1319 } else {
1320 let base = self.cat_expr(e_base)?;
1321 self.cat_deref(expr.into(), base)
1322 }
1323 }
1324
1325 Expr::Field { expr: base, .. } => {
1326 let base = self.cat_expr(base)?;
1327 debug!(?base);
1328
1329 let field_idx = self
1330 .cx
1331 .result
1332 .field_resolutions
1333 .get(&expr)
1334 .map(|field| match *field {
1335 Either::Left(field) => field.local_id.into_raw().into_u32(),
1336 Either::Right(tuple_field) => tuple_field.index,
1337 })
1338 .ok_or(ErrorGuaranteed)?;
1339
1340 Ok(self.cat_projection(
1341 expr.into(),
1342 base,
1343 expr_ty,
1344 ProjectionKind::Field { field_idx, variant_idx: 0 },
1345 ))
1346 }
1347
1348 Expr::Index { base, index: _ } => {
1349 // rustc checks if this is an overloaded index, but the check is buggy and treats any indexing
1350 // as overloaded, see https://rust-lang.zulipchat.com/#narrow/channel/144729-t-types/topic/.E2.9C.94.20Is.20builtin.20indexing.20any.20special.20in.20typeck.3F/near/565881390.
1351 // So that's what we do here.
1352 self.cat_overloaded_place(expr, base)
1353 }
1354
1355 Expr::Path(ref path) => {
1356 let resolver_guard =
1357 self.cx.resolver.update_to_inner_scope(self.cx.db, self.cx.store_owner, expr);
1358 let resolution = self.cx.resolver.resolve_path_in_value_ns_fully(
1359 self.cx.db,
1360 path,
1361 self.cx.store.expr_path_hygiene(expr),
1362 );
1363 self.cx.resolver.reset_to_guard(resolver_guard);
1364 match (resolution, self.cx.result.assoc_resolutions_for_expr(expr)) {
1365 (_, Some((CandidateId::FunctionId(_) | CandidateId::ConstId(_), _)))
1366 | (
1367 Some(
1368 ValueNs::ConstId(_)
1369 | ValueNs::GenericParam(_)
1370 | ValueNs::FunctionId(_)
1371 | ValueNs::ImplSelf(_)
1372 | ValueNs::EnumVariantId(_)
1373 | ValueNs::StructId(_),
1374 ),
1375 None,
1376 ) => Ok(self.cat_rvalue(expr.into(), expr_ty)),
1377 (Some(ValueNs::StaticId(_)), None) => Ok(PlaceWithOrigin::new_no_projections(
1378 expr,
1379 expr_ty,
1380 PlaceBase::StaticItem,
1381 )),
1382 (Some(ValueNs::LocalBinding(var_id)), None) => {
1383 self.cat_local(expr.into(), expr_ty, var_id)
1384 }
1385 (None, None) => Err(ErrorGuaranteed),
1386 }
1387 }
1388
1389 _ => Ok(self.cat_rvalue(expr.into(), expr_ty)),
1390 }
1391 }
1392
1393 fn cat_local(
1394 &mut self,
1395 id: ExprOrPatIdPacked,
1396 expr_ty: Ty<'db>,
1397 var_id: BindingId,
1398 ) -> Result<PlaceWithOrigin> {
1399 if self.upvars.contains(var_id) {
1400 self.cat_upvar(id, var_id)
1401 } else {
1402 Ok(PlaceWithOrigin::new_no_projections(id, expr_ty, PlaceBase::Local(var_id)))
1403 }
1404 }
1405
1406 /// Categorize an upvar.
1407 ///
1408 /// Note: the actual upvar access contains invisible derefs of closure
1409 /// environment and upvar reference as appropriate. Only regionck cares
1410 /// about these dereferences, so we let it compute them as needed.
1411 fn cat_upvar(
1412 &mut self,
1413 hir_id: ExprOrPatIdPacked,
1414 var_id: BindingId,
1415 ) -> Result<PlaceWithOrigin> {
1416 let var_ty = self.expect_and_resolve_type(
1417 self.cx.result.type_of_binding.get(var_id).map(|it| it.as_ref()),
1418 )?;
1419
1420 Ok(PlaceWithOrigin::new_no_projections(
1421 hir_id,
1422 var_ty,
1423 PlaceBase::Upvar { closure: self.closure_expr, var_id },
1424 ))
1425 }
1426
1427 fn cat_rvalue(&self, hir_id: ExprOrPatIdPacked, expr_ty: Ty<'db>) -> PlaceWithOrigin {
1428 PlaceWithOrigin::new_no_projections(hir_id, expr_ty, PlaceBase::Rvalue)
1429 }
1430
1431 fn cat_projection(
1432 &self,
1433 node: ExprOrPatIdPacked,
1434 mut base_place: PlaceWithOrigin,
1435 ty: Ty<'db>,
1436 kind: ProjectionKind,
1437 ) -> PlaceWithOrigin {
1438 base_place.push_projection(Projection { kind, ty: ty.store() }, node);
1439 base_place
1440 }
1441
1442 fn cat_overloaded_place(&mut self, expr: ExprId, base: ExprId) -> Result<PlaceWithOrigin> {
1443 // Reconstruct the output assuming it's a reference with the
1444 // same region and mutability as the receiver. This holds for
1445 // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
1446 let place_ty = self.expr_ty(expr)?;
1447 let base_ty = self.expr_ty_adjusted(base)?;
1448
1449 let TyKind::Ref(region, _, mutbl) =
1450 self.cx.structurally_resolve_type(base.into(), base_ty).kind()
1451 else {
1452 return Err(ErrorGuaranteed);
1453 };
1454 let ref_ty = Ty::new_ref(self.cx.interner(), region, place_ty, mutbl);
1455
1456 let base = self.cat_rvalue(expr.into(), ref_ty);
1457 self.cat_deref(expr.into(), base)
1458 }
1459
1460 fn cat_deref(
1461 &mut self,
1462 node: ExprOrPatIdPacked,
1463 mut base_place: PlaceWithOrigin,
1464 ) -> Result<PlaceWithOrigin> {
1465 let base_curr_ty = base_place.place.ty();
1466 let Some(deref_ty) =
1467 self.cx.structurally_resolve_type(node, base_curr_ty).builtin_deref(true)
1468 else {
1469 debug!("explicit deref of non-derefable type: {:?}", base_curr_ty);
1470 return Err(ErrorGuaranteed);
1471 };
1472 base_place.push_projection(
1473 Projection { kind: ProjectionKind::Deref, ty: deref_ty.store() },
1474 node,
1475 );
1476 Ok(base_place)
1477 }
1478
1479 /// Returns the variant index for an ADT used within a Struct or TupleStruct pattern
1480 /// Here `pat_hir_id` is the ExprId of the pattern itself.
1481 fn variant_index_for_adt(&self, pat_id: PatId) -> Result<(u32, VariantId)> {
1482 let variant = self.cx.result.variant_resolution_for_pat(pat_id).ok_or(ErrorGuaranteed)?;
1483 let variant_idx = match variant {
1484 VariantId::EnumVariantId(variant) => variant.index(self.cx.db) as u32,
1485 VariantId::StructId(_) | VariantId::UnionId(_) => 0,
1486 };
1487 Ok((variant_idx, variant))
1488 }
1489
1490 /// Returns the total number of fields in a tuple used within a Tuple pattern.
1491 /// Here `pat_hir_id` is the ExprId of the pattern itself.
1492 fn total_fields_in_tuple(&mut self, pat_id: PatId) -> usize {
1493 let ty = self.cx.result.pat_ty(pat_id);
1494 match self.cx.structurally_resolve_type(pat_id.into(), ty).kind() {
1495 TyKind::Tuple(args) => args.len(),
1496 _ => panic!("tuple pattern not applied to a tuple"),
1497 }
1498 }
1499
1500 /// Here, `place` is the `PlaceWithId` being matched and pat is the pattern it
1501 /// is being matched against.
1502 ///
1503 /// In general, the way that this works is that we walk down the pattern,
1504 /// constructing a `PlaceWithId` that represents the path that will be taken
1505 /// to reach the value being matched.
1506 fn cat_pattern<F>(
1507 &mut self,
1508 mut place_with_id: PlaceWithOrigin,
1509 pat: PatId,
1510 op: &mut F,
1511 ) -> Result
1512 where
1513 F: FnMut(&mut Self, PlaceWithOrigin, CatPatternPat) -> Result,
1514 {
1515 // If (pattern) adjustments are active for this pattern, adjust the `PlaceWithId` correspondingly.
1516 // `PlaceWithId`s are constructed differently from patterns. For example, in
1517 //
1518 // ```
1519 // match foo {
1520 // &&Some(x, ) => { ... },
1521 // _ => { ... },
1522 // }
1523 // ```
1524 //
1525 // the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the
1526 // corresponding `PlaceWithId` we start with the `PlaceWithId` for `foo`, and then, by traversing the
1527 // pattern, try to answer the question: given the address of `foo`, how is `x` reached?
1528 //
1529 // `&&Some(x,)` `place_foo`
1530 // `&Some(x,)` `deref { place_foo}`
1531 // `Some(x,)` `deref { deref { place_foo }}`
1532 // `(x,)` `field0 { deref { deref { place_foo }}}` <- resulting place
1533 //
1534 // The above example has no adjustments. If the code were instead the (after adjustments,
1535 // equivalent) version
1536 //
1537 // ```
1538 // match foo {
1539 // Some(x, ) => { ... },
1540 // _ => { ... },
1541 // }
1542 // ```
1543 //
1544 // Then we see that to get the same result, we must start with
1545 // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)`
1546 // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`.
1547 let adjustments = self.pat_adjustments(pat);
1548 let mut adjusts = adjustments.iter().peekable();
1549 while let Some(adjust) = adjusts.next() {
1550 debug!("applying adjustment to place_with_id={:?}", place_with_id);
1551 place_with_id = match adjust.kind {
1552 PatAdjust::BuiltinDeref => self.cat_deref(pat.into(), place_with_id)?,
1553 PatAdjust::OverloadedDeref => {
1554 // This adjustment corresponds to an overloaded deref; unless it's on a box, it
1555 // borrows the scrutinee to call `Deref::deref` or `DerefMut::deref_mut`. Invoke
1556 // the callback before setting `place_with_id` to the temporary storing the
1557 // result of the deref.
1558 op(self, place_with_id.clone(), CatPatternPat::DerefPat { inner: pat })?;
1559 let target_ty = match adjusts.peek() {
1560 Some(next_adjust) => next_adjust.source.as_ref(),
1561 // At the end of the deref chain, we get `pat`'s scrutinee.
1562 None => self.pat_ty_unadjusted(pat)?,
1563 };
1564 self.pat_deref_place(pat.into(), place_with_id, pat, target_ty)?
1565 }
1566 };
1567 }
1568 let place_with_id = place_with_id; // lose mutability
1569 debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);
1570
1571 // Invoke the callback, but only now, after the `place_with_id` has adjusted.
1572 //
1573 // To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that
1574 // case, the initial `place_with_id` will be that for `&Some(3)` and the pattern is `Some(x)`. We
1575 // don't want to call `op` with these incompatible values. As written, what happens instead
1576 // is that `op` is called with the adjusted place (that for `*&Some(3)`) and the pattern
1577 // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)`
1578 // result in the place `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with
1579 // that (where the `ref` on `x` is implied).
1580 op(self, place_with_id.clone(), pat.into())?;
1581
1582 match self.cx.store[pat] {
1583 Pat::Tuple { args: ref subpats, ellipsis: dots_pos } => {
1584 // (p1, ..., pN)
1585 let total_fields = self.total_fields_in_tuple(pat);
1586
1587 for (i, &subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) {
1588 let subpat_ty = self.pat_ty_adjusted(subpat)?;
1589 let projection_kind =
1590 ProjectionKind::Field { field_idx: i as u32, variant_idx: 0 };
1591 let sub_place = self.cat_projection(
1592 pat.into(),
1593 place_with_id.clone(),
1594 subpat_ty,
1595 projection_kind,
1596 );
1597 self.cat_pattern(sub_place, subpat, op)?;
1598 }
1599 }
1600
1601 Pat::TupleStruct { args: ref subpats, ellipsis: dots_pos, .. } => {
1602 // S(p1, ..., pN)
1603 let (variant_index, variant) = self.variant_index_for_adt(pat)?;
1604 let total_fields = variant.fields(self.cx.db).len();
1605
1606 for (i, &subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) {
1607 let subpat_ty = self.pat_ty_adjusted(subpat)?;
1608 let projection_kind =
1609 ProjectionKind::Field { variant_idx: variant_index, field_idx: i as u32 };
1610 let sub_place = self.cat_projection(
1611 pat.into(),
1612 place_with_id.clone(),
1613 subpat_ty,
1614 projection_kind,
1615 );
1616 self.cat_pattern(sub_place, subpat, op)?;
1617 }
1618 }
1619
1620 Pat::Record { args: ref field_pats, .. } => {
1621 // S { f1: p1, ..., fN: pN }
1622
1623 let (variant_index, variant) = self.variant_index_for_adt(pat)?;
1624 let fields = variant.fields(self.cx.db);
1625
1626 for fp in field_pats {
1627 let field_ty = self.pat_ty_adjusted(fp.pat)?;
1628 let field_index = fields.field(&fp.name).ok_or(ErrorGuaranteed)?;
1629
1630 let field_place = self.cat_projection(
1631 pat.into(),
1632 place_with_id.clone(),
1633 field_ty,
1634 ProjectionKind::Field {
1635 variant_idx: variant_index,
1636 field_idx: field_index.into_raw().into_u32(),
1637 },
1638 );
1639 self.cat_pattern(field_place, fp.pat, op)?;
1640 }
1641 }
1642
1643 Pat::Or(ref pats) => {
1644 for &pat in pats {
1645 self.cat_pattern(place_with_id.clone(), pat, op)?;
1646 }
1647 }
1648
1649 Pat::Bind { subpat: Some(subpat), .. } => {
1650 self.cat_pattern(place_with_id, subpat, op)?;
1651 }
1652
1653 Pat::Box { inner: subpat } | Pat::Ref { pat: subpat, .. } => {
1654 // box p1, &p1, &mut p1. we can ignore the mutability of
1655 // PatKind::Ref since that information is already contained
1656 // in the type.
1657 let subplace = self.cat_deref(pat.into(), place_with_id)?;
1658 self.cat_pattern(subplace, subpat, op)?;
1659 }
1660 Pat::Deref { inner: subpat } => {
1661 let ty = self.pat_ty_adjusted(subpat)?;
1662 let place = self.pat_deref_place(pat.into(), place_with_id, subpat, ty)?;
1663 self.cat_pattern(place, subpat, op)?;
1664 }
1665
1666 Pat::Slice { prefix: ref before, slice, suffix: ref after } => {
1667 let Some(element_ty) = self
1668 .cx
1669 .structurally_resolve_type(pat.into(), place_with_id.place.ty())
1670 .builtin_index()
1671 else {
1672 debug!("explicit index of non-indexable type {:?}", place_with_id);
1673 return Err(ErrorGuaranteed);
1674 };
1675 let elt_place = self.cat_projection(
1676 pat.into(),
1677 place_with_id.clone(),
1678 element_ty,
1679 ProjectionKind::Index,
1680 );
1681 for &before_pat in before {
1682 self.cat_pattern(elt_place.clone(), before_pat, op)?;
1683 }
1684 if let Some(slice_pat) = slice {
1685 let slice_pat_ty = self.pat_ty_adjusted(slice_pat)?;
1686 let slice_place = self.cat_projection(
1687 pat.into(),
1688 place_with_id,
1689 slice_pat_ty,
1690 ProjectionKind::Subslice,
1691 );
1692 self.cat_pattern(slice_place, slice_pat, op)?;
1693 }
1694 for &after_pat in after {
1695 self.cat_pattern(elt_place.clone(), after_pat, op)?;
1696 }
1697 }
1698
1699 Pat::Bind { subpat: None, .. }
1700 | Pat::Expr(..)
1701 | Pat::Path(_)
1702 | Pat::Lit(..)
1703 | Pat::ConstBlock(..)
1704 | Pat::Range { .. }
1705 | Pat::Missing
1706 | Pat::Rest
1707 | Pat::NotNull
1708 | Pat::Wild => {
1709 // always ok
1710 }
1711 }
1712
1713 Ok(())
1714 }
1715
1716 /// Represents the place matched on by a deref pattern's interior.
1717 fn pat_deref_place(
1718 &mut self,
1719 node: ExprOrPatIdPacked,
1720 base_place: PlaceWithOrigin,
1721 inner: PatId,
1722 target_ty: Ty<'db>,
1723 ) -> Result<PlaceWithOrigin> {
1724 match self.cx.deref_pat_borrow_mode(base_place.place.ty(), inner) {
1725 // Deref patterns on boxes are lowered using a built-in deref.
1726 DerefPatBorrowMode::Box => self.cat_deref(node, base_place),
1727 // For other types, we create a temporary to match on.
1728 DerefPatBorrowMode::Borrow(mutability) => {
1729 let re_erased = self.cx.types.regions.erased;
1730 let ty = Ty::new_ref(self.cx.interner(), re_erased, target_ty, mutability);
1731 // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ...
1732 let base = self.cat_rvalue(node, ty);
1733 // ... and the inner pattern matches on the place behind that reference.
1734 self.cat_deref(node, base)
1735 }
1736 }
1737 }
1738
1739 /// Checks whether a type has multiple variants, and therefore, whether a
1740 /// read of the discriminant might be necessary. Note that the actual MIR
1741 /// builder code does a more specific check, filtering out variants that
1742 /// happen to be uninhabited.
1743 ///
1744 /// Here, it is not practical to perform such a check, because inhabitedness
1745 /// queries require typeck results, and typeck requires closure capture analysis.
1746 ///
1747 /// Moreover, the language is moving towards uninhabited variants still semantically
1748 /// causing a discriminant read, so we *shouldn't* perform any such check.
1749 ///
1750 /// FIXME(never_patterns): update this comment once the aforementioned MIR builder
1751 /// code is changed to be insensitive to inhhabitedness.
1752 #[instrument(skip(self), level = "debug")]
1753 fn is_multivariant_adt(&mut self, node: ExprOrPatIdPacked, ty: Ty<'db>) -> bool {
1754 if let TyKind::Adt(def, _) = self.cx.structurally_resolve_type(node, ty).kind() {
1755 // Note that if a non-exhaustive SingleVariant is defined in another crate, we need
1756 // to assume that more cases will be added to the variant in the future. This mean
1757 // that we should handle non-exhaustive SingleVariant the same way we would handle
1758 // a MultiVariant.
1759 match def.def_id() {
1760 AdtId::StructId(_) | AdtId::UnionId(_) => false,
1761 AdtId::EnumId(did) => {
1762 let has_foreign_non_exhaustive = || {
1763 AttrFlags::query(self.cx.db, did.into()).contains(AttrFlags::NON_EXHAUSTIVE)
1764 && did.krate(self.cx.db) != self.cx.krate()
1765 };
1766 did.enum_variants(self.cx.db).variants.len() > 1 || has_foreign_non_exhaustive()
1767 }
1768 }
1769 } else {
1770 false
1771 }
1772 }
1773}