From 56f5c388406c51ae1681323b1cc00b0291f90b15 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Mon, 25 May 2026 19:08:39 +0200 Subject: [PATCH] Fix const-eval of shared generic reborrows --- .../rustc_const_eval/src/interpret/step.rs | 12 +++-- .../rustc_mir_transform/src/promote_consts.rs | 6 +-- .../miri/tests/pass/reborrow-coerce-shared.rs | 48 +++++++++++++++++++ tests/ui/reborrow/coerce_shared_consteval.rs | 28 +++++++++++ .../reborrow/reborrow-promotion-rejected.rs | 21 ++++++++ .../reborrow-promotion-rejected.stderr | 13 +++++ 6 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 src/tools/miri/tests/pass/reborrow-coerce-shared.rs create mode 100644 tests/ui/reborrow/coerce_shared_consteval.rs create mode 100644 tests/ui/reborrow/reborrow-promotion-rejected.rs create mode 100644 tests/ui/reborrow/reborrow-promotion-rejected.stderr diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index c60fb92a7a200..0718638c1d219 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -230,9 +230,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { })?; } - Reborrow(_, _, place) => { - let op = self.eval_place_to_op(place, Some(dest.layout))?; - self.copy_op(&op, &dest)?; + Reborrow(_, mutability, place) => { + let op = self.eval_place_to_op(place, None)?; + if mutability.is_not() { + // Shared generic reborrows use `CoerceShared`: a bitwise copy into a + // distinct same-layout target ADT. + self.copy_op_allow_transmute(&op, &dest)?; + } else { + self.copy_op(&op, &dest)?; + } } RawPtr(kind, place) => { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index f3e86c4eef75d..3694a0614a7b7 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -580,11 +580,7 @@ impl<'tcx> Validator<'_, 'tcx> { self.validate_ref(*kind, place)?; } - Rvalue::Reborrow(_, _, place) => { - // FIXME(reborrow): should probably have a place_simplified like above. - let op = &Operand::Copy(*place); - self.validate_operand(op)? - } + Rvalue::Reborrow(..) => return Err(Unpromotable), Rvalue::Aggregate(_, operands) => { for o in operands { diff --git a/src/tools/miri/tests/pass/reborrow-coerce-shared.rs b/src/tools/miri/tests/pass/reborrow-coerce-shared.rs new file mode 100644 index 0000000000000..bb9918055bfc1 --- /dev/null +++ b/src/tools/miri/tests/pass/reborrow-coerce-shared.rs @@ -0,0 +1,48 @@ +// Regression test for the Miri reproducer in rust-lang/rust#156313. +// +// The issue's exact recursive example ICEd while evaluating the argument +// conversion before the recursion mattered. This keeps that same +// `CustomMut`-to-`CustomRef` call path, but terminates after one recursive +// call so it can be a pass test once the ICE is fixed. + +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +#[allow(unused)] +struct CustomMut<'a, T>(&'a mut T); +impl<'a, T> Reborrow for CustomMut<'a, T> {} +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} + +struct CustomRef<'a, T>(&'a T); + +impl<'a, T> Clone for CustomRef<'a, T> { + fn clone(&self) -> Self { + Self(self.0) + } +} +impl<'a, T> Copy for CustomRef<'a, T> {} + +fn method(_a: CustomRef<'_, ()>) {} + +fn recursive_method(_a: CustomRef<'_, ()>, recurse: bool) { + if recurse { + let a = CustomMut(&mut ()); + recursive_method(a, false); + } +} + +fn issue_156313_runtime_reproducer() { + let a = CustomMut(&mut ()); + method(a); +} + +fn issue_156313_recursive_call_reproducer() { + let a = CustomMut(&mut ()); + recursive_method(a, true); +} + +fn main() { + issue_156313_runtime_reproducer(); + issue_156313_recursive_call_reproducer(); +} diff --git a/tests/ui/reborrow/coerce_shared_consteval.rs b/tests/ui/reborrow/coerce_shared_consteval.rs new file mode 100644 index 0000000000000..29b44b6438631 --- /dev/null +++ b/tests/ui/reborrow/coerce_shared_consteval.rs @@ -0,0 +1,28 @@ +//@ run-pass +// Regression test for the const-eval reproducer in rust-lang/rust#156313. + +#![feature(reborrow)] +#![allow(dead_code)] +#![allow(unused_variables)] + +use std::marker::{CoerceShared, Reborrow}; + +pub struct MyMut<'a>(&'a u8); + +impl Reborrow for MyMut<'_> {} + +#[derive(Clone, Copy)] +pub struct MyRef<'a>(&'a u8); + +impl<'a> CoerceShared> for MyMut<'a> {} + +const fn consteval_reproducer() { + let value = 1; + foo(MyMut(&value)); +} + +const fn foo(x: MyRef<'_>) {} + +fn main() { + const { consteval_reproducer(); } +} diff --git a/tests/ui/reborrow/reborrow-promotion-rejected.rs b/tests/ui/reborrow/reborrow-promotion-rejected.rs new file mode 100644 index 0000000000000..38366cd9ac2a7 --- /dev/null +++ b/tests/ui/reborrow/reborrow-promotion-rejected.rs @@ -0,0 +1,21 @@ +//@ check-fail + +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +struct MyMut<'a>(&'a u8); +impl Reborrow for MyMut<'_> {} + +#[derive(Clone, Copy)] +struct MyRef<'a>(&'a u8); +impl<'a> CoerceShared> for MyMut<'a> {} + +const fn coerce(x: MyRef<'_>) -> MyRef<'_> { + x +} + +static BAD: &'static MyRef<'static> = &coerce(MyMut(&1)); +//~^ ERROR temporary value dropped while borrowed + +fn main() {} diff --git a/tests/ui/reborrow/reborrow-promotion-rejected.stderr b/tests/ui/reborrow/reborrow-promotion-rejected.stderr new file mode 100644 index 0000000000000..f7e1560f02089 --- /dev/null +++ b/tests/ui/reborrow/reborrow-promotion-rejected.stderr @@ -0,0 +1,13 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/reborrow-promotion-rejected.rs:18:47 + | +LL | static BAD: &'static MyRef<'static> = &coerce(MyMut(&1)); + | --------^^^^^^^^^- + | | | | + | | | temporary value is freed at the end of this statement + | | creates a temporary value which is freed while still in use + | using this value as a static requires that borrow lasts for `'static` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0716`.