Skip to content

Commit b29039e

Browse files
Ensure inherited reference is never set to &mut behind an &
1 parent 1922665 commit b29039e

File tree

6 files changed

+120
-63
lines changed

6 files changed

+120
-63
lines changed

compiler/rustc_ast/src/ast.rs

+8
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use rustc_macros::HashStable_Generic;
3636
use rustc_span::source_map::{respan, Spanned};
3737
use rustc_span::symbol::{kw, sym, Ident, Symbol};
3838
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
39+
use std::cmp;
3940
use std::fmt;
4041
use std::mem;
4142
use thin_vec::{thin_vec, ThinVec};
@@ -731,6 +732,13 @@ impl BindingAnnotation {
731732
Self::MUT_REF_MUT => "mut ref mut ",
732733
}
733734
}
735+
736+
pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self {
737+
if let ByRef::Yes(old_mutbl) = self.0 {
738+
self.0 = ByRef::Yes(cmp::min(old_mutbl, mutbl));
739+
}
740+
self
741+
}
734742
}
735743

736744
#[derive(Clone, Encodable, Decodable, Debug)]

compiler/rustc_hir_typeck/src/pat.rs

+52-39
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ struct TopInfo<'tcx> {
7979
#[derive(Copy, Clone)]
8080
struct PatInfo<'tcx, 'a> {
8181
binding_mode: BindingAnnotation,
82+
max_ref_mutbl: Mutability,
8283
top_info: TopInfo<'tcx>,
8384
decl_origin: Option<DeclOrigin<'a>>,
8485

@@ -160,8 +161,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
160161
decl_origin: Option<DeclOrigin<'tcx>>,
161162
) {
162163
let info = TopInfo { expected, origin_expr, span };
163-
let pat_info =
164-
PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_origin, current_depth: 0 };
164+
let pat_info = PatInfo {
165+
binding_mode: INITIAL_BM,
166+
max_ref_mutbl: Mutability::Mut,
167+
top_info: info,
168+
decl_origin,
169+
current_depth: 0,
170+
};
165171
self.check_pat(pat, expected, pat_info);
166172
}
167173

@@ -172,7 +178,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
172178
/// Conversely, inside this module, `check_pat_top` should never be used.
173179
#[instrument(level = "debug", skip(self, pat_info))]
174180
fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) {
175-
let PatInfo { binding_mode: def_bm, top_info: ti, current_depth, .. } = pat_info;
181+
let PatInfo { binding_mode: def_bm, max_ref_mutbl, top_info: ti, current_depth, .. } =
182+
pat_info;
176183

177184
let path_res = match &pat.kind {
178185
PatKind::Path(qpath) => Some(
@@ -181,10 +188,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
181188
_ => None,
182189
};
183190
let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
184-
let (expected, def_bm, ref_pattern_already_consumed) =
185-
self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode);
191+
let (expected, def_bm, max_ref_mutbl, ref_pattern_already_consumed) =
192+
self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode, max_ref_mutbl);
186193
let pat_info = PatInfo {
187194
binding_mode: def_bm,
195+
max_ref_mutbl,
188196
top_info: ti,
189197
decl_origin: pat_info.decl_origin,
190198
current_depth: current_depth + 1,
@@ -289,35 +297,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
289297
expected: Ty<'tcx>,
290298
def_bm: BindingAnnotation,
291299
adjust_mode: AdjustMode,
292-
) -> (Ty<'tcx>, BindingAnnotation, bool) {
300+
max_ref_mutbl: Mutability,
301+
) -> (Ty<'tcx>, BindingAnnotation, Mutability, bool) {
302+
if let ByRef::Yes(mutbl) = def_bm.0 {
303+
debug_assert!(mutbl <= max_ref_mutbl);
304+
}
293305
match adjust_mode {
294-
AdjustMode::Pass => (expected, def_bm, false),
295-
AdjustMode::Reset => (expected, INITIAL_BM, false),
296-
AdjustMode::ResetAndConsumeRef(mutbl) => {
297-
let mutbls_match = def_bm.0 == ByRef::Yes(mutbl);
306+
AdjustMode::Pass => (expected, def_bm, max_ref_mutbl, false),
307+
AdjustMode::Reset => (expected, INITIAL_BM, Mutability::Mut, false),
308+
AdjustMode::ResetAndConsumeRef(ref_pat_mutbl) => {
309+
let mutbls_match = def_bm.0 == ByRef::Yes(ref_pat_mutbl);
298310
if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
311+
let max_ref_mutbl = cmp::min(max_ref_mutbl, ref_pat_mutbl);
299312
if mutbls_match {
300-
(expected, INITIAL_BM, true)
313+
debug!("consuming inherited reference");
314+
(expected, INITIAL_BM, max_ref_mutbl, true)
301315
} else {
302-
let (new_ty, new_bm) = if mutbl == Mutability::Mut {
303-
self.peel_off_references(pat, expected, def_bm, Mutability::Not)
316+
let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut {
317+
self.peel_off_references(
318+
pat,
319+
expected,
320+
def_bm,
321+
Mutability::Not,
322+
max_ref_mutbl,
323+
)
304324
} else {
305-
let new_byref = if def_bm.0 == ByRef::Yes(Mutability::Mut) {
306-
ByRef::Yes(Mutability::Not)
307-
} else {
308-
def_bm.0
309-
};
310-
(expected, BindingAnnotation(new_byref, def_bm.1))
325+
(expected, def_bm.cap_ref_mutability(Mutability::Not), Mutability::Not)
311326
};
312-
(new_ty, new_bm, false)
327+
(new_ty, new_bm, max_ref_mutbl, false)
313328
}
314329
} else {
315-
(expected, INITIAL_BM, mutbls_match)
330+
(expected, INITIAL_BM, max_ref_mutbl, mutbls_match)
316331
}
317332
}
318333
AdjustMode::Peel => {
319-
let peeled = self.peel_off_references(pat, expected, def_bm, Mutability::Mut);
320-
(peeled.0, peeled.1, false)
334+
let peeled =
335+
self.peel_off_references(pat, expected, def_bm, Mutability::Mut, max_ref_mutbl);
336+
(peeled.0, peeled.1, peeled.2, false)
321337
}
322338
}
323339
}
@@ -398,8 +414,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
398414
pat: &'tcx Pat<'tcx>,
399415
expected: Ty<'tcx>,
400416
mut def_bm: BindingAnnotation,
401-
max_mutability: Mutability,
402-
) -> (Ty<'tcx>, BindingAnnotation) {
417+
max_peelable_mutability: Mutability,
418+
mut max_ref_mutability: Mutability,
419+
) -> (Ty<'tcx>, BindingAnnotation, Mutability) {
403420
let mut expected = self.try_structurally_resolve_type(pat.span, expected);
404421
// Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
405422
// for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
@@ -411,7 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
411428
// See the examples in `ui/match-defbm*.rs`.
412429
let mut pat_adjustments = vec![];
413430
while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind()
414-
&& inner_mutability <= max_mutability
431+
&& inner_mutability <= max_peelable_mutability
415432
{
416433
debug!("inspecting {:?}", expected);
417434

@@ -430,6 +447,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
430447
// This is because a `& &mut` cannot mutate the underlying value.
431448
ByRef::Yes(Mutability::Not) => Mutability::Not,
432449
});
450+
451+
if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
452+
max_ref_mutability = cmp::min(max_ref_mutability, inner_mutability);
453+
def_bm = def_bm.cap_ref_mutability(max_ref_mutability);
454+
}
433455
}
434456

