Skip to content

Commit 3e15f82

Browse files
Rollup merge of #156955 - P8L1:fix-reborrow-promotion-consteval, r=RalfJung
Fix const-eval of shared generic reborrows `Rvalue::Reborrow` const-eval now handles shared generic reborrows produced by `CoerceShared`. Those reborrows intentionally copy from the source ADT into a distinct same-layout target ADT, so the interpreter uses the transmute-capable copy path for `Mutability::Not`. Promotion is intentionally kept conservative: promotion candidates whose validated temporary graph contains `Rvalue::Reborrow` are rejected, so generic/user-defined reborrows are not promoted. Fixes #156313. cc @aapoalas Tracking: #145612 @rustbot label F-reborrow
2 parents 66ce7b0 + 56f5c38 commit 3e15f82

6 files changed

Lines changed: 120 additions & 8 deletions

File tree

compiler/rustc_const_eval/src/interpret/step.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
230230
})?;
231231
}
232232

233-
Reborrow(_, _, place) => {
234-
let op = self.eval_place_to_op(place, Some(dest.layout))?;
235-
self.copy_op(&op, &dest)?;
233+
Reborrow(_, mutability, place) => {
234+
let op = self.eval_place_to_op(place, None)?;
235+
if mutability.is_not() {
236+
// Shared generic reborrows use `CoerceShared`: a bitwise copy into a
237+
// distinct same-layout target ADT.
238+
self.copy_op_allow_transmute(&op, &dest)?;
239+
} else {
240+
self.copy_op(&op, &dest)?;
241+
}
236242
}
237243

238244
RawPtr(kind, place) => {

compiler/rustc_mir_transform/src/promote_consts.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -580,11 +580,7 @@ impl<'tcx> Validator<'_, 'tcx> {
580580
self.validate_ref(*kind, place)?;
581581
}
582582

583-
Rvalue::Reborrow(_, _, place) => {
584-
// FIXME(reborrow): should probably have a place_simplified like above.
585-
let op = &Operand::Copy(*place);
586-
self.validate_operand(op)?
587-
}
583+
Rvalue::Reborrow(..) => return Err(Unpromotable),
588584

589585
Rvalue::Aggregate(_, operands) => {
590586
for o in operands {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Regression test for the Miri reproducer in rust-lang/rust#156313.
2+
//
3+
// The issue's exact recursive example ICEd while evaluating the argument
4+
// conversion before the recursion mattered. This keeps that same
5+
// `CustomMut`-to-`CustomRef` call path, but terminates after one recursive
6+
// call so it can be a pass test once the ICE is fixed.
7+
8+
#![feature(reborrow)]
9+
10+
use std::marker::{CoerceShared, Reborrow};
11+
12+
#[allow(unused)]
13+
struct CustomMut<'a, T>(&'a mut T);
14+
impl<'a, T> Reborrow for CustomMut<'a, T> {}
15+
impl<'a, T> CoerceShared<CustomRef<'a, T>> for CustomMut<'a, T> {}
16+
17+
struct CustomRef<'a, T>(&'a T);
18+
19+
impl<'a, T> Clone for CustomRef<'a, T> {
20+
fn clone(&self) -> Self {
21+
Self(self.0)
22+
}
23+
}
24+
impl<'a, T> Copy for CustomRef<'a, T> {}
25+
26+
fn method(_a: CustomRef<'_, ()>) {}
27+
28+
fn recursive_method(_a: CustomRef<'_, ()>, recurse: bool) {
29+
if recurse {
30+
let a = CustomMut(&mut ());
31+
recursive_method(a, false);
32+
}
33+
}
34+
35+
fn issue_156313_runtime_reproducer() {
36+
let a = CustomMut(&mut ());
37+
method(a);
38+
}
39+
40+
fn issue_156313_recursive_call_reproducer() {
41+
let a = CustomMut(&mut ());
42+
recursive_method(a, true);
43+
}
44+
45+
fn main() {
46+
issue_156313_runtime_reproducer();
47+
issue_156313_recursive_call_reproducer();
48+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//@ run-pass
2+
// Regression test for the const-eval reproducer in rust-lang/rust#156313.
3+
4+
#![feature(reborrow)]
5+
#![allow(dead_code)]
6+
#![allow(unused_variables)]
7+
8+
use std::marker::{CoerceShared, Reborrow};
9+
10+
pub struct MyMut<'a>(&'a u8);
11+
12+
impl Reborrow for MyMut<'_> {}
13+
14+
#[derive(Clone, Copy)]
15+
pub struct MyRef<'a>(&'a u8);
16+
17+
impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {}
18+
19+
const fn consteval_reproducer() {
20+
let value = 1;
21+
foo(MyMut(&value));
22+
}
23+
24+
const fn foo(x: MyRef<'_>) {}
25+
26+
fn main() {
27+
const { consteval_reproducer(); }
28+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ check-fail
2+
3+
#![feature(reborrow)]
4+
5+
use std::marker::{CoerceShared, Reborrow};
6+
7+
struct MyMut<'a>(&'a u8);
8+
impl Reborrow for MyMut<'_> {}
9+
10+
#[derive(Clone, Copy)]
11+
struct MyRef<'a>(&'a u8);
12+
impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {}
13+
14+
const fn coerce(x: MyRef<'_>) -> MyRef<'_> {
15+
x
16+
}
17+
18+
static BAD: &'static MyRef<'static> = &coerce(MyMut(&1));
19+
//~^ ERROR temporary value dropped while borrowed
20+
21+
fn main() {}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0716]: temporary value dropped while borrowed
2+
--> $DIR/reborrow-promotion-rejected.rs:18:47
3+
|
4+
LL | static BAD: &'static MyRef<'static> = &coerce(MyMut(&1));
5+
| --------^^^^^^^^^-
6+
| | | |
7+
| | | temporary value is freed at the end of this statement
8+
| | creates a temporary value which is freed while still in use
9+
| using this value as a static requires that borrow lasts for `'static`
10+
11+
error: aborting due to 1 previous error
12+
13+
For more information about this error, try `rustc --explain E0716`.

0 commit comments

Comments
 (0)