1
Fork 0

Ensure inherited reference is never set to &mut behind an &

This commit is contained in:
Jules Bertholet 2024-04-06 17:07:09 -04:00
parent b6c409723b
commit e3945bd3a8
No known key found for this signature in database
GPG key ID: 32034DAFC38C1BFC
6 changed files with 124 additions and 67 deletions

View file

@ -36,6 +36,7 @@ use rustc_macros::HashStable_Generic;
use rustc_span::source_map::{respan, Spanned}; use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
use std::cmp;
use std::fmt; use std::fmt;
use std::mem; use std::mem;
use thin_vec::{thin_vec, ThinVec}; use thin_vec::{thin_vec, ThinVec};
@ -731,6 +732,13 @@ impl BindingAnnotation {
Self::MUT_REF_MUT => "mut ref mut ", Self::MUT_REF_MUT => "mut ref mut ",
} }
} }
pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self {
if let ByRef::Yes(old_mutbl) = self.0 {
self.0 = ByRef::Yes(cmp::min(old_mutbl, mutbl));
}
self
}
} }
#[derive(Clone, Encodable, Decodable, Debug)] #[derive(Clone, Encodable, Decodable, Debug)]

View file

@ -79,6 +79,7 @@ struct TopInfo<'tcx> {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct PatInfo<'tcx, 'a> { struct PatInfo<'tcx, 'a> {
binding_mode: BindingAnnotation, binding_mode: BindingAnnotation,
max_ref_mutbl: Mutability,
top_info: TopInfo<'tcx>, top_info: TopInfo<'tcx>,
decl_origin: Option<DeclOrigin<'a>>, decl_origin: Option<DeclOrigin<'a>>,
@ -160,8 +161,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
decl_origin: Option<DeclOrigin<'tcx>>, decl_origin: Option<DeclOrigin<'tcx>>,
) { ) {
let info = TopInfo { expected, origin_expr, span }; let info = TopInfo { expected, origin_expr, span };
let pat_info = let pat_info = PatInfo {
PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_origin, current_depth: 0 }; binding_mode: INITIAL_BM,
max_ref_mutbl: Mutability::Mut,
top_info: info,
decl_origin,
current_depth: 0,
};
self.check_pat(pat, expected, pat_info); self.check_pat(pat, expected, pat_info);
} }
@ -172,7 +178,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Conversely, inside this module, `check_pat_top` should never be used. /// Conversely, inside this module, `check_pat_top` should never be used.
#[instrument(level = "debug", skip(self, pat_info))] #[instrument(level = "debug", skip(self, pat_info))]
fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) { fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) {
let PatInfo { binding_mode: def_bm, top_info: ti, current_depth, .. } = pat_info; let PatInfo { binding_mode: def_bm, max_ref_mutbl, top_info: ti, current_depth, .. } =
pat_info;
let path_res = match &pat.kind { let path_res = match &pat.kind {
PatKind::Path(qpath) => Some( PatKind::Path(qpath) => Some(
@ -181,10 +188,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => None, _ => None,
}; };
let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
let (expected, def_bm, ref_pattern_already_consumed) = let (expected, def_bm, max_ref_mutbl, ref_pattern_already_consumed) =
self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode, max_ref_mutbl);
let pat_info = PatInfo { let pat_info = PatInfo {
binding_mode: def_bm, binding_mode: def_bm,
max_ref_mutbl,
top_info: ti, top_info: ti,
decl_origin: pat_info.decl_origin, decl_origin: pat_info.decl_origin,
current_depth: current_depth + 1, current_depth: current_depth + 1,
@ -289,35 +297,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Ty<'tcx>, expected: Ty<'tcx>,
def_bm: BindingAnnotation, def_bm: BindingAnnotation,
adjust_mode: AdjustMode, adjust_mode: AdjustMode,
) -> (Ty<'tcx>, BindingAnnotation, bool) { max_ref_mutbl: Mutability,
) -> (Ty<'tcx>, BindingAnnotation, Mutability, bool) {
if let ByRef::Yes(mutbl) = def_bm.0 {
debug_assert!(mutbl <= max_ref_mutbl);
}
match adjust_mode { match adjust_mode {
AdjustMode::Pass => (expected, def_bm, false), AdjustMode::Pass => (expected, def_bm, max_ref_mutbl, false),
AdjustMode::Reset => (expected, INITIAL_BM, false), AdjustMode::Reset => (expected, INITIAL_BM, Mutability::Mut, false),
AdjustMode::ResetAndConsumeRef(mutbl) => { AdjustMode::ResetAndConsumeRef(ref_pat_mutbl) => {
let mutbls_match = def_bm.0 == ByRef::Yes(mutbl); let mutbls_match = def_bm.0 == ByRef::Yes(ref_pat_mutbl);
if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
let max_ref_mutbl = cmp::min(max_ref_mutbl, ref_pat_mutbl);
if mutbls_match { if mutbls_match {
(expected, INITIAL_BM, true) debug!("consuming inherited reference");
(expected, INITIAL_BM, max_ref_mutbl, true)
} else { } else {
let (new_ty, new_bm) = if mutbl == Mutability::Mut { let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut {
self.peel_off_references(pat, expected, def_bm, Mutability::Not) self.peel_off_references(
pat,
expected,
def_bm,
Mutability::Not,
max_ref_mutbl,
)
} else { } else {
let new_byref = if def_bm.0 == ByRef::Yes(Mutability::Mut) { (expected, def_bm.cap_ref_mutability(Mutability::Not), Mutability::Not)
ByRef::Yes(Mutability::Not)
} else {
def_bm.0
};
(expected, BindingAnnotation(new_byref, def_bm.1))
}; };
(new_ty, new_bm, false) (new_ty, new_bm, max_ref_mutbl, false)
} }
} else { } else {
(expected, INITIAL_BM, mutbls_match) (expected, INITIAL_BM, max_ref_mutbl, mutbls_match)
} }
} }
AdjustMode::Peel => { AdjustMode::Peel => {
let peeled = self.peel_off_references(pat, expected, def_bm, Mutability::Mut); let peeled =
(peeled.0, peeled.1, false) self.peel_off_references(pat, expected, def_bm, Mutability::Mut, max_ref_mutbl);
(peeled.0, peeled.1, peeled.2, false)
} }
} }
} }
@ -398,8 +414,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pat: &'tcx Pat<'tcx>, pat: &'tcx Pat<'tcx>,
expected: Ty<'tcx>, expected: Ty<'tcx>,
mut def_bm: BindingAnnotation, mut def_bm: BindingAnnotation,
max_mutability: Mutability, max_peelable_mutability: Mutability,
) -> (Ty<'tcx>, BindingAnnotation) { mut max_ref_mutability: Mutability,
) -> (Ty<'tcx>, BindingAnnotation, Mutability) {
let mut expected = self.try_structurally_resolve_type(pat.span, expected); let mut expected = self.try_structurally_resolve_type(pat.span, expected);
// Peel off as many `&` or `&mut` from the scrutinee type as possible. For example, // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
// for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
@ -411,7 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// See the examples in `ui/match-defbm*.rs`. // See the examples in `ui/match-defbm*.rs`.
let mut pat_adjustments = vec![]; let mut pat_adjustments = vec![];
while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind()
&& inner_mutability <= max_mutability && inner_mutability <= max_peelable_mutability
{ {
debug!("inspecting {:?}", expected); debug!("inspecting {:?}", expected);
@ -430,6 +447,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// This is because a `& &mut` cannot mutate the underlying value. // This is because a `& &mut` cannot mutate the underlying value.
ByRef::Yes(Mutability::Not) => Mutability::Not, ByRef::Yes(Mutability::Not) => Mutability::Not,
}); });
if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
max_ref_mutability = cmp::min(max_ref_mutability, inner_mutability);
def_bm = def_bm.cap_ref_mutability(max_ref_mutability);
}
} }
if !pat_adjustments.is_empty() { if !pat_adjustments.is_empty() {
@ -440,7 +462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.insert(pat.hir_id, pat_adjustments); .insert(pat.hir_id, pat_adjustments);
} }
(expected, def_bm) (expected, def_bm, max_ref_mutability)
} }
fn check_pat_lit( fn check_pat_lit(
@ -1116,15 +1138,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Ty<'tcx>, expected: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>, pat_info: PatInfo<'tcx, '_>,
) -> Ty<'tcx> { ) -> Ty<'tcx> {
let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth } = pat_info;
let tcx = self.tcx; let tcx = self.tcx;
let on_error = |e| { let on_error = |e| {
for pat in subpats { for pat in subpats {
self.check_pat( self.check_pat(pat, Ty::new_error(tcx, e), pat_info);
pat,
Ty::new_error(tcx, e),
PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth },
);
} }
}; };
let report_unexpected_res = |res: Res| { let report_unexpected_res = |res: Res| {
@ -1169,7 +1186,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let pat_ty = pat_ty.no_bound_vars().expect("expected fn type"); let pat_ty = pat_ty.no_bound_vars().expect("expected fn type");
// Type-check the tuple struct pattern against the expected type. // Type-check the tuple struct pattern against the expected type.
let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, ti); let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, pat_info.top_info);
let had_err = if let Some(err) = diag { let had_err = if let Some(err) = diag {
err.emit(); err.emit();
true true
@ -1187,11 +1204,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) {
let field = &variant.fields[FieldIdx::from_usize(i)]; let field = &variant.fields[FieldIdx::from_usize(i)];
let field_ty = self.field_ty(subpat.span, field, args); let field_ty = self.field_ty(subpat.span, field, args);
self.check_pat( self.check_pat(subpat, field_ty, pat_info);
subpat,
field_ty,
PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth },
);
self.tcx.check_stability( self.tcx.check_stability(
variant.fields[FieldIdx::from_usize(i)].did, variant.fields[FieldIdx::from_usize(i)].did,

View file

@ -69,7 +69,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
hir::PatKind::Ref(inner, _) hir::PatKind::Ref(inner, _)
if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) => if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) =>
{ {
self.lower_pattern_unadjusted(inner) self.lower_pattern(inner)
} }
_ => self.lower_pattern_unadjusted(pat), _ => self.lower_pattern_unadjusted(pat),
}; };

