diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index fe2deee378e2c..7029f5433d8b0 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -78,7 +78,7 @@ struct TopInfo<'tcx> { #[derive(Copy, Clone)] struct PatInfo<'tcx, 'a> { binding_mode: ByRef, - max_ref_mutbl: Mutability, + max_ref_mutbl: MutblCap, top_info: TopInfo<'tcx>, decl_origin: Option>, @@ -124,22 +124,61 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } /// Mode for adjusting the expected type and binding mode. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] enum AdjustMode { /// Peel off all immediate reference types. Peel, /// Reset binding mode to the initial mode. /// Used for destructuring assignment, where we don't want any match ergonomics. Reset, - /// Produced by ref patterns. - /// Reset the binding mode to the initial mode, - /// and if the old biding mode was by-reference - /// with mutability matching the pattern, - /// mark the pattern as having consumed this reference. - ResetAndConsumeRef(Mutability), /// Pass on the input binding mode and expected type. Pass, } +/// `ref mut` patterns (explicit or match-ergonomics) +/// are not allowed behind an `&` reference. +/// +/// This includes explicit `ref mut` behind `&` patterns +/// that match against `&mut` references, +/// where the code would have compiled +/// had the pattern been written as `&mut`. +/// However, the borrow checker will not catch +/// this last case, so we need to throw an error ourselves. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum MutblCap { + /// Mutability restricted to immutable. + Not, + + /// Mutability restricted to immutable, but only because of the pattern + /// (not the scrutinee type). + /// + /// The contained span, if present, points to an `&` pattern + /// that is the reason for the restriction, + /// and which will be reported in a diagnostic. + WeaklyNot(Option), + + /// No restriction on mutability + Mut, +} + +impl MutblCap { + #[must_use] + fn cap_to_weakly_not(self, span: Option) -> Self { + match self { + MutblCap::Not => MutblCap::Not, + _ => MutblCap::WeaklyNot(span), + } + } + + #[must_use] + fn as_mutbl(self) -> Mutability { + match self { + MutblCap::Not | MutblCap::WeaklyNot(_) => Mutability::Not, + MutblCap::Mut => Mutability::Mut, + } + } +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Type check the given top level pattern against the `expected` type. /// @@ -160,7 +199,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let info = TopInfo { expected, origin_expr, span }; let pat_info = PatInfo { binding_mode: ByRef::No, - max_ref_mutbl: Mutability::Mut, + max_ref_mutbl: MutblCap::Mut, top_info: info, decl_origin, current_depth: 0, @@ -169,14 +208,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Type check the given `pat` against the `expected` type - /// with the provided `def_bm` (default binding mode). + /// with the provided `binding_mode` (default binding mode). /// /// Outside of this module, `check_pat_top` should always be used. /// Conversely, inside this module, `check_pat_top` should never be used. #[instrument(level = "debug", skip(self, pat_info))] fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) { - let PatInfo { binding_mode: def_bm, max_ref_mutbl, top_info: ti, current_depth, .. } = - pat_info; + let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info; let path_res = match &pat.kind { PatKind::Path(qpath) => Some( @@ -185,10 +223,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); - let (expected, def_bm, max_ref_mutbl, ref_pattern_already_consumed) = - self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode, max_ref_mutbl); + let (expected, binding_mode, max_ref_mutbl) = + self.calc_default_binding_mode(pat, expected, binding_mode, adjust_mode, max_ref_mutbl); let pat_info = PatInfo { - binding_mode: def_bm, + binding_mode, max_ref_mutbl, top_info: ti, decl_origin: pat_info.decl_origin, @@ -201,8 +239,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Never => expected, PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti), PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), - PatKind::Binding(ba, var_id, _, sub) => { - self.check_pat_ident(pat, ba, var_id, sub, expected, pat_info) + PatKind::Binding(ba, var_id, ident, sub) => { + self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info) } PatKind::TupleStruct(ref qpath, subpats, ddpos) => { self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info) @@ -224,14 +262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info), PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info), - PatKind::Ref(inner, mutbl) => self.check_pat_ref( - pat, - inner, - mutbl, - expected, - pat_info, - ref_pattern_already_consumed, - ), + PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info), PatKind::Slice(before, slice, after) => { self.check_pat_slice(pat.span, before, slice, after, expected, pat_info) } @@ -284,52 +315,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Compute the new expected type and default binding mode from the old ones /// as well as the pattern form we are currently checking. - /// - /// Last entry is only relevant for ref patterns (`&` and `&mut`); - /// if `true`, then the ref pattern consumed a match ergonomics inserted reference - /// and so does no need to match against a reference in the scrutinee type. fn calc_default_binding_mode( &self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, def_br: ByRef, adjust_mode: AdjustMode, - max_ref_mutbl: Mutability, - ) -> (Ty<'tcx>, ByRef, Mutability, bool) { - if let ByRef::Yes(mutbl) = def_br { - debug_assert!(mutbl <= max_ref_mutbl); + max_ref_mutbl: MutblCap, + ) -> (Ty<'tcx>, ByRef, MutblCap) { + if let ByRef::Yes(Mutability::Mut) = def_br { + debug_assert!(max_ref_mutbl == MutblCap::Mut); } match adjust_mode { - AdjustMode::Pass => (expected, def_br, max_ref_mutbl, false), - AdjustMode::Reset => (expected, ByRef::No, Mutability::Mut, false), - AdjustMode::ResetAndConsumeRef(ref_pat_mutbl) => { - let mutbls_match = def_br == ByRef::Yes(ref_pat_mutbl); - if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { - if mutbls_match { - debug!("consuming inherited reference"); - (expected, ByRef::No, cmp::min(max_ref_mutbl, ref_pat_mutbl), true) - } else { - let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut { - self.peel_off_references( - pat, - expected, - def_br, - Mutability::Not, - max_ref_mutbl, - ) - } else { - (expected, def_br.cap_ref_mutability(Mutability::Not), Mutability::Not) - }; - (new_ty, new_bm, max_ref_mutbl, false) - } - } else { - (expected, ByRef::No, max_ref_mutbl, mutbls_match) - } - } + AdjustMode::Pass => (expected, def_br, max_ref_mutbl), + AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut), AdjustMode::Peel => { - let peeled = - self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl); - (peeled.0, peeled.1, peeled.2, false) + self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl) } } } @@ -375,17 +376,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // a reference type wherefore peeling doesn't give up any expressiveness. _ => AdjustMode::Peel, }, - // When encountering a `& mut? pat` pattern, reset to "by value". - // This is so that `x` and `y` here are by value, as they appear to be: - // - // ``` - // match &(&22, &44) { - // (&x, &y) => ... - // } - // ``` - // - // See issue #46688. - PatKind::Ref(_, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl), + // Ref patterns are complicated, we handle them in `check_pat_ref`. + PatKind::Ref(..) => AdjustMode::Pass, // A `_` pattern works with any expected type, so there's no need to do anything. PatKind::Wild // A malformed pattern doesn't have an expected type, so let's just accept any type. @@ -411,8 +403,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, mut def_br: ByRef, max_peelable_mutability: Mutability, - mut max_ref_mutability: Mutability, - ) -> (Ty<'tcx>, ByRef, Mutability) { + mut max_ref_mutability: MutblCap, + ) -> (Ty<'tcx>, ByRef, MutblCap) { 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, // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches @@ -446,9 +438,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { - def_br = def_br.cap_ref_mutability(max_ref_mutability); + def_br = def_br.cap_ref_mutability(max_ref_mutability.as_mutbl()); if def_br == ByRef::Yes(Mutability::Not) { - max_ref_mutability = Mutability::Not; + max_ref_mutability = MutblCap::Not; } } @@ -665,8 +657,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_ident( &self, pat: &'tcx Pat<'tcx>, - ba: BindingMode, + user_bind_annot: BindingMode, var_id: HirId, + ident: Ident, sub: Option<&'tcx Pat<'tcx>>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, @@ -674,7 +667,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let PatInfo { binding_mode: def_br, top_info: ti, .. } = pat_info; // Determine the binding mode... - let bm = match ba { + let bm = match user_bind_annot { BindingMode(ByRef::No, Mutability::Mut) if !(pat.span.at_least_rust_2024() && self.tcx.features().mut_preserve_binding_mode_2024) @@ -690,8 +683,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { BindingMode(ByRef::No, Mutability::Mut) } BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), - BindingMode(ByRef::Yes(_), _) => ba, + BindingMode(ByRef::Yes(_), _) => user_bind_annot, }; + + if bm.0 == ByRef::Yes(Mutability::Mut) + && let MutblCap::WeaklyNot(and_pat_span) = pat_info.max_ref_mutbl + { + let mut err = struct_span_code_err!( + self.tcx.dcx(), + ident.span, + E0596, + "cannot borrow as mutable inside an `&` pattern" + ); + + if let Some(span) = and_pat_span { + err.span_suggestion( + span, + "replace this `&` with `&mut`", + "&mut ", + Applicability::MachineApplicable, + ); + } + err.emit(); + } + // ...and store it in a side table: self.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm); @@ -717,7 +732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If there are multiple arms, make sure they all agree on // what the type of the binding `x` ought to be. if var_id != pat.hir_id { - self.check_binding_alt_eq_ty(ba, pat.span, var_id, local_ty, ti); + self.check_binding_alt_eq_ty(user_bind_annot, pat.span, var_id, local_ty, ti); } if let Some(p) = sub { @@ -2102,72 +2117,136 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat: &'tcx Pat<'tcx>, inner: &'tcx Pat<'tcx>, - mutbl: Mutability, - expected: Ty<'tcx>, - pat_info: PatInfo<'tcx, '_>, - consumed_inherited_ref: bool, + pat_mutbl: Mutability, + mut expected: Ty<'tcx>, + mut pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - if consumed_inherited_ref - && pat.span.at_least_rust_2024() - && self.tcx.features().ref_pat_eat_one_layer_2024 - { - self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); - self.check_pat(inner, expected, pat_info); - expected + // FIXME: repace with `bool` once final decision on 1 vs 2 layers is made + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + enum MatchErgonomicsMode { + EatOneLayer, + EatTwoLayers, + Legacy, + } + + let match_ergonomics_mode = + if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + MatchErgonomicsMode::EatOneLayer + } else if self.tcx.features().ref_pat_everywhere { + MatchErgonomicsMode::EatTwoLayers + } else { + MatchErgonomicsMode::Legacy + }; + + let mut inherited_ref_mutbl_match = false; + if match_ergonomics_mode != MatchErgonomicsMode::Legacy { + if pat_mutbl == Mutability::Not { + // Prevent the inner pattern from binding with `ref mut`. + pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not( + inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end)), + ); + } + + if let ByRef::Yes(inh_mut) = pat_info.binding_mode { + inherited_ref_mutbl_match = pat_mutbl <= inh_mut; + } + + if inherited_ref_mutbl_match { + pat_info.binding_mode = ByRef::No; + if match_ergonomics_mode == MatchErgonomicsMode::EatOneLayer { + self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); + self.check_pat(inner, expected, pat_info); + return expected; + } + } else if match_ergonomics_mode == MatchErgonomicsMode::EatOneLayer + && pat_mutbl == Mutability::Mut + { + // `&mut` patterns pell off `&` references + let (new_expected, new_bm, max_ref_mutbl) = self.peel_off_references( + pat, + expected, + pat_info.binding_mode, + Mutability::Not, + pat_info.max_ref_mutbl, + ); + expected = new_expected; + pat_info.binding_mode = new_bm; + pat_info.max_ref_mutbl = max_ref_mutbl; + } } else { - let tcx = self.tcx; - let expected = self.shallow_resolve(expected); - let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) { - Ok(()) => { - // `demand::subtype` would be good enough, but using `eqtype` turns - // out to be equally general. See (note_1) for details. - - // Take region, inner-type from expected type if we can, - // to avoid creating needless variables. This also helps with - // the bad interactions of the given hack detailed in (note_1). - debug!("check_pat_ref: expected={:?}", expected); - match *expected.kind() { - ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), - _ => { - if consumed_inherited_ref && self.tcx.features().ref_pat_everywhere { - // We already matched against a match-ergonmics inserted reference, - // so we don't need to match against a reference from the original type. - // Save this infor for use in lowering later - self.typeck_results - .borrow_mut() - .skipped_ref_pats_mut() - .insert(pat.hir_id); - (expected, expected) - } else { - let inner_ty = self.next_ty_var(inner.span); - let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); - debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); - let err = self.demand_eqtype_pat_diag( - pat.span, - expected, - ref_ty, - pat_info.top_info, - ); + // Reset binding mode on old editions + pat_info.binding_mode = ByRef::No; + pat_info.max_ref_mutbl = MutblCap::Mut + } - // Look for a case like `fn foo(&foo: u32)` and suggest - // `fn foo(foo: &u32)` - if let Some(mut err) = err { - self.borrow_pat_suggestion(&mut err, pat); - err.emit(); - } - (ref_ty, inner_ty) - } + let tcx = self.tcx; + expected = self.try_structurally_resolve_type(pat.span, expected); + let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) { + Ok(()) => { + // `demand::subtype` would be good enough, but using `eqtype` turns + // out to be equally general. See (note_1) for details. + + // Take region, inner-type from expected type if we can, + // to avoid creating needless variables. This also helps with + // the bad interactions of the given hack detailed in (note_1). + debug!("check_pat_ref: expected={:?}", expected); + match *expected.kind() { + ty::Ref(_, r_ty, r_mutbl) if r_mutbl == pat_mutbl => { + if r_mutbl == Mutability::Not + && match_ergonomics_mode != MatchErgonomicsMode::Legacy + { + pat_info.max_ref_mutbl = MutblCap::Not; } + + (expected, r_ty) + } + + // `&` pattern eats `&mut` reference + ty::Ref(_, r_ty, Mutability::Mut) + if pat_mutbl == Mutability::Not + && match_ergonomics_mode != MatchErgonomicsMode::Legacy => + { + (expected, r_ty) + } + + _ if inherited_ref_mutbl_match + && match_ergonomics_mode == MatchErgonomicsMode::EatTwoLayers => + { + // We already matched against a match-ergonmics inserted reference, + // so we don't need to match against a reference from the original type. + // Save this info for use in lowering later + self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); + (expected, expected) + } + + _ => { + let inner_ty = self.next_ty_var(inner.span); + let ref_ty = self.new_ref_ty(pat.span, pat_mutbl, inner_ty); + debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); + let err = self.demand_eqtype_pat_diag( + pat.span, + expected, + ref_ty, + pat_info.top_info, + ); + + // Look for a case like `fn foo(&foo: u32)` and suggest + // `fn foo(foo: &u32)` + if let Some(mut err) = err { + self.borrow_pat_suggestion(&mut err, pat); + err.emit(); + } + (ref_ty, inner_ty) } } - Err(guar) => { - let err = Ty::new_error(tcx, guar); - (err, err) - } - }; - self.check_pat(inner, inner_ty, pat_info); - ref_ty - } + } + Err(guar) => { + let err = Ty::new_error(tcx, guar); + (err, err) + } + }; + self.check_pat(inner, inner_ty, pat_info); + ref_ty } /// Create a reference type with a fresh region variable. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs index f1ac3e340e915..62e4f82a3ffbb 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs @@ -53,6 +53,12 @@ pub fn main() { if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { let _: u32 = x; } + if let Some(&Some(&x)) = Some(&Some(&mut 0)) { + let _: u32 = x; + } + if let Some(&Some(x)) = &mut Some(Some(0)) { + let _: u32 = x; + } let &mut x = &&mut 0; let _: &u32 = x; diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs index ec091bb17467d..96b4ff77ddb4b 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs @@ -14,18 +14,20 @@ pub fn main() { let _: &mut u32 = x; //~^ ERROR: mismatched types } - 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 } + if let Some(&mut Some(x)) = &Some(Some(0)) { + //~^ ERROR: mismatched types + } + if let Some(&mut Some(x)) = &Some(Some(0)) { + //~^ ERROR: mismatched types + } - - let &mut _= &&0; + let &mut _ = &&0; //~^ ERROR: mismatched types let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr index be71ee606c767..e06a645fc0d33 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr @@ -34,17 +34,6 @@ LL | let _: &mut u32 = x; error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:17:23 | -LL | if let Some(&Some(&_)) = Some(&Some(&mut 0)) { - | ^^ ------------------- this expression has type `Option<&Option<&mut {integer}>>` - | | - | types differ in mutability - | - = note: expected mutable reference `&mut {integer}` - found reference `&_` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:20:23 - | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` | | @@ -54,7 +43,7 @@ LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:29 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:20:29 | LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { | ^^^^^^ ------------------------- this expression has type `&Option>>` @@ -65,10 +54,32 @@ LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:28:9 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:17 + | +LL | if let Some(&mut Some(x)) = &Some(Some(0)) { + | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` + | | + | expected `Option<{integer}>`, found `&mut _` + | + = note: expected enum `Option<{integer}>` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:26:17 + | +LL | if let Some(&mut Some(x)) = &Some(Some(0)) { + | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` + | | + | expected `Option<{integer}>`, found `&mut _` + | + = note: expected enum `Option<{integer}>` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:30:9 | -LL | let &mut _= &&0; - | ^^^^^^ --- this expression has type `&&{integer}` +LL | let &mut _ = &&0; + | ^^^^^^ --- this expression has type `&&{integer}` | | | expected integer, found `&mut _` | @@ -76,7 +87,7 @@ LL | let &mut _= &&0; found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:31:9 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:33:9 | LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` @@ -86,6 +97,6 @@ LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; = note: expected type `{integer}` found mutable reference `&mut _` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs index 364554884073a..3cdf47c1dbfe1 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs @@ -8,4 +8,7 @@ pub fn main() { //~^ ERROR: cannot move out of a shared reference [E0507] let _: &u32 = x; } + + let &ref mut x = &0; + //~^ cannot borrow data in a `&` reference as mutable [E0596] } diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr index ccfb5c7a0c076..8b86fa65c4d8f 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr @@ -12,6 +12,13 @@ help: consider borrowing the pattern binding LL | if let Some(&Some(ref x)) = Some(&Some(&mut 0)) { | +++ -error: aborting due to 1 previous error +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/ref_pat_eat_one_layer_2024_fail2.rs:12:10 + | +LL | let &ref mut x = &0; + | ^^^^^^^^^ cannot borrow as mutable + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0507`. +Some errors have detailed explanations: E0507, E0596. +For more information about an error, try `rustc --explain E0507`. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed new file mode 100644 index 0000000000000..bc7a58a382d60 --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed @@ -0,0 +1,30 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +//@ run-rustfix +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] + +pub fn main() { + if let Some(&mut Some(ref mut x)) = &mut Some(Some(0)) { + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + if let &mut Some(Some(ref mut x)) = &mut Some(Some(0)) { + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + macro_rules! pat { + ($var:ident) => { ref mut $var }; + } + let &mut pat!(x) = &mut 0; + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + + let &mut (ref mut a, ref mut b) = &mut (true, false); + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //~| ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut bool = a; + let _: &mut bool = b; +} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs new file mode 100644 index 0000000000000..c6d72b0a9d777 --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs @@ -0,0 +1,30 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +//@ run-rustfix +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] + +pub fn main() { + if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + macro_rules! pat { + ($var:ident) => { ref mut $var }; + } + let &pat!(x) = &mut 0; + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + + let &(ref mut a, ref mut b) = &mut (true, false); + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //~| ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut bool = a; + let _: &mut bool = b; +} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr new file mode 100644 index 0000000000000..964e9f36596bd --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr @@ -0,0 +1,43 @@ +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:8:31 + | +LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:13:31 + | +LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:21:15 + | +LL | let &pat!(x) = &mut 0; + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:25:19 + | +LL | let &(ref mut a, ref mut b) = &mut (true, false); + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:25:30 + | +LL | let &(ref mut a, ref mut b) = &mut (true, false); + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs b/tests/ui/match/ref_pat_everywhere-fail.rs similarity index 63% rename from tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs rename to tests/ui/match/ref_pat_everywhere-fail.rs index 9dd7a7893ec71..d1b1c04730d34 100644 --- a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs +++ b/tests/ui/match/ref_pat_everywhere-fail.rs @@ -5,11 +5,7 @@ pub fn main() { //~^ ERROR: mismatched types [E0308] let _: u32 = x; } - if let &Some(x) = &mut Some(0) { - //~^ ERROR: mismatched types [E0308] - let _: u32 = x; - } - if let Some(&x) = &mut Some(0) { + if let Some(&mut x) = Some(&0) { //~^ ERROR: mismatched types [E0308] let _: u32 = x; } diff --git a/tests/ui/match/ref_pat_everywhere-fail.stderr b/tests/ui/match/ref_pat_everywhere-fail.stderr new file mode 100644 index 0000000000000..25a01129f4a9d --- /dev/null +++ b/tests/ui/match/ref_pat_everywhere-fail.stderr @@ -0,0 +1,38 @@ +error[E0308]: mismatched types + --> $DIR/ref_pat_everywhere-fail.rs:4:17 + | +LL | if let Some(&x) = Some(0) { + | ^^ ------- this expression has type `Option<{integer}>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(x) = Some(0) { + | ~ + +error[E0308]: mismatched types + --> $DIR/ref_pat_everywhere-fail.rs:8:17 + | +LL | if let Some(&mut x) = Some(&0) { + | ^^^^^^ -------- this expression has type `Option<&{integer}>` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/ref_pat_everywhere-fail.rs:8:17 + | +LL | if let Some(&mut x) = Some(&0) { + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL | if let Some(x) = Some(&0) { + | ~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr b/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr deleted file mode 100644 index d512ea5f957da..0000000000000 --- a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr +++ /dev/null @@ -1,44 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:4:17 - | -LL | if let Some(&x) = Some(0) { - | ^^ ------- this expression has type `Option<{integer}>` - | | - | expected integer, found `&_` - | - = note: expected type `{integer}` - found reference `&_` -help: consider removing `&` from the pattern - | -LL | if let Some(x) = Some(0) { - | ~ - -error[E0308]: mismatched types - --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:8:12 - | -LL | if let &Some(x) = &mut Some(0) { - | ^^^^^^^^ ------------ this expression has type `&mut Option<{integer}>` - | | - | types differ in mutability - | - = note: expected mutable reference `&mut Option<{integer}>` - found reference `&_` - -error[E0308]: mismatched types - --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:12:17 - | -LL | if let Some(&x) = &mut Some(0) { - | ^^ ------------ this expression has type `&mut Option<{integer}>` - | | - | expected integer, found `&_` - | - = note: expected type `{integer}` - found reference `&_` -help: consider removing `&` from the pattern - | -LL | if let Some(x) = &mut Some(0) { - | ~ - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_everywhere.rs b/tests/ui/match/ref_pat_everywhere.rs index b3daca484092b..9a79c548475f6 100644 --- a/tests/ui/match/ref_pat_everywhere.rs +++ b/tests/ui/match/ref_pat_everywhere.rs @@ -15,4 +15,10 @@ pub fn main() { if let Some(Some(&x)) = &Some(&mut Some(0)) { let _: u32 = x; } + if let &Some(x) = &mut Some(0) { + let _: u32 = x; + } + if let Some(&x) = &mut Some(0) { + let _: u32 = x; + } }