Skip to content

Preserve generic reborrow while fixing identity moves#156786

Closed
P8L1 wants to merge 1 commit into
rust-lang:mainfrom
P8L1:fix/reborrow-identity-move
Closed

Preserve generic reborrow while fixing identity moves#156786
P8L1 wants to merge 1 commit into
rust-lang:mainfrom
P8L1:fix/reborrow-identity-move

Conversation

@P8L1
Copy link
Copy Markdown
Contributor

@P8L1 P8L1 commented May 20, 2026

Fixes a move-vs-generic-reborrow selection issue in typeck without changing the intended generic Reborrow behaviour.

In ordinary identity move/coercion contexts, a T -> T use should not become Adjust::GenericReborrow merely because the target ADT implements Reborrow. This PR makes those ordinary identity contexts try normal unification first; if normal unification succeeds, the expression remains an ordinary move/copy.

cc @aapoalas

Ordinary identity move/coercion contexts should not become Adjust::GenericReborrow merely because the target ADT implements Reborrow.

Try ordinary unification first in the ordinary identity-move path, while preserving intended generic Reborrow behavior for implicit argument reborrows. This keeps custom Reborrow tests such as repeated by-value use of marker and mutable wrapper types working as before.

The fix is intentionally limited to typeck ordering and does not redesign the Reborrow representation.
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 20, 2026
@rustbot rustbot added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label May 20, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented May 20, 2026

r? @jieyouxu

rustbot has assigned @jieyouxu.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 73 candidates
  • Random selection from 20 candidates

Copy link
Copy Markdown
Contributor

@aapoalas aapoalas left a comment

Choose a reason for hiding this comment

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

I don't think this should be necessary, and I believe this would not match the semantics that we expect from comparing to &mut _.

If you dump the MIR for an identity function that takes a &mut _ you should see that it performs two reborrows, one from argument to local and one from local to return value. I assume with this change an identity function of a Reborrow type would no longer show the same form but would instead just have a single move. If one day &mut _ unifies with Reborrow then that could cause issues.

Further, I think this change would not actually fix the general issue. eg. I expect this function would not work:

fn foo<'a>(x: CustomMut<'a>) -> Result<(), CustomMut<'a>> {
  bar(x)?; // <--
  println!("{x:?}");
  Ok(())
}

// this function would work
fn bar<'a>(x: CustomMut<'a>) -> Result<(), CustomMut<'a>> {
  Err(x)
}

I expect that the marked line with ? usage would not compile as a reborrow will happen there, capturing the local x and then making the borrow checker angry at us trying to return a local value.

We have talks ongoing at the Rust all-hands to correct the implementation / design to resolve the problem of borrowing local places in reborrowing generally.

View changes since this review

@P8L1
Copy link
Copy Markdown
Contributor Author

P8L1 commented May 21, 2026

I don't think this should be necessary, and I believe this would not match the semantics that we expect from comparing to &mut _.

If you dump the MIR for an identity function that takes a &mut _ you should see that it performs two reborrows, one from argument to local and one from local to return value. I assume with this change an identity function of a Reborrow type would no longer show the same form but would instead just have a single move. If one day &mut _ unifies with Reborrow then that could cause issues.

Further, I think this change would not actually fix the general issue. eg. I expect this function would not work:

fn foo<'a>(x: CustomMut<'a>) -> Result<(), CustomMut<'a>> {
  bar(x)?; // <--
  println!("{x:?}");
  Ok(())
}

// this function would work
fn bar<'a>(x: CustomMut<'a>) -> Result<(), CustomMut<'a>> {
  Err(x)
}

I expect that the marked line with ? usage would not compile as a reborrow will happen there, capturing the local x and then making the borrow checker angry at us trying to return a local value.

We have talks ongoing at the Rust all-hands to correct the implementation / design to resolve the problem of borrowing local places in reborrowing generally.

View changes since this review

Thanks, I think you are right that this PR is treating a symptom at the wrong layer.

I checked the ordering change again. The PR makes Coerce::coerce try plain unification before coerce_reborrow only when allow_two_phase == AllowTwoPhase::No, so identity coercions in ordinary non-argument contexts can remain a move/copy. That does make the direct CustomMut<'a> -> CustomMut<'a> identity case compile, and the MIR for the scratch identity function is just:

_0 = move _1;
return;

That differs from the &mut identity shape you described. Dumping MIR for:

pub fn id_mut<'a, T>(x: &'a mut T) -> &'a mut T { x }

with -Zunpretty=mir -Zmir-opt-level=0 shows the two reborrows:

_2 = &mut (*_1);
_0 = &mut (*_2);

I also tested the ? example. On this PR, the bar(x)?; println!("{x:?}"); Ok(()) shape still fails with E0515/E0502: the reborrow captures the local x, and returning the residual requires that borrow to live for 'a. In my scratch version, even fn bar<'a>(x: CustomMut<'a>) -> Result<(), CustomMut<'a>> { Err(x) } hit the same local-place reborrow issue because Err(x) goes through a function-argument coercion path where this PR intentionally preserves reborrow-first behavior. A non-? call/use-after shape with a diverging bar_never(x) compiled, but that does not address the residual-return case.

I will be closing this PR, please let me know what happens at All Hands.

@P8L1 P8L1 closed this May 21, 2026
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants