Skip to content

Commit f37a4d5

Browse files
Implement "&<pat> everywhere"
The original proposal allows reference patterns with "compatible" mutability, however it's not clear what that means so for now we require an exact match. I don't know the type system code well, so if something seems to not make sense it's probably because I made a mistake
1 parent 877d36b commit f37a4d5

File tree

11 files changed

+257
-29
lines changed

11 files changed

+257
-29
lines changed

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,8 @@ declare_features! (
339339
(incomplete, adt_const_params, "1.56.0", Some(95174)),
340340
/// Allows defining an `#[alloc_error_handler]`.
341341
(unstable, alloc_error_handler, "1.29.0", Some(51540)),
342+
/// Allows `&` and `&mut` patterns to consume match-ergonomics-inserted references.
343+
(incomplete, and_pat_everywhere, "CURRENT_RUSTC_VERSION", Some(123076)),
342344
/// Allows trait methods with arbitrary self types.
343345
(unstable, arbitrary_self_types, "1.23.0", Some(44874)),
344346
/// Allows using `const` operands in inline assembly.

compiler/rustc_hir_typeck/src/pat.rs

+61-27
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ enum AdjustMode {
131131
Peel,
132132
/// Reset binding mode to the initial mode.
133133
Reset,
134+
/// Produced by ref patterns.
135+
/// Reset the binding mode to the initial mode,
136+
/// and if the old biding mode was by-reference
137+
/// with mutability matching the pattern,
138+
/// mark the pattern as having consumed this reference.
139+
RefReset(Mutability),
134140
/// Pass on the input binding mode and expected type.
135141
Pass,
136142
}
@@ -174,7 +180,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
174180
_ => None,
175181
};
176182
let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
177-
let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode);
183+
let (expected, def_bm, ref_pattern_already_consumed) =
184+
self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode);
178185
let pat_info = PatInfo {
179186
binding_mode: def_bm,
180187
top_info: ti,
@@ -211,7 +218,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
211218
}
212219
PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
213220
PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info),
214-
PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
221+
PatKind::Ref(inner, mutbl) => self.check_pat_ref(
222+
pat,
223+
inner,
224+
mutbl,
225+
expected,
226+
pat_info,
227+
ref_pattern_already_consumed,
228+
),
215229
PatKind::Slice(before, slice, after) => {
216230
self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
217231
}
@@ -264,17 +278,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
264278

265279
/// Compute the new expected type and default binding mode from the old ones
266280
/// as well as the pattern form we are currently checking.
281+
///
282+
/// Last entry is only relevant for ref patterns (`&` and `&mut`);
283+
/// if `true`, then the ref pattern consumed a match ergonomics inserted reference
284+
/// and so does no need to match against a reference in the scrutinee type.
267285
fn calc_default_binding_mode(
268286
&self,
269287
pat: &'tcx Pat<'tcx>,
270288
expected: Ty<'tcx>,
271289
def_bm: BindingAnnotation,
272290
adjust_mode: AdjustMode,
273-
) -> (Ty<'tcx>, BindingAnnotation) {
291+
) -> (Ty<'tcx>, BindingAnnotation, bool) {
274292
match adjust_mode {
275-
AdjustMode::Pass => (expected, def_bm),
276-
AdjustMode::Reset => (expected, INITIAL_BM),
277-
AdjustMode::Peel => self.peel_off_references(pat, expected, def_bm),
293+
AdjustMode::Pass => (expected, def_bm, false),
294+
AdjustMode::Reset => (expected, INITIAL_BM, false),
295+
AdjustMode::RefReset(mutbl) => (expected, INITIAL_BM, def_bm.0 == ByRef::Yes(mutbl)),
296+
AdjustMode::Peel => {
297+
let peeled = self.peel_off_references(pat, expected, def_bm);
298+
(peeled.0, peeled.1, false)
299+
}
278300
}
279301
}
280302

