Skip to content

Commit 83e1efb

Browse files
committed
Replace a long inline "autoref" comment with method docs
This comment has two problems: - It is very long, making the flow of the enclosing method hard to follow. - It starts by talking about an `autoref` flag that hasn't existed since rust-lang#59114. This PR therefore replaces the long inline comment with a revised doc comment on `bind_matched_candidate_for_guard`, and some shorter inline comments. For readers who want more historical context, we also link to the PR that added the old comment, and the PR that removed the `autoref` flag.
1 parent 7caf672 commit 83e1efb

File tree

1 file changed

+84
-81
lines changed
  • compiler/rustc_mir_build/src/build/matches

1 file changed

+84
-81
lines changed

compiler/rustc_mir_build/src/build/matches/mod.rs

+84-81
Original file line numberDiff line numberDiff line change
@@ -2177,92 +2177,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
21772177

21782178
self.ascribe_types(block, ascriptions);
21792179

2180-
// rust-lang/rust#27282: The `autoref` business deserves some
2181-
// explanation here.
2182-
//
2183-
// The intent of the `autoref` flag is that when it is true,
2184-
// then any pattern bindings of type T will map to a `&T`
2185-
// within the context of the guard expression, but will
2186-
// continue to map to a `T` in the context of the arm body. To
2187-
// avoid surfacing this distinction in the user source code
2188-
// (which would be a severe change to the language and require
2189-
// far more revision to the compiler), when `autoref` is true,
2190-
// then any occurrence of the identifier in the guard
2191-
// expression will automatically get a deref op applied to it.
2192-
//
2193-
// So an input like:
2194-
//
2195-
// ```
2196-
// let place = Foo::new();
2197-
// match place { foo if inspect(foo)
2198-
// => feed(foo), ... }
2199-
// ```
2200-
//
2201-
// will be treated as if it were really something like:
2202-
//
2203-
// ```
2204-
// let place = Foo::new();
2205-
// match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) }
2206-
// => { let tmp2 = place; feed(tmp2) }, ... }
2207-
// ```
2208-
//
2209-
// And an input like:
2210-
//
2211-
// ```
2212-
// let place = Foo::new();
2213-
// match place { ref mut foo if inspect(foo)
2214-
// => feed(foo), ... }
2215-
// ```
2216-
//
2217-
// will be treated as if it were really something like:
2218-
//
2219-
// ```
2220-
// let place = Foo::new();
2221-
// match place { Foo { .. } if { let tmp1 = & &mut place; inspect(*tmp1) }
2222-
// => { let tmp2 = &mut place; feed(tmp2) }, ... }
2223-
// ```
2224-
//
2225-
// In short, any pattern binding will always look like *some*
2226-
// kind of `&T` within the guard at least in terms of how the
2227-
// MIR-borrowck views it, and this will ensure that guard
2228-
// expressions cannot mutate their the match inputs via such
2229-
// bindings. (It also ensures that guard expressions can at
2230-
// most *copy* values from such bindings; non-Copy things
2231-
// cannot be moved via pattern bindings in guard expressions.)
2232-
//
2233-
// ----
2234-
//
2235-
// Implementation notes (under assumption `autoref` is true).
2236-
//
2237-
// To encode the distinction above, we must inject the
2238-
// temporaries `tmp1` and `tmp2`.
2239-
//
2240-
// There are two cases of interest: binding by-value, and binding by-ref.
2241-
//
2242-
// 1. Binding by-value: Things are simple.
2243-
//
2244-
// * Establishing `tmp1` creates a reference into the
2245-
// matched place. This code is emitted by
2246-
// bind_matched_candidate_for_guard.
2247-
//
2248-
// * `tmp2` is only initialized "lazily", after we have
2249-
// checked the guard. Thus, the code that can trigger
2250-
// moves out of the candidate can only fire after the
2251-
// guard evaluated to true. This initialization code is
2252-
// emitted by bind_matched_candidate_for_arm.
2253-
//
2254-
// 2. Binding by-reference: Things are tricky.
2255-
//
2256-
// * Here, the guard expression wants a `&&` or `&&mut`
2257-
// into the original input. This means we need to borrow
2258-
// the reference that we create for the arm.
2259-
// * So we eagerly create the reference for the arm and then take a
2260-
// reference to that.
2180+
// Lower an instance of the arm guard (if present) for this candidate,
2181+
// and then perform bindings for the arm body.
22612182
if let Some((arm, match_scope)) = arm_match_scope
22622183
&& let Some(guard) = arm.guard
22632184
{
22642185
let tcx = self.tcx;
22652186

2187+
// Bindings for guards require some extra handling to automatically
2188+
// insert implicit references/dereferences.
22662189
self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone());
22672190
let guard_frame = GuardFrame {
22682191
locals: bindings.clone().map(|b| GuardFrameLocal::new(b.var_id)).collect(),
@@ -2402,6 +2325,82 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
24022325
}
24032326
}
24042327

