Skip to content

Commit 5fc6649

Browse files
authored
Rollup merge of rust-lang#127556 - Zalathar:autoref, r=Nadrieril
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 makes it hard to trust that the information in the comment is accurate or relevant, even though much of it still seems to be true. 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.
2 parents 77b75f9 + 83e1efb commit 5fc6649

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
@@ -2162,92 +2162,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
21622162

21632163
self.ascribe_types(block, ascriptions);
21642164

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

2172+
// Bindings for guards require some extra handling to automatically
2173+
// insert implicit references/dereferences.
22512174
self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone());
22522175
let guard_frame = GuardFrame {
22532176
locals: bindings.clone().map(|b| GuardFrameLocal::new(b.var_id)).collect(),
@@ -2387,6 +2310,82 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
23872310
}
23882311
}
23892312

2313+
/// Binding for guards is a bit different from binding for the arm body,
2314+
/// because an extra layer of implicit reference/dereference is added.
2315+
///
2316+
/// The idea is that any pattern bindings of type T will map to a `&T` within
2317+
/// the context of the guard expression, but will continue to map to a `T`
2318+
/// in the context of the arm body. To avoid surfacing this distinction in
2319+
/// the user source code (which would be a severe change to the language and
2320+
/// require far more revision to the compiler), any occurrence of the
2321+
/// identifier in the guard expression will automatically get a deref op
2322+
/// applied to it. (See the caller of [`Self::is_bound_var_in_guard`].)
2323+
///
2324+
/// So an input like:
2325+
///
2326+
/// ```ignore (illustrative)
2327+
/// let place = Foo::new();
2328+
/// match place { foo if inspect(foo)
2329+
/// => feed(foo), ... }
2330+
/// ```
2331+
///
2332+
/// will be treated as if it were really something like:
2333+
///
2334+
/// ```ignore (illustrative)
2335+
/// let place = Foo::new();
2336+
/// match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) }
2337+
/// => { let tmp2 = place; feed(tmp2) }, ... }
2338+
/// ```
2339+
///
2340+
/// And an input like:
2341+
///
2342+
/// ```ignore (illustrative)
2343+
/// let place = Foo::new();
2344+
/// match place { ref mut foo if inspect(foo)
2345+
/// => feed(foo), ... }
2346+
/// ```
2347+
///
2348+
/// will be treated as if it were really something like:
2349+
///
2350+
/// ```ignore (illustrative)
2351+
/// let place = Foo::new();
2352+
/// match place { Foo { .. } if { let tmp1 = & &mut place; inspect(*tmp1) }
2353+
/// => { let tmp2 = &mut place; feed(tmp2) }, ... }
2354+
/// ```
2355+
/// ---
2356+
///
2357+
/// ## Implementation notes
2358+
///
2359+
/// To encode the distinction above, we must inject the
2360+
/// temporaries `tmp1` and `tmp2`.
2361+
///
2362+
/// There are two cases of interest: binding by-value, and binding by-ref.
2363+
///
2364+
/// 1. Binding by-value: Things are simple.
2365+
///
2366+
/// * Establishing `tmp1` creates a reference into the
2367+
/// matched place. This code is emitted by
2368+
/// [`Self::bind_matched_candidate_for_guard`].
2369+
///
2370+
/// * `tmp2` is only initialized "lazily", after we have
2371+
/// checked the guard. Thus, the code that can trigger
2372+
/// moves out of the candidate can only fire after the
2373+
/// guard evaluated to true. This initialization code is
2374+
/// emitted by [`Self::bind_matched_candidate_for_arm_body`].
2375+
///
2376+
/// 2. Binding by-reference: Things are tricky.
2377+
///
2378+
/// * Here, the guard expression wants a `&&` or `&&mut`
2379+
/// into the original input. This means we need to borrow
2380+
/// the reference that we create for the arm.
2381+
/// * So we eagerly create the reference for the arm and then take a
2382+
/// reference to that.
2383+
///
2384+
/// ---
2385+
///
2386+
/// See these PRs for some historical context:
2387+
/// - <https://github.com/rust-lang/rust/pull/49870> (introduction of autoref)
2388+
/// - <https://github.com/rust-lang/rust/pull/59114> (always use autoref)
23902389
fn bind_matched_candidate_for_guard<'b>(
23912390
&mut self,
23922391
block: BasicBlock,
@@ -2418,10 +2417,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
24182417
);
24192418
match binding.binding_mode.0 {
24202419
ByRef::No => {
2420+
// The arm binding will be by value, so for the guard binding
2421+
// just take a shared reference to the matched place.
24212422
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source);
24222423
self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
24232424
}
24242425
ByRef::Yes(mutbl) => {
2426+
// The arm binding will be by reference, so eagerly create it now.
24252427
let value_for_arm = self.storage_live_binding(
24262428
block,
24272429
binding.var_id,
@@ -2433,6 +2435,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
24332435
let rvalue =
24342436
Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source);
24352437
self.cfg.push_assign(block, source_info, value_for_arm, rvalue);
2438+
// For the guard binding, take a shared reference to that reference.
24362439
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm);
24372440
self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
24382441
}

0 commit comments

Comments
 (0)