@@ -329,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
329351
// ```
330352
//
331353
// See issue #46688.
332-
PatKind::Ref(..) => AdjustMode::Reset,
354+
PatKind::Ref(_, mutbl) => AdjustMode::RefReset(*mutbl),
333355
// A `_` pattern works with any expected type, so there's no need to do anything.
334356
PatKind::Wild
335357
// A malformed pattern doesn't have an expected type, so let's just accept any type.
@@ -840,8 +862,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
840862
&& let Some(mt) = self.shallow_resolve(expected).builtin_deref(true)
841863
&& let ty::Dynamic(..) = mt.ty.kind()
842864
{
843-
// This is "x = SomeTrait" being reduced from
844-
// "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error.
865+
// This is "x = dyn SomeTrait" being reduced from
866+
// "let &x = &dyn SomeTrait" or "let box x = Box<dyn SomeTrait>", an error.
845867
let type_str = self.ty_to_string(expected);
846868
let mut err = struct_span_code_err!(
847869
self.dcx(),
@@ -2036,6 +2058,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20362058
mutbl: Mutability,
20372059
expected: Ty<'tcx>,
20382060
pat_info: PatInfo<'tcx, '_>,
2061+
already_consumed: bool,
20392062
) -> Ty<'tcx> {
20402063
let tcx = self.tcx;
20412064
let expected = self.shallow_resolve(expected);
@@ -2051,26 +2074,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20512074
match *expected.kind() {
20522075
ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty),
20532076
_ => {
2054-
let inner_ty = self.next_ty_var(TypeVariableOrigin {
2055-
kind: TypeVariableOriginKind::TypeInference,
2056-
span: inner.span,
2057-
});
2058-
let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty);
2059-
debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
2060-
let err = self.demand_eqtype_pat_diag(
2061-
pat.span,
2062-
expected,
2063-
ref_ty,
2064-
pat_info.top_info,
2065-
);
2077+
if already_consumed && self.tcx.features().and_pat_everywhere {
2078+
// We already matched against a match-ergonmics inserted reference,
2079+
// so we don't need to match against a reference from the original type.
2080+
// Save this infor for use in lowering later
2081+
self.typeck_results
2082+
.borrow_mut()
2083+
.ref_pats_that_dont_deref_mut()
2084+
.insert(pat.hir_id);
2085+
(expected, expected)
2086+
} else {
2087+
let inner_ty = self.next_ty_var(TypeVariableOrigin {
2088+
kind: TypeVariableOriginKind::TypeInference,
2089+
span: inner.span,
2090+
});
2091+
let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty);
2092+
debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
2093+
let err = self.demand_eqtype_pat_diag(
2094+
pat.span,
2095+
expected,
2096+
ref_ty,
2097+
pat_info.top_info,
2098+
);
20662099

2067-
// Look for a case like `fn foo(&foo: u32)` and suggest
2068-
// `fn foo(foo: &u32)`
2069-
if let Some(mut err) = err {
2070-
self.borrow_pat_suggestion(&mut err, pat);
2071-
err.emit();
2100+
// Look for a case like `fn foo(&foo: u32)` and suggest
2101+
// `fn foo(foo: &u32)`
2102+
if let Some(mut err) = err {
2103+
self.borrow_pat_suggestion(&mut err, pat);
2104+
err.emit();
2105+
}
2106+
(ref_ty, inner_ty)
20722107
}
2073-
(ref_ty, inner_ty)
20742108
}
20752109
}
20762110
}

compiler/rustc_hir_typeck/src/writeback.rs

+9
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
345345
_ => {}
346346
};
347347

348+
self.visit_ref_pats_that_dont_deref(p.hir_id);
348349
self.visit_pat_adjustments(p.span, p.hir_id);
349350

350351
self.visit_node_id(p.span, p.hir_id);
@@ -674,6 +675,14 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
674675
}
675676
}
676677

