Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 0 additions & 38 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,23 +229,19 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> {
is_loop_move = true;
}

let mut has_suggest_reborrow = false;
if !seen_spans.contains(&move_span) {
self.suggest_ref_or_clone(
mpi,
&mut err,
move_spans,
moved_place.as_ref(),
&mut has_suggest_reborrow,
closure,
);

let msg_opt = CapturedMessageOpt {
is_partial_move,
is_loop_message,
is_move_msg,
is_loop_move,
has_suggest_reborrow,
maybe_reinitialized_locations_is_empty: maybe_reinitialized_locations
.is_empty(),
};
Expand Down Expand Up @@ -356,7 +352,6 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> {
err: &mut Diag<'_>,
move_spans: UseSpans<'tcx>,
moved_place: PlaceRef<'tcx>,
has_suggest_reborrow: &mut bool,
moved_or_invoked_closure: bool,
) {
let move_span = match move_spans {
Expand Down Expand Up @@ -477,20 +472,6 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> {
None
};

// If the moved value is a mut reference, it is used in a
// generic function and it's type is a generic param, it can be
// reborrowed to avoid moving.
// for example:
// struct Y(u32);
// x's type is '& mut Y' and it is used in `fn generic<T>(x: T) {}`.
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind()
&& arg_param.is_some()
{
*has_suggest_reborrow = true;
self.suggest_reborrow(err, expr.span, moved_place);
return;
}

// If the moved place is used generically by the callee and a reference to it
// would still satisfy any bounds on its type, suggest borrowing.
if let Some(&param) = arg_param
Expand Down Expand Up @@ -632,25 +613,6 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> {
}
}

pub(crate) fn suggest_reborrow(
&self,
err: &mut Diag<'_>,
span: Span,
moved_place: PlaceRef<'tcx>,
) {
err.span_suggestion_verbose(
span.shrink_to_lo(),
format!(
"consider creating a fresh reborrow of {} here",
self.describe_place(moved_place)
.map(|n| format!("`{n}`"))
.unwrap_or_else(|| "the mutable reference".to_string()),
),
"&mut *",
Applicability::MachineApplicable,
);
}

/// If a place is used after being moved as an argument to a function, the function is generic
/// in that argument, and a reference to the argument's type would still satisfy the function's
/// bounds, suggest borrowing. This covers, e.g., borrowing an `impl Fn()` argument being passed
Expand Down
31 changes: 0 additions & 31 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ use tracing::debug;

use super::MirBorrowckCtxt;
use super::borrow_set::BorrowData;
use crate::LocalMutationIsAllowed;
use crate::constraints::OutlivesConstraint;
use crate::nll::ConstraintDescription;
use crate::session_diagnostics::{
Expand Down Expand Up @@ -993,8 +992,6 @@ struct CapturedMessageOpt {
is_partial_move: bool,
is_loop_message: bool,
is_move_msg: bool,
is_loop_move: bool,
has_suggest_reborrow: bool,
maybe_reinitialized_locations_is_empty: bool,
}

Expand Down Expand Up @@ -1235,8 +1232,6 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
is_partial_move: is_partial,
is_loop_message,
is_move_msg,
is_loop_move,
has_suggest_reborrow,
maybe_reinitialized_locations_is_empty,
} = msg_opt;
let mut suggested_cloning = false;
Expand Down Expand Up @@ -1435,32 +1430,6 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
}
_ => {}
}
// If the moved place was a `&mut` ref, then we can
// suggest to reborrow it where it was moved, so it
// will still be valid by the time we get to the usage.
if let ty::Ref(_, _, hir::Mutability::Mut) =
moved_place.ty(self.body, self.infcx.tcx).ty.kind()
{
// The `&mut *place` reborrow suggestion is `MachineApplicable`, so
// only offer it where `*place` can be borrowed mutably: a value
// captured by an `Fn` closure (held via `&self`) cannot, and the
// suggestion would otherwise fail to compile with E0596.
let reborrow_place = self.infcx.tcx.mk_place_deref(moved_place);
let reborrow_is_valid = self
.is_mutable(reborrow_place.as_ref(), LocalMutationIsAllowed::No)
.is_ok();
// Suggest `reborrow` in other place for following situations:
// 1. If we are in a loop this will be suggested later.
// 2. If the moved value is a mut reference, it is used in a
// generic function and the corresponding arg's type is generic param.
if !is_loop_move && !has_suggest_reborrow && reborrow_is_valid {
self.suggest_reborrow(
err,
move_span.shrink_to_lo(),
moved_place.as_ref(),
);
}
}
} else {
match desugaring {
Some((CallDesugaringKind::Await, _)) => {
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_borrowck/src/diagnostics/move_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,8 +552,6 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> {
is_partial_move: false,
is_loop_message: false,
is_move_msg: false,
is_loop_move: false,
has_suggest_reborrow: false,
maybe_reinitialized_locations_is_empty: true,
};
let suggested_cloning = if let Some(use_spans) = use_spans {
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1481,9 +1481,10 @@ pub enum Rvalue<'tcx> {
/// Future work may add support for multiple lifetimes and changing memory layout as part of
/// CoerceShared. These may be end up implemented as multiple MIR operations.
///
/// This is produced by the [`ExprKind::Reborrow`].
/// This is produced by [`ExprKind::CoerceShared`],
/// and by `consume_by_copy_reborrow_or_move()` in `rustc_mir_build`.
///
/// [`ExprKind::Reborrow`]: crate::thir::ExprKind::Reborrow
/// [`ExprKind::CoerceShared`]: crate::thir::ExprKind::CoerceShared
Reborrow(Ty<'tcx>, Mutability, Place<'tcx>),
}

Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,16 +554,15 @@ pub enum ExprKind<'tcx> {
Yield {
value: ExprId,
},
/// Use of an ADT that implements the Reborrow (for Mut) or CoerceShared traits (for Not). This
/// Use of an ADT that implements the CoerceShared trait. This
/// expression is produced by the [`Adjust::GenericReborrow`] in places where normally the ADT
/// would be moved or assigned over. Instead, this produces an [`Rvalue::Reborrow`] which
/// produces a bitwise copy of the source ADT and disables the source for the copy's lifetime.
///
/// [`Adjust::GenericReborrow`]: crate::ty::adjustment::Adjust::GenericReborrow
/// [`Rvalue::Reborrow`]: mir::Rvalue::Reborrow
Reborrow {
CoerceShared {
source: ExprId,
mutability: Mutability,
target: Ty<'tcx>,
},
}
Expand Down
4 changes: 1 addition & 3 deletions compiler/rustc_middle/src/thir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
}
ThreadLocalRef(_) => {}
Yield { value } => visitor.visit_expr(&visitor.thir()[value]),
Reborrow { source, mutability: _, target: _ } => {
visitor.visit_expr(&visitor.thir()[source])
}
CoerceShared { source, target: _ } => visitor.visit_expr(&visitor.thir()[source]),
}
}

Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_middle/src/ty/adjustment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,10 @@ pub enum Adjust {
/// Take a user-type T implementing the Reborrow trait (for Mut) or the CoerceShared trait (for
/// Not) and reborrow as `T` or `CoreceShared<U>`.
///
/// This produces an [`ExprKind::Reborrow`].
/// Thu Not form produces an [`ExprKind::CoerceShared`]; the Mut one produces no THIR,
/// and exists only for closure capture inference.
///
/// [`ExprKind::Reborrow`]: crate::thir::ExprKind::Reborrow
/// [`ExprKind::CoerceShared`]: crate::thir::ExprKind::CoerceShared
GenericReborrow(hir::Mutability),
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/builder/expr/as_place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// A reborrow is an rvalue. If a place is needed for it, materialize
// the rvalue in a temporary instead of treating the reborrow
// expression itself as an assignable place.
| ExprKind::Reborrow { .. }
| ExprKind::CoerceShared { .. }
| ExprKind::WrapUnsafeBinder { .. } => {
// these are not places, so we need to make a temporary.
debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place)));
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// by reference captures use as_operand
Some(Category::Place) => {
let place = unpack!(block = this.as_place(block, upvar));
this.consume_by_copy_or_move(place)
this.consume_by_copy_reborrow_or_move(place, block, upvar_expr.span)
}
_ => {
// Turn mutable borrow captures into unique
Expand Down Expand Up @@ -435,9 +435,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);
block.and(Rvalue::Use(operand, WithRetag::Yes))
}
ExprKind::Reborrow { source, mutability, target } => {
let temp = unpack!(block = this.as_temp(block, scope, source, mutability));
block.and(Rvalue::Reborrow(target, mutability, temp.into()))
ExprKind::CoerceShared { source, target } => {
let temp = unpack!(block = this.as_temp(block, scope, source, Mutability::Not));
block.and(Rvalue::Reborrow(target, Mutability::Not, temp.into()))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/builder/expr/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl Category {
// A reborrow expression produces a value represented in MIR as
// `Rvalue::Reborrow`. Its source may be a place, but the reborrow
// expression itself does not denote an assignable place.
| ExprKind::Reborrow { .. }
| ExprKind::CoerceShared { .. }
| ExprKind::ThreadLocalRef(_)
| ExprKind::WrapUnsafeBinder { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)),

Expand Down
30 changes: 21 additions & 9 deletions compiler/rustc_mir_build/src/builder/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,14 +371,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
)
.into_block();

let arg = this.consume_by_copy_reborrow_or_move(
state_result_place,
block,
scrutinee_span,
);
this.cfg.push_assign(
block,
source_info,
state_place,
Rvalue::Use(
this.consume_by_copy_or_move(state_result_place),
WithRetag::Yes,
),
Rvalue::Use(arg, WithRetag::Yes),
);
block.unit()
},
Expand Down Expand Up @@ -642,7 +644,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
None => {
let place =
place_builder.clone_project(PlaceElem::Field(n, *ty));
this.consume_by_copy_or_move(place.to_place(this))
this.consume_by_copy_reborrow_or_move(
place.to_place(this),
block,
this.thir.exprs[*base].span,
)
}
})
.collect()
Expand Down Expand Up @@ -839,7 +845,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
debug_assert!(Category::of(&expr.kind) == Some(Category::Place));