435457
if !pat_adjustments.is_empty() {
@@ -440,7 +462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
440462
.insert(pat.hir_id, pat_adjustments);
441463
}
442464

443-
(expected, def_bm)
465+
(expected, def_bm, max_ref_mutability)
444466
}
445467

446468
fn check_pat_lit(
@@ -1116,15 +1138,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11161138
expected: Ty<'tcx>,
11171139
pat_info: PatInfo<'tcx, '_>,
11181140
) -> Ty<'tcx> {
1119-
let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth } = pat_info;
11201141
let tcx = self.tcx;
11211142
let on_error = |e| {
11221143
for pat in subpats {
1123-
self.check_pat(
1124-
pat,
1125-
Ty::new_error(tcx, e),
1126-
PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth },
1127-
);
1144+
self.check_pat(pat, Ty::new_error(tcx, e), pat_info);
11281145
}
11291146
};
11301147
let report_unexpected_res = |res: Res| {
@@ -1169,7 +1186,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11691186
let pat_ty = pat_ty.no_bound_vars().expect("expected fn type");
11701187

11711188
// Type-check the tuple struct pattern against the expected type.
1172-
let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, ti);
1189+
let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, pat_info.top_info);
11731190
let had_err = if let Some(err) = diag {
11741191
err.emit();
11751192
true
@@ -1187,11 +1204,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11871204
for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) {
11881205
let field = &variant.fields[FieldIdx::from_usize(i)];
11891206
let field_ty = self.field_ty(subpat.span, field, args);
1190-
self.check_pat(
1191-
subpat,
1192-
field_ty,
1193-
PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth },
1194-
);
1207+
self.check_pat(subpat, field_ty, pat_info);
11951208

11961209
self.tcx.check_stability(
11971210
variant.fields[FieldIdx::from_usize(i)].did,

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
6969
hir::PatKind::Ref(inner, _)
7070
if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) =>
7171
{
72-
self.lower_pattern_unadjusted(inner)
72+
self.lower_pattern(inner)
7373
}
7474
_ => self.lower_pattern_unadjusted(pat),
7575
};

tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs

