Skip to content

Commit adbde49

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 4a1c86e commit adbde49

File tree

14 files changed

+198
-128
lines changed

14 files changed

+198
-128
lines changed

compiler/rustc_hir_typeck/src/pat.rs

+62-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.
@@ -853,8 +875,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
853875
&& let Some(mt) = self.shallow_resolve(expected).builtin_deref(true)
854876
&& let ty::Dynamic(..) = mt.ty.kind()
855877
{
856-
// This is "x = SomeTrait" being reduced from
857-
// "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error.
878+
// This is "x = dyn SomeTrait" being reduced from
879+
// "let &x = &dyn SomeTrait" or "let box x = Box<dyn SomeTrait>", an error.
858880
let type_str = self.ty_to_string(expected);
859881
let mut err = struct_span_code_err!(
860882
self.dcx(),
@@ -2015,6 +2037,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20152037
mutbl: Mutability,
20162038
expected: Ty<'tcx>,
20172039
pat_info: PatInfo<'tcx, '_>,
2040+
already_consumed: bool,
20182041
) -> Ty<'tcx> {
20192042
let tcx = self.tcx;
20202043
let expected = self.shallow_resolve(expected);
@@ -2030,26 +2053,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20302053
match *expected.kind() {
20312054
ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty),
20322055
_ => {
2033-
let inner_ty = self.next_ty_var(TypeVariableOrigin {
2034-
kind: TypeVariableOriginKind::TypeInference,
2035-
span: inner.span,
2036-
});
2037-
let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty);
2038-
debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
2039-
let err = self.demand_eqtype_pat_diag(
2040-
pat.span,
2041-
expected,
2042-
ref_ty,
2043-
pat_info.top_info,
2044-
);
2056+
if already_consumed {
2057+
// We already matched against a match-ergonmics inserted reference,
2058+
// so we don't need to match against a reference from the original type.
2059+
// Save this infor for use in lowering later
2060+
self.inh
2061+
.typeck_results
2062+
.borrow_mut()
2063+
.ref_pats_that_dont_deref_mut()
2064+
.insert(pat.hir_id);
2065+
(expected, expected)
2066+
} else {
2067+
let inner_ty = self.next_ty_var(TypeVariableOrigin {
2068+
kind: TypeVariableOriginKind::TypeInference,
2069+
span: inner.span,
2070+
});
2071+
let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty);
2072+
debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
2073+
let err = self.demand_eqtype_pat_diag(
2074+
pat.span,
2075+
expected,
2076+
ref_ty,
2077+
pat_info.top_info,
2078+
);
20452079

2046-
// Look for a case like `fn foo(&foo: u32)` and suggest
2047-
// `fn foo(foo: &u32)`
2048-
if let Some(mut err) = err {
2049-
self.borrow_pat_suggestion(&mut err, pat);
2050-
err.emit();
2080+
// Look for a case like `fn foo(&foo: u32)` and suggest
2081+
// `fn foo(foo: &u32)`
2082+
if let Some(mut err) = err {
2083+
self.borrow_pat_suggestion(&mut err, pat);
2084+
err.emit();
2085+
}
2086+
(ref_ty, inner_ty)
20512087
}
2052-
(ref_ty, inner_ty)
20532088
}
20542089
}
20552090
}

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
/// For a given closure, returns the iterator of `ty::CapturedPlace`s that are captured
439452
/// by the closure.
440453
pub fn closure_min_captures_flattened(
@@ -604,6 +617,49 @@ impl<'a, V> LocalTableInContextMut<'a, V> {
604617
}
605618
}
606619

620+
#[derive(Clone, Copy, Debug)]
621+
pub struct LocalSetInContext<'a> {
622+
hir_owner: OwnerId,
623+
data: &'a ItemLocalSet,
624+
}
625+
626+
impl<'a> LocalSetInContext<'a> {
627+
pub fn is_empty(&self) -> bool {
628+
self.data.is_empty()
629+
}
630+
631+
pub fn contains(&self, id: hir::HirId) -> bool {
632+
validate_hir_id_for_typeck_results(self.hir_owner, id);
633+
self.data.contains(&id.local_id)
634+
}
635+
}
636+
637+
#[derive(Debug)]
638+
pub struct LocalSetInContextMut<'a> {
639+
hir_owner: OwnerId,
640+
data: &'a mut ItemLocalSet,
641+
}
642+
643+
impl<'a> LocalSetInContextMut<'a> {
644+
pub fn is_empty(&self) -> bool {
645+
self.data.is_empty()
646+
}
647+
648+
pub fn contains(&self, id: hir::HirId) -> bool {
649+
validate_hir_id_for_typeck_results(self.hir_owner, id);
650+
self.data.contains(&id.local_id)
651+
}
652+
pub fn insert(&mut self, id: hir::HirId) -> bool {
653+
validate_hir_id_for_typeck_results(self.hir_owner, id);
654+
self.data.insert(id.local_id)
655+
}
656+
657+
pub fn remove(&mut self, id: hir::HirId) -> bool {
658+
validate_hir_id_for_typeck_results(self.hir_owner, id);
659+
self.data.remove(&id.local_id)
660+
}
661+
}
662+
607663
rustc_index::newtype_index! {
608664
#[derive(HashStable)]
609665
#[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 {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
pub fn main() {
2+
if let Some(&x) = Some(0) {
3+
//~^ ERROR: mismatched types [E0308]
4+
let _: u32 = x;
5+
}
6+
if let &Some(x) = &mut Some(0) {
7+
//~^ ERROR: mismatched types [E0308]
8+
let _: u32 = x;
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/match-2024-references-fail.rs:2: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/match-2024-references-fail.rs:6: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`.
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//@ run-pass
2+
3+
pub fn main() {
4+
if let Some(Some(&x)) = &Some(&Some(0)) {
5+
let _: u32 = x;
6+
}
7+
if let Some(&Some(x)) = &Some(Some(0)) {
8+
let _: u32 = x;
9+
}
10+
if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) {
11+
let _: u32 = x;
12+
}
13+
}

tests/ui/mismatched_types/issue-106182.fixed

-14
This file was deleted.
+3-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
//@ run-rustfix
1+
//@ run-pass
22

33
struct _S(u32, Vec<i32>);
44

55
fn _foo(x: &_S) {
66
match x {
7-
_S(& (mut _y), _v) => {
8-
//~^ ERROR mismatched types [E0308]
9-
}
7+
_S(&(mut _y), _v) => {}
108
}
119
}
1210

13-
fn main() {
14-
}
11+
fn main() {}

tests/ui/mismatched_types/issue-106182.stderr

-18
This file was deleted.

0 commit comments

Comments
 (0)