let place = unpack!(block = this.as_place(block, expr_id));
let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place), WithRetag::Yes);
let rvalue = Rvalue::Use(
this.consume_by_copy_reborrow_or_move(place, block, expr.span),
WithRetag::Yes,
);
this.cfg.push_assign(block, source_info, destination, rvalue);
block.unit()
}
Expand All @@ -854,7 +863,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}

let place = unpack!(block = this.as_place(block, expr_id));
let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place), WithRetag::Yes);
let rvalue = Rvalue::Use(
this.consume_by_copy_reborrow_or_move(place, block, expr.span),
WithRetag::Yes,
);
this.cfg.push_assign(block, source_info, destination, rvalue);
block.unit()
}
Expand Down Expand Up @@ -909,13 +921,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.cfg.push_assign(block, source_info, destination, rvalue);
block.unit()
}
ExprKind::Reborrow { source, mutability, target } => {
ExprKind::CoerceShared { source, target } => {
let place = unpack!(block = this.as_place(block, source));
this.cfg.push_assign(
block,
source_info,
destination,
Rvalue::Reborrow(target, mutability, place),
Rvalue::Reborrow(target, Mutability::Not, place),
);
block.unit()
}
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_mir_build/src/builder/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2723,9 +2723,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
}
let rvalue = match binding.binding_mode.0 {
ByRef::No => {
Rvalue::Use(self.consume_by_copy_or_move(binding.source), WithRetag::Yes)
}
ByRef::No => Rvalue::Use(
self.consume_by_copy_reborrow_or_move(binding.source, block, binding.span),
WithRetag::Yes,
),
Comment on lines -2726 to +2729

@dianne dianne Sep 30, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would resolve #143437; it makes the match compile. cc @Nadrieril

View changes since the review

@dianne dianne Sep 30, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also a bit funny if we don't change closure capture inference. By-value &mut bindings will actually reborrow, but capture inference treats it as a move. No breakage, but kind of inconsistent-feeling. e.g.

// This now works, since it reborrows.
fn main() {
    let x = &mut ();
    match x {
        _y => {}
    }
    x;
}
// This still fails. We'd need to insert pattern adjustments or
// special-case capture inference for it to work. Just doing
// adjustments for reborrows in expressions later won't work.
fn main() {
    let x = &mut ();
    let mut f = || {
        match x {
            _y => {}
        }
    };
    f();
    f();
}

ByRef::Yes(pinnedness, mutbl) => {
let rvalue =
Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source);
Expand Down
39 changes: 37 additions & 2 deletions compiler/rustc_mir_build/src/builder/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;
use tracing::debug;

use crate::builder::Builder;
use crate::builder::{Builder, PlaceBuilder};

impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Adds a new temporary value of type `ty` storing the result of
Expand Down Expand Up @@ -58,11 +58,46 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
temp
}