+15
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,21 @@ pub fn main() {
3838
if let Some(Some(&mut x)) = &Some(Some(&mut 0)) {
3939
let _: &u32 = x;
4040
}
41+
if let &Some(Some(x)) = &Some(&mut Some(0)) {
42+
let _: &u32 = x;
43+
}
44+
if let Some(&Some(&x)) = &Some(&mut Some(0)) {
45+
let _: u32 = x;
46+
}
47+
if let Some(&Some(&x)) = &Some(&Some(0)) {
48+
let _: u32 = x;
49+
}
50+
if let Some(&Some(&x)) = &Some(&mut Some(0)) {
51+
let _: u32 = x;
52+
}
53+
if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) {
54+
let _: u32 = x;
55+
}
4156

4257
let &mut x = &&mut 0;
4358
let _: &u32 = x;

tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,27 @@ pub fn main() {
77
if let Some(&mut Some(&_)) = &Some(&Some(0)) {
88
//~^ ERROR: mismatched types
99
}
10-
if let Some(&Some(&_)) = &Some(&mut Some(0)) {
10+
if let Some(&Some(&mut _)) = &Some(&mut Some(0)) {
1111
//~^ ERROR: mismatched types
1212
}
1313
if let Some(&Some(x)) = &mut Some(&Some(0)) {
1414
let _: &mut u32 = x;
1515
//~^ ERROR: mismatched types
1616
}
17-
if let Some(&Some(&x)) = Some(&Some(&mut 0)) {
17+
if let Some(&Some(&_)) = Some(&Some(&mut 0)) {
1818
//~^ ERROR: mismatched types
1919
}
20+
if let Some(&Some(&mut _)) = &mut Some(&Some(0)) {
21+
//~^ ERROR: mismatched types
22+
}
23+
if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) {
24+
//~^ ERROR: mismatched types
25+
}
26+
2027

21-
let &mut x = &&0;
28+
let &mut _= &&0;
2229
//~^ ERROR: mismatched types
23-
let _: &u32 = x;
2430

25-
let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0;
31+
let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0;
2632
//~^ ERROR: mismatched types
27-
let _: &u32 = x;
2833
}

tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr

+33-17
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) {
1212
error[E0308]: mismatched types
1313
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:10:23
1414
|
15-
LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) {
16-
| ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>`
15+
LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) {
16+
| ^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>`
1717
| |
18-
| expected integer, found `&_`
18+
| expected integer, found `&mut _`
1919
|
20-
= note: expected type `{integer}`
21-
found reference `&_`
20+
= note: expected type `{integer}`
21+
found mutable reference `&mut _`
2222

2323
error[E0308]: mismatched types
2424
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:14:27
@@ -34,42 +34,58 @@ LL | let _: &mut u32 = x;
3434
error[E0308]: mismatched types
3535
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:17:23
3636
|
37-
LL | if let Some(&Some(&x)) = Some(&Some(&mut 0)) {
37+
LL | if let Some(&Some(&_)) = Some(&Some(&mut 0)) {
3838
| ^^ ------------------- this expression has type `Option<&Option<&mut {integer}>>`
3939
| |
4040
| types differ in mutability
4141
|
4242
= note: expected mutable reference `&mut {integer}`
4343
found reference `&_`
44-
help: consider removing `&` from the pattern
44+
45+
error[E0308]: mismatched types
46+
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:20:23
47+
|
48+
LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) {
49+
| ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>`
50+
| |
51+
| expected integer, found `&mut _`
52+
|
53+
= note: expected type `{integer}`
54+
found mutable reference `&mut _`
55+
56+
error[E0308]: mismatched types
57+
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:29
4558
|
46-
LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) {
47-
| ~
59+
LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) {
60+
| ^^^^^^ ------------------------- this expression has type `&Option<Option<&mut Option<{integer}>>>`
61+
| |
62+
| expected integer, found `&mut _`
63+
|
64+
= note: expected type `{integer}`
65+
found mutable reference `&mut _`
4866

4967
error[E0308]: mismatched types
50-
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:21:9
68+
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:28:9
5169
|
52-
LL | let &mut x = &&0;
53-
| ^^^^^^ --- this expression has type `&&{integer}`
70+
LL | let &mut _= &&0;
71+
| ^^^^^^ --- this expression has type `&&{integer}`
5472
| |
5573
| expected integer, found `&mut _`
56-
| help: to declare a mutable variable use: `mut x`
5774
|
5875
= note: expected type `{integer}`
5976
found mutable reference `&mut _`
6077

6178
error[E0308]: mismatched types
62-
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:25:9
79+
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:31:9
6380
|
64-
LL | let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0;
81+
LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0;
6582
| ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}`
6683
| |
6784
| expected integer, found `&mut _`
68-
| help: to declare a mutable variable use: `mut x`
6985
|
7086
= note: expected type `{integer}`
7187
found mutable reference `&mut _`
7288

73-
error: aborting due to 6 previous errors
89+
error: aborting due to 8 previous errors
7490

7591
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)