678+
#[instrument(skip(self), level = "debug")]
679+
fn visit_ref_pats_that_dont_deref(&mut self, hir_id: hir::HirId) {
680+
if self.fcx.typeck_results.borrow_mut().ref_pats_that_dont_deref_mut().remove(hir_id) {
681+
debug!("node is a ref pat that doesn't deref");
682+
self.typeck_results.ref_pats_that_dont_deref_mut().insert(hir_id);
683+
}
684+
}
685+
677686
fn visit_liberated_fn_sigs(&mut self) {
678687
let fcx_typeck_results = self.fcx.typeck_results.borrow();
679688
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);

compiler/rustc_middle/src/ty/typeck_results.rs

+56
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ pub struct TypeckResults<'tcx> {
9696
/// <https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions>
9797
pat_adjustments: ItemLocalMap<Vec<Ty<'tcx>>>,
9898

99+
/// Set of reference patterns that match against a match-ergonomics inserted reference
100+
/// (as opposed to against a reference in the scrutinee type).
101+
ref_pats_that_dont_deref: ItemLocalSet,
102+
99103
/// Records the reasons that we picked the kind of each closure;
100104
/// not all closures are present in the map.
101105
closure_kind_origins: ItemLocalMap<(Span, HirPlace<'tcx>)>,
@@ -228,6 +232,7 @@ impl<'tcx> TypeckResults<'tcx> {
228232
adjustments: Default::default(),
229233
pat_binding_modes: Default::default(),
230234
pat_adjustments: Default::default(),
235+
ref_pats_that_dont_deref: Default::default(),
231236
closure_kind_origins: Default::default(),
232237
liberated_fn_sigs: Default::default(),
233238
fru_field_types: Default::default(),
@@ -435,6 +440,14 @@ impl<'tcx> TypeckResults<'tcx> {
435440
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments }
436441
}
437442

443+
pub fn ref_pats_that_dont_deref(&self) -> LocalSetInContext<'_> {
444+
LocalSetInContext { hir_owner: self.hir_owner, data: &self.ref_pats_that_dont_deref }
445+
}
446+
447+
pub fn ref_pats_that_dont_deref_mut(&mut self) -> LocalSetInContextMut<'_> {
448+
LocalSetInContextMut { hir_owner: self.hir_owner, data: &mut self.ref_pats_that_dont_deref }
449+
}
450+
438451
/// Does the pattern recursively contain a `ref mut` binding in it?
439452
///
440453
/// This is used to determined whether a `deref` pattern should emit a `Deref`
@@ -629,6 +642,49 @@ impl<'a, V> LocalTableInContextMut<'a, V> {
629642
}
630643
}
631644