View file

@ -38,6 +38,21 @@ pub fn main() {
if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { if let Some(Some(&mut x)) = &Some(Some(&mut 0)) {
let _: &u32 = x; let _: &u32 = x;
} }
if let &Some(Some(x)) = &Some(&mut Some(0)) {
let _: &u32 = x;
}
if let Some(&Some(&x)) = &Some(&mut Some(0)) {
let _: u32 = x;
}
if let Some(&Some(&x)) = &Some(&Some(0)) {
let _: u32 = x;
}
if let Some(&Some(&x)) = &Some(&mut Some(0)) {
let _: u32 = x;
}
if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) {
let _: u32 = x;
}
let &mut x = &&mut 0; let &mut x = &&mut 0;
let _: &u32 = x; let _: &u32 = x;

View file

@ -7,22 +7,27 @@ pub fn main() {
if let Some(&mut Some(&_)) = &Some(&Some(0)) { if let Some(&mut Some(&_)) = &Some(&Some(0)) {
//~^ ERROR: mismatched types //~^ ERROR: mismatched types
} }
if let Some(&Some(&_)) = &Some(&mut Some(0)) { if let Some(&Some(&mut _)) = &Some(&mut Some(0)) {
//~^ ERROR: mismatched types //~^ ERROR: mismatched types
} }
if let Some(&Some(x)) = &mut Some(&Some(0)) { if let Some(&Some(x)) = &mut Some(&Some(0)) {
let _: &mut u32 = x; let _: &mut u32 = x;
//~^ ERROR: mismatched types //~^ ERROR: mismatched types
} }
if let Some(&Some(&x)) = Some(&Some(&mut 0)) { if let Some(&Some(&_)) = Some(&Some(&mut 0)) {
//~^ ERROR: mismatched types
}
if let Some(&Some(&mut _)) = &mut Some(&Some(0)) {
//~^ ERROR: mismatched types
}
if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) {
//~^ ERROR: mismatched types //~^ ERROR: mismatched types
} }
let &mut x = &&0;
//~^ ERROR: mismatched types
let _: &u32 = x;
let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; let &mut _= &&0;
//~^ ERROR: mismatched types
let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0;
//~^ ERROR: mismatched types //~^ ERROR: mismatched types
let _: &u32 = x;
} }