2328+
/// Binding for guards is a bit different from binding for the arm body,
2329+
/// because an extra layer of implicit reference/dereference is added.
2330+
///
2331+
/// The idea is that any pattern bindings of type T will map to a `&T` within
2332+
/// the context of the guard expression, but will continue to map to a `T`
2333+
/// in the context of the arm body. To avoid surfacing this distinction in
2334+
/// the user source code (which would be a severe change to the language and
2335+
/// require far more revision to the compiler), any occurrence of the
2336+
/// identifier in the guard expression will automatically get a deref op
2337+
/// applied to it. (See the caller of [`Self::is_bound_var_in_guard`].)
2338+
///
2339+
/// So an input like:
2340+
///
2341+
/// ```ignore (illustrative)
2342+
/// let place = Foo::new();
2343+
/// match place { foo if inspect(foo)
2344+
/// => feed(foo), ... }
2345+
/// ```
2346+
///
2347+
/// will be treated as if it were really something like:
2348+
///
2349+
/// ```ignore (illustrative)
2350+
/// let place = Foo::new();
2351+
/// match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) }
2352+
/// => { let tmp2 = place; feed(tmp2) }, ... }
2353+
/// ```
2354+
///
2355+
/// And an input like:
2356+
///
2357+
/// ```ignore (illustrative)
2358+
/// let place = Foo::new();
2359+
/// match place { ref mut foo if inspect(foo)
2360+
/// => feed(foo), ... }
2361+
/// ```
2362+
///
2363+
/// will be treated as if it were really something like:
2364+
///
2365+
/// ```ignore (illustrative)
2366+
/// let place = Foo::new();
2367+
/// match place { Foo { .. } if { let tmp1 = & &mut place; inspect(*tmp1) }
2368+
/// => { let tmp2 = &mut place; feed(tmp2) }, ... }
2369+
/// ```
2370+
/// ---
2371+
///
2372+
/// ## Implementation notes
2373+
///
2374+
/// To encode the distinction above, we must inject the
2375+
/// temporaries `tmp1` and `tmp2`.
2376+
///
2377+
/// There are two cases of interest: binding by-value, and binding by-ref.
2378+
///
2379+
/// 1. Binding by-value: Things are simple.
2380+
///
2381+
/// * Establishing `tmp1` creates a reference into the
2382+
/// matched place. This code is emitted by
2383+
/// [`Self::bind_matched_candidate_for_guard`].
2384+
///
2385+
/// * `tmp2` is only initialized "lazily", after we have
2386+
/// checked the guard. Thus, the code that can trigger
2387+
/// moves out of the candidate can only fire after the
2388+
/// guard evaluated to true. This initialization code is
2389+
/// emitted by [`Self::bind_matched_candidate_for_arm_body`].
2390+
///
2391+
/// 2. Binding by-reference: Things are tricky.
2392+
///
2393+
/// * Here, the guard expression wants a `&&` or `&&mut`
2394+
/// into the original input. This means we need to borrow
2395+
/// the reference that we create for the arm.
2396+
/// * So we eagerly create the reference for the arm and then take a
2397+
/// reference to that.
2398+
///
2399+
/// ---
2400+
///
2401+
/// See these PRs for some historical context:
2402+
/// - <https://github.com/rust-lang/rust/pull/49870> (introduction of autoref)
2403+
/// - <https://github.com/rust-lang/rust/pull/59114> (always use autoref)
24052404
fn bind_matched_candidate_for_guard<'b>(
24062405
&mut self,
24072406
block: BasicBlock,
@@ -2433,10 +2432,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
24332432
);
24342433
match binding.binding_mode.0 {
24352434
ByRef::No => {
2435+
// The arm binding will be by value, so for the guard binding
2436+
// just take a shared reference to the matched place.
24362437
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source);
24372438
self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
24382439
}
24392440
ByRef::Yes(mutbl) => {
2441+
// The arm binding will be by reference, so eagerly create it now.
24402442
let value_for_arm = self.storage_live_binding(
24412443
block,
24422444
binding.var_id,
@@ -2448,6 +2450,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
24482450
let rvalue =
24492451
Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source);
24502452
self.cfg.push_assign(block, source_info, value_for_arm, rvalue);
2453+
// For the guard binding, take a shared reference to that reference.
24512454
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm);
24522455
self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
24532456
}

0 commit comments

Comments
 (0)