645+
#[derive(Clone, Copy, Debug)]
646+
pub struct LocalSetInContext<'a> {
647+
hir_owner: OwnerId,
648+
data: &'a ItemLocalSet,
649+
}
650+
651+
impl<'a> LocalSetInContext<'a> {
652+
pub fn is_empty(&self) -> bool {
653+
self.data.is_empty()
654+
}
655+
656+
pub fn contains(&self, id: hir::HirId) -> bool {
657+
validate_hir_id_for_typeck_results(self.hir_owner, id);
658+
self.data.contains(&id.local_id)
659+
}
660+
}
661+
662+
#[derive(Debug)]
663+
pub struct LocalSetInContextMut<'a> {
664+
hir_owner: OwnerId,
665+
data: &'a mut ItemLocalSet,
666+
}
667+
668+
impl<'a> LocalSetInContextMut<'a> {
669+
pub fn is_empty(&self) -> bool {
670+
self.data.is_empty()
671+
}
672+
673+
pub fn contains(&self, id: hir::HirId) -> bool {
674+
validate_hir_id_for_typeck_results(self.hir_owner, id);
675+
self.data.contains(&id.local_id)
676+
}
677+
pub fn insert(&mut self, id: hir::HirId) -> bool {
678+
validate_hir_id_for_typeck_results(self.hir_owner, id);
679+
self.data.insert(id.local_id)
680+
}
681+
682+
pub fn remove(&mut self, id: hir::HirId) -> bool {
683+
validate_hir_id_for_typeck_results(self.hir_owner, id);
684+
self.data.remove(&id.local_id)
685+
}
686+
}
687+
632688
rustc_index::newtype_index! {
633689
#[derive(HashStable)]
634690
#[encodable]

compiler/rustc_mir_build/src/thir/pattern/mod.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,16 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
6565
// we wrap the unadjusted pattern in `PatKind::Deref` repeatedly, consuming the
6666
// adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted
6767
// gets the least-dereferenced type).
68-
let unadjusted_pat = self.lower_pattern_unadjusted(pat);
68+
let unadjusted = if self.typeck_results.ref_pats_that_dont_deref().contains(pat.hir_id) {
69+
match pat.kind {
70+
hir::PatKind::Ref(inner, _) => self.lower_pattern_unadjusted(inner),
71+
_ => span_bug!(pat.span, "non ref pattern marked as non-deref ref pattern"),
72+
}
73+
} else {
74+
self.lower_pattern_unadjusted(pat)
75+
};
6976
self.typeck_results.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold(
70-
unadjusted_pat,
77+
unadjusted,
7178
|pat: Box<_>, ref_ty| {
7279
debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty);
7380
Box::new(Pat {

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ symbols! {
380380
alu32,
381381
always,
382382
and,
383+
and_pat_everywhere,
383384
and_then,
384385
anon,
385386
anon_adt,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![allow(incomplete_features)]
2+
#![feature(and_pat_everywhere)]
3+
pub fn main() {
4+
if let Some(&x) = Some(0) {
5+
//~^ ERROR: mismatched types [E0308]
6+
let _: u32 = x;
7+
}
8+
if let &Some(x) = &mut Some(0) {
9+
//~^ ERROR: mismatched types [E0308]
10+
let _: u32 = x;
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/and_pat_everywhere-mutability-mismatch.rs:4:17
3+
|
4+
LL | if let Some(&x) = Some(0) {
5+
| ^^ ------- this expression has type `Option<{integer}>`
6+
| |
7+
| expected integer, found `&_`
8+
|
9+
= note: expected type `{integer}`
10+
found reference `&_`
11+
help: consider removing `&` from the pattern
12+
|
13+
LL | if let Some(x) = Some(0) {
14+
| ~
15+
16+
error[E0308]: mismatched types
17+
--> $DIR/and_pat_everywhere-mutability-mismatch.rs:8:12
18+
|
19+
LL | if let &Some(x) = &mut Some(0) {
20+
| ^^^^^^^^ ------------ this expression has type `&mut Option<{integer}>`
21+
| |
22+
| types differ in mutability
23+
|
24+
= note: expected mutable reference `&mut Option<{integer}>`
25+
found reference `&_`
26+
27+
error: aborting due to 2 previous errors
28+
29+
For more information about this error, try `rustc --explain E0308`.

tests/ui/match/and_pat_everywhere.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ run-pass
2+
#![allow(incomplete_features)]
3+
#![feature(and_pat_everywhere)]
4+
5+
pub fn main() {
6+
if let Some(Some(&x)) = &Some(&Some(0)) {
7+
let _: u32 = x;
8+
}
9+
if let Some(&Some(x)) = &Some(Some(0)) {
10+
let _: u32 = x;
11+
}
12+
if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) {
13+
let _: u32 = x;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
pub fn main() {
2+
if let Some(Some(&x)) = &Some(&Some(0)) {
3+
//~^ ERROR: mismatched types [E0308]
4+
let _: u32 = x;
5+
}
6+
if let Some(&Some(x)) = &Some(Some(0)) {
7+
//~^ ERROR: mismatched types [E0308]
8+
let _: u32 = x;
9+
}
10+
if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) {
11+
//~^ ERROR: mismatched types [E0308]
12+
let _: u32 = x;
13+
}
14+
}

0 commit comments

Comments
 (0)