View file

@ -12,13 +12,13 @@ LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) {
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:10:23 --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:10:23
| |
LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) {
| ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` | ^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>`
| | | |
| expected integer, found `&_` | expected integer, found `&mut _`
| |
= note: expected type `{integer}` = note: expected type `{integer}`
found reference `&_` found mutable reference `&mut _`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:14:27 --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:14:27
@ -34,42 +34,58 @@ LL | let _: &mut u32 = x;
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:17:23 --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:17:23
| |
LL | if let Some(&Some(&x)) = Some(&Some(&mut 0)) { LL | if let Some(&Some(&_)) = Some(&Some(&mut 0)) {
| ^^ ------------------- this expression has type `Option<&Option<&mut {integer}>>` | ^^ ------------------- this expression has type `Option<&Option<&mut {integer}>>`
| | | |
| types differ in mutability | types differ in mutability
| |
= note: expected mutable reference `&mut {integer}` = note: expected mutable reference `&mut {integer}`
found reference `&_` found reference `&_`
help: consider removing `&` from the pattern
|
LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) {
| ~
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:21:9 --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:20:23
| |
LL | let &mut x = &&0; LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) {
| ^^^^^^ --- this expression has type `&&{integer}` | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>`
| | | |
| expected integer, found `&mut _` | expected integer, found `&mut _`
| help: to declare a mutable variable use: `mut x`
| |
= note: expected type `{integer}` = note: expected type `{integer}`
found mutable reference `&mut _` found mutable reference `&mut _`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:25:9 --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:29
| |
LL | let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) {
| ^^^^^^ ------------------------- this expression has type `&Option<Option<&mut Option<{integer}>>>`
| |
| expected integer, found `&mut _`
|
= note: expected type `{integer}`
found mutable reference `&mut _`
error[E0308]: mismatched types
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:28:9
|
LL | let &mut _= &&0;
| ^^^^^^ --- this expression has type `&&{integer}`
| |
| expected integer, found `&mut _`
|
= note: expected type `{integer}`
found mutable reference `&mut _`
error[E0308]: mismatched types
--> $DIR/ref_pat_eat_one_layer_2024_fail.rs:31:9
|
LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0;
| ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}`
| | | |
| expected integer, found `&mut _` | expected integer, found `&mut _`
| help: to declare a mutable variable use: `mut x`
| |
= note: expected type `{integer}` = note: expected type `{integer}`
found mutable reference `&mut _` found mutable reference `&mut _`
error: aborting due to 6 previous errors error: aborting due to 8 previous errors
For more information about this error, try `rustc --explain E0308`. For more information about this error, try `rustc --explain E0308`.