@@ -334,14 +334,19 @@ pub enum StatementKind<'tcx> {
334
334
/// See [`Rvalue`] documentation for details on each of those.
335
335
Assign ( Box < ( Place < ' tcx > , Rvalue < ' tcx > ) > ) ,
336
336
337
- /// This represents all the reading that a pattern match may do (e.g., inspecting constants and
338
- /// discriminant values), and the kind of pattern it comes from. This is in order to adapt
339
- /// potential error messages to these specific patterns.
337
+ /// When executed at runtime, this is a nop.
340
338
///
341
- /// Note that this also is emitted for regular `let` bindings to ensure that locals that are
342
- /// never accessed still get some sanity checks for, e.g., `let x: ! = ..;`
339
+ /// During static analysis, a fake read:
340
+ /// - requires that the value being read is initialized (or, in the case
341
+ /// of closures, that it was fully initialized at some point in the past)
342
+ /// - constitutes a use of a value for the purposes of NLL (i.e. if the
343
+ /// value being fake-read is a reference, the lifetime of that reference
344
+ /// will be extended to cover the `FakeRead`)
345
+ /// - but, unlike an actual read, does *not* invalidate any exclusive
346
+ /// borrows.
343
347
///
344
- /// When executed at runtime this is a nop.
348
+ /// See [`FakeReadCause`] for more details on the situations in which a
349
+ /// `FakeRead` is emitted.
345
350
///
346
351
/// Disallowed after drop elaboration.
347
352
FakeRead ( Box < ( FakeReadCause , Place < ' tcx > ) > ) ,
@@ -518,28 +523,59 @@ pub enum RetagKind {
518
523
/// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists.
519
524
#[ derive( Copy , Clone , TyEncodable , TyDecodable , Debug , Hash , HashStable , PartialEq ) ]
520
525
pub enum FakeReadCause {
521
- /// Inject a fake read of the borrowed input at the end of each guards
522
- /// code.
526
+ /// A fake read injected into a match guard to ensure that the discriminants
527
+ /// that are being matched on aren't modified while the match guard is being
528
+ /// evaluated.
529
+ ///
530
+ /// At the beginning of each match guard, a [fake borrow][FakeBorrowKind] is
531
+ /// inserted for each discriminant accessed in the entire `match` statement.
532
+ ///
533
+ /// Then, at the end of the match guard, a `FakeRead(ForMatchGuard)` is
534
+ /// inserted to keep the fake borrows alive until that point.
523
535
///
524
536
/// This should ensure that you cannot change the variant for an enum while
525
537
/// you are in the midst of matching on it.
526
538
ForMatchGuard ,
527
539
528
- /// `let x: !; match x {}` doesn't generate any read of x so we need to
529
- /// generate a read of x to check that it is initialized and safe.
540
+ /// Fake read of the scrutinee of a `match` or destructuring `let`
541
+ /// (i.e. `let` with non-trivial pattern).
542
+ ///
543
+ /// In `match x { ... }`, we generate a `FakeRead(ForMatchedPlace, x)`
544
+ /// and insert it into the `otherwise_block` (which is supposed to be
545
+ /// unreachable for irrefutable pattern-matches like `match` or `let`).
546
+ ///
547
+ /// This is necessary because `let x: !; match x {}` doesn't generate any
548
+ /// actual read of x, so we need to generate a `FakeRead` to check that it
549
+ /// is initialized.
530
550
///
531
- /// If a closure pattern matches a Place starting with an Upvar, then we introduce a
532
- /// FakeRead for that Place outside the closure, in such a case this option would be
533
- /// Some(closure_def_id).
534
- /// Otherwise, the value of the optional LocalDefId will be None.
551
+ /// If the `FakeRead(ForMatchedPlace)` is being performed with a closure
552
+ /// that doesn't capture the required upvars, the `FakeRead` within the
553
+ /// closure is omitted entirely.
554
+ ///
555
+ /// To make sure that this is still sound, if a closure matches against
556
+ /// a Place starting with an Upvar, we hoist the `FakeRead` to the
557
+ /// definition point of the closure.
558
+ ///
559
+ /// If the `FakeRead` comes from being hoisted out of a closure like this,
560
+ /// we record the `LocalDefId` of the closure. Otherwise, the `Option` will be `None`.
535
561
//
536
562
// We can use LocalDefId here since fake read statements are removed
537
563
// before codegen in the `CleanupNonCodegenStatements` pass.
538
564
ForMatchedPlace ( Option < LocalDefId > ) ,
539
565
540
- /// A fake read of the RefWithinGuard version of a bind-by-value variable
541
- /// in a match guard to ensure that its value hasn't change by the time
542
- /// we create the OutsideGuard version.
566
+ /// A fake read injected into a match guard to ensure that the places
567
+ /// bound by the pattern are immutable for the duration of the match guard.
568
+ ///
569
+ /// Within a match guard, references are created for each place that the
570
+ /// pattern creates a binding for — this is known as the `RefWithinGuard`
571
+ /// version of the variables. To make sure that the references stay
572
+ /// alive until the end of the match guard, and properly prevent the
573
+ /// places in question from being modified, a `FakeRead(ForGuardBinding)`
574
+ /// is inserted at the end of the match guard.
575
+ ///
576
+ /// For details on how these references are created, see the extensive
577
+ /// documentation on `bind_matched_candidate_for_guard` in
578
+ /// `rustc_mir_build`.
543
579
ForGuardBinding ,
544
580
545
581
/// Officially, the semantics of
@@ -552,22 +588,42 @@ pub enum FakeReadCause {
552
588
/// However, if we see the simple pattern `let var = <expr>`, we optimize this to
553
589
/// evaluate `<expr>` directly into the variable `var`. This is mostly unobservable,
554
590
/// but in some cases it can affect the borrow checker, as in #53695.
555
- /// Therefore, we insert a "fake read" here to ensure that we get
556
- /// appropriate errors.
557
591
///
558
- /// If a closure pattern matches a Place starting with an Upvar, then we introduce a
559
- /// FakeRead for that Place outside the closure, in such a case this option would be
560
- /// Some(closure_def_id).
561
- /// Otherwise, the value of the optional DefId will be None.
592
+ /// Therefore, we insert a `FakeRead(ForLet)` immediately after each `let`
593
+ /// with a trivial pattern.
594
+ ///
595
+ /// FIXME: `ExprUseVisitor` has an entirely different opinion on what `FakeRead(ForLet)`
596
+ /// is supposed to mean. If it was accurate to what MIR lowering does,
597
+ /// would it even make sense to hoist these out of closures like
598
+ /// `ForMatchedPlace`?
562
599
ForLet ( Option < LocalDefId > ) ,
563
600
564
- /// If we have an index expression like
601
+ /// Currently, index expressions overloaded through the `Index` trait
602
+ /// get lowered differently than index expressions with builtin semantics
603
+ /// for arrays and slices — the latter will emit code to perform
604
+ /// bound checks, and then return a MIR place that will only perform the
605
+ /// indexing "for real" when it gets incorporated into an instruction.
606
+ ///
607
+ /// This is observable in the fact that the following compiles:
608
+ ///
609
+ /// ```
610
+ /// fn f(x: &mut [&mut [u32]], i: usize) {
611
+ /// x[i][x[i].len() - 1] += 1;
612
+ /// }
613
+ /// ```
614
+ ///
615
+ /// However, we need to be careful to not let the user invalidate the
616
+ /// bound check with an expression like
617
+ ///
618
+ /// `(*x)[1][{ x = y; 4}]`
565
619
///
566
- /// (*x)[1][{ x = y; 4}]
620
+ /// Here, the first bounds check would be invalidated when we evaluate the
621
+ /// second index expression. To make sure that this doesn't happen, we
622
+ /// create a fake borrow of `x` and hold it while we evaluate the second
623
+ /// index.
567
624
///
568
- /// then the first bounds check is invalidated when we evaluate the second
569
- /// index expression. Thus we create a fake borrow of `x` across the second
570
- /// indexer, which will cause a borrow check error.
625
+ /// This borrow is kept alive by a `FakeRead(ForIndex)` at the end of its
626
+ /// scope.
571
627
ForIndex ,
572
628
}
573
629
0 commit comments