pub(crate) fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> {
pub(crate) fn consume_by_copy_reborrow_or_move(
&mut self,
place: Place<'tcx>,
block: BasicBlock,
span: Span,
) -> Operand<'tcx> {
let tcx = self.tcx;
let ty = place.ty(&self.local_decls, tcx).ty;

if self.infcx.type_is_copy_modulo_regions(self.param_env, ty) {
Operand::Copy(place)
} else if let ty::Ref(_, _, Mutability::Mut) = ty.kind() {
// Reborrow the mutable reference.
let reborrow = self.temp(ty, span);
let deref = PlaceBuilder::from(place).project(ProjectionElem::Deref).to_place(self);
self.cfg.push_assign(
block,
self.source_info(span),
reborrow,
Rvalue::Ref(
tcx.lifetimes.re_erased,
BorrowKind::Mut { kind: MutBorrowKind::Default },
deref,
),
);
Operand::Move(reborrow)
} else if tcx.features().reborrow()
&& self.infcx.type_is_reborrow_modulo_regions(self.param_env, ty)
// generic `T: Reborrow` not currently supported
&& ty.is_adt()
{
// Reborrow the value.
let reborrow = self.temp(ty, span);
self.cfg.push_assign(
block,
self.source_info(span),
reborrow,
Rvalue::Reborrow(ty, Mutability::Mut, place),
);
Operand::Move(reborrow)
} else {
Operand::Move(place)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
| ExprKind::InlineAsm { .. }
| ExprKind::LogicalOp { .. }
| ExprKind::ValueExpr { .. }
| ExprKind::Reborrow { .. } => {
| ExprKind::CoerceShared { .. } => {
// We don't need to save the old value and restore it
// because all the place expressions can't have more
// than one child.
Expand Down
Loading
Loading