Skip to content

Commit e11f6d5

Browse files
committed
Auto merge of #44614 - tschottdorf:pat_adjustments, r=nikomatsakis
implement pattern-binding-modes RFC See the [RFC] and [tracking issue]. [tracking issue]: #42640 [RFC]: https://github.com/rust-lang/rfcs/blob/491e0af/text/2005-match-ergonomics.md
2 parents 05cbece + de55b4f commit e11f6d5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1406
-75
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# `match_default_bindings`
2+
3+
The tracking issue for this feature is: [#42640]
4+
5+
[#42640]: https://github.com/rust-lang/rust/issues/42640
6+
7+
------------------------
8+
9+
Match default bindings (also called "default binding modes in match") improves ergonomics for
10+
pattern-matching on references by introducing automatic dereferencing (and a corresponding shift
11+
in binding modes) for large classes of patterns that would otherwise not compile.
12+
13+
For example, under match default bindings,
14+
15+
```rust
16+
#![feature(match_default_bindings)]
17+
18+
fn main() {
19+
let x: &Option<_> = &Some(0);
20+
21+
match x {
22+
Some(y) => {
23+
println!("y={}", *y);
24+
},
25+
None => {},
26+
}
27+
}
28+
```
29+
30+
compiles and is equivalent to either of the below:
31+
32+
```rust
33+
fn main() {
34+
let x: &Option<_> = &Some(0);
35+
36+
match *x {
37+
Some(ref y) => {
38+
println!("y={}", *y);
39+
},
40+
None => {},
41+
}
42+
}
43+
```
44+
45+
or
46+
47+
```rust
48+
fn main() {
49+
let x: &Option<_> = &Some(0);
50+
51+
match x {
52+
&Some(ref y) => {
53+
println!("y={}", *y);
54+
},
55+
&None => {},
56+
}
57+
}
58+
```

src/librustc/hir/pat_util.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,13 @@ impl hir::Pat {
160160
variants
161161
}
162162

163-
/// Checks if the pattern contains any `ref` or `ref mut` bindings,
164-
/// and if yes whether it contains mutable or just immutables ones.
163+
/// Checks if the pattern contains any `ref` or `ref mut` bindings, and if
164+
/// yes whether it contains mutable or just immutables ones.
165165
///
166-
/// FIXME(tschottdorf): this is problematic as the HIR is being scraped,
167-
/// but ref bindings may be implicit after #42640.
166+
/// FIXME(tschottdorf): this is problematic as the HIR is being scraped, but
167+
/// ref bindings are be implicit after #42640 (default match binding modes).
168+
///
169+
/// See #44848.
168170
pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
169171
let mut result = None;
170172
self.each_binding(|annotation, _, _, _| {
@@ -188,7 +190,9 @@ impl hir::Arm {
188190
/// bindings, and if yes whether its containing mutable ones or just immutables ones.
189191
pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
190192
// FIXME(tschottdorf): contains_explicit_ref_binding() must be removed
191-
// for #42640.
193+
// for #42640 (default match binding modes).
194+
//
195+
// See #44848.
192196
self.pats.iter()
193197
.filter_map(|pat| pat.contains_explicit_ref_binding())
194198
.max_by_key(|m| match *m {

src/librustc/middle/mem_categorization.rs

+51-1
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
10941094
}
10951095

10961096
// FIXME(#19596) This is a workaround, but there should be a better way to do this
1097-
fn cat_pattern_<F>(&self, cmt: cmt<'tcx>, pat: &hir::Pat, op: &mut F) -> McResult<()>
1097+
fn cat_pattern_<F>(&self, mut cmt: cmt<'tcx>, pat: &hir::Pat, op: &mut F) -> McResult<()>
10981098
where F : FnMut(cmt<'tcx>, &hir::Pat)
10991099
{
11001100
// Here, `cmt` is the categorization for the value being
@@ -1144,6 +1144,56 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
11441144

11451145
debug!("cat_pattern: {:?} cmt={:?}", pat, cmt);
11461146

1147+
// If (pattern) adjustments are active for this pattern, adjust the `cmt` correspondingly.
1148+
// `cmt`s are constructed differently from patterns. For example, in
1149+
//
1150+
// ```
1151+
// match foo {
1152+
// &&Some(x, ) => { ... },
1153+
// _ => { ... },
1154+
// }
1155+
// ```
1156+
//
1157+
// the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the
1158+
// corresponding `cmt` we start with a `cmt` for `foo`, and then, by traversing the
1159+
// pattern, try to answer the question: given the address of `foo`, how is `x` reached?
1160+
//
1161+
// `&&Some(x,)` `cmt_foo`
1162+
// `&Some(x,)` `deref { cmt_foo}`
1163+
// `Some(x,)` `deref { deref { cmt_foo }}`
1164+
// (x,)` `field0 { deref { deref { cmt_foo }}}` <- resulting cmt
1165+
//
1166+
// The above example has no adjustments. If the code were instead the (after adjustments,
1167+
// equivalent) version
1168+
//
1169+
// ```
1170+
// match foo {
1171+
// Some(x, ) => { ... },
1172+
// _ => { ... },
1173+
// }
1174+
// ```
1175+
//
1176+
// Then we see that to get the same result, we must start with `deref { deref { cmt_foo }}`
1177+
// instead of `cmt_foo` since the pattern is now `Some(x,)` and not `&&Some(x,)`, even
1178+
// though its assigned type is that of `&&Some(x,)`.
1179+
for _ in 0..self.tables
1180+
.pat_adjustments()
1181+
.get(pat.hir_id)
1182+
.map(|v| v.len())
1183+
.unwrap_or(0) {
1184+
cmt = self.cat_deref(pat, cmt, true /* implicit */)?;
1185+
}
1186+
let cmt = cmt; // lose mutability
1187+
1188+
// Invoke the callback, but only now, after the `cmt` has adjusted.
1189+
//
1190+
// To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that
1191+
// case, the initial `cmt` will be that for `&Some(3)` and the pattern is `Some(x)`. We
1192+
// don't want to call `op` with these incompatible values. As written, what happens instead
1193+
// is that `op` is called with the adjusted cmt (that for `*&Some(3)`) and the pattern
1194+
// `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)`
1195+
// result in the cmt `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with
1196+
// that (where the `ref` on `x` is implied).
11471197
op(cmt.clone(), pat);
11481198

11491199
match pat.node {

src/librustc/ty/context.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,24 @@ pub struct TypeckTables<'tcx> {
337337

338338
adjustments: ItemLocalMap<Vec<ty::adjustment::Adjustment<'tcx>>>,
339339

340-
// Stores the actual binding mode for all instances of hir::BindingAnnotation.
340+
/// Stores the actual binding mode for all instances of hir::BindingAnnotation.
341341
pat_binding_modes: ItemLocalMap<BindingMode>,
342342

343+
/// Stores the types which were implicitly dereferenced in pattern binding modes
344+
/// for later usage in HAIR lowering. For example,
345+
///
346+
/// ```
347+
/// match &&Some(5i32) {
348+
/// Some(n) => {},
349+
/// _ => {},
350+
/// }
351+
/// ```
352+
/// leads to a `vec![&&Option<i32>, &Option<i32>]`. Empty vectors are not stored.
353+
///
354+
/// See:
355+
/// https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions
356+
pat_adjustments: ItemLocalMap<Vec<Ty<'tcx>>>,
357+
343358
/// Borrows
344359
pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>,
345360

@@ -394,6 +409,7 @@ impl<'tcx> TypeckTables<'tcx> {
394409
node_substs: ItemLocalMap(),
395410
adjustments: ItemLocalMap(),
396411
pat_binding_modes: ItemLocalMap(),
412+
pat_adjustments: ItemLocalMap(),
397413
upvar_capture_map: FxHashMap(),
398414
generator_sigs: ItemLocalMap(),
399415
generator_interiors: ItemLocalMap(),
@@ -574,6 +590,21 @@ impl<'tcx> TypeckTables<'tcx> {
574590
}
575591
}
576592

593+
pub fn pat_adjustments(&self) -> LocalTableInContext<Vec<Ty<'tcx>>> {
594+
LocalTableInContext {
595+
local_id_root: self.local_id_root,
596+
data: &self.pat_adjustments,
597+
}
598+
}
599+
600+
pub fn pat_adjustments_mut(&mut self)
601+
-> LocalTableInContextMut<Vec<Ty<'tcx>>> {
602+
LocalTableInContextMut {
603+
local_id_root: self.local_id_root,
604+
data: &mut self.pat_adjustments,
605+
}
606+
}
607+
577608
pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> {
578609
self.upvar_capture_map[&upvar_id]
579610
}
@@ -699,6 +730,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for TypeckTables<'gcx> {
699730
ref node_substs,
700731
ref adjustments,
701732
ref pat_binding_modes,
733+
ref pat_adjustments,
702734
ref upvar_capture_map,
703735
ref closure_tys,
704736
ref closure_kinds,
@@ -720,6 +752,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for TypeckTables<'gcx> {
720752
node_substs.hash_stable(hcx, hasher);
721753
adjustments.hash_stable(hcx, hasher);
722754
pat_binding_modes.hash_stable(hcx, hasher);
755+
pat_adjustments.hash_stable(hcx, hasher);
723756
hash_stable_hashmap(hcx, hasher, upvar_capture_map, |up_var_id, hcx| {
724757
let ty::UpvarId {
725758
var_id,

src/librustc_const_eval/pattern.rs

+38
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,44 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
301301
}
302302

303303
pub fn lower_pattern(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> {
304+
// When implicit dereferences have been inserted in this pattern, the unadjusted lowered
305+
// pattern has the type that results *after* dereferencing. For example, in this code:
306+
//
307+
// ```
308+
// match &&Some(0i32) {
309+
// Some(n) => { ... },
310+
// _ => { ... },
311+
// }
312+
// ```
313+
//
314+
// the type assigned to `Some(n)` in `unadjusted_pat` would be `Option<i32>` (this is
315+
// determined in rustc_typeck::check::match). The adjustments would be
316+
//
317+
// `vec![&&Option<i32>, &Option<i32>]`.
318+
//
319+
// Applying the adjustments, we want to instead output `&&Some(n)` (as a HAIR pattern). So
320+
// we wrap the unadjusted pattern in `PatternKind::Deref` repeatedly, consuming the
321+
// adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted
322+
// gets the least-dereferenced type).
323+
let unadjusted_pat = self.lower_pattern_unadjusted(pat);
324+
self.tables
325+
.pat_adjustments()
326+
.get(pat.hir_id)
327+
.unwrap_or(&vec![])
328+
.iter()
329+
.rev()
330+
.fold(unadjusted_pat, |pat, ref_ty| {
331+
debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty);
332+
Pattern {
333+
span: pat.span,
334+
ty: ref_ty,
335+
kind: Box::new(PatternKind::Deref { subpattern: pat }),
336+
}
337+
},
338+
)
339+
}
340+
341+
fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> {
304342
let mut ty = self.tables.node_id_to_type(pat.hir_id);
305343

306344
let kind = match pat.node {

0 commit comments

Comments
 (0)