@@ -334,14 +334,19 @@ pub enum StatementKind<'tcx> {
334334 /// See [`Rvalue`] documentation for details on each of those.
335335 Assign ( Box < ( Place < ' tcx > , Rvalue < ' tcx > ) > ) ,
336336
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.
340338 ///
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.
343347 ///
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.
345350 ///
346351 /// Disallowed after drop elaboration.
347352 FakeRead ( Box < ( FakeReadCause , Place < ' tcx > ) > ) ,
@@ -518,28 +523,59 @@ pub enum RetagKind {
518523/// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists.
519524#[ derive( Copy , Clone , TyEncodable , TyDecodable , Debug , Hash , HashStable , PartialEq ) ]
520525pub 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.
523535 ///
524536 /// This should ensure that you cannot change the variant for an enum while
525537 /// you are in the midst of matching on it.
526538 ForMatchGuard ,
527539
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.
530550 ///
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`.
535561 //
536562 // We can use LocalDefId here since fake read statements are removed
537563 // before codegen in the `CleanupNonCodegenStatements` pass.
538564 ForMatchedPlace ( Option < LocalDefId > ) ,
539565
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`.
543579 ForGuardBinding ,
544580
545581 /// Officially, the semantics of
@@ -552,22 +588,42 @@ pub enum FakeReadCause {
552588 /// However, if we see the simple pattern `let var = <expr>`, we optimize this to
553589 /// evaluate `<expr>` directly into the variable `var`. This is mostly unobservable,
554590 /// 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.
557591 ///
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`?
562599 ForLet ( Option < LocalDefId > ) ,
563600
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}]`
565619 ///
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.
567624 ///
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.
571627 ForIndex ,
572628}
573629
0 commit comments