Skip to content

Commit 1184da6

Browse files
committed
coerce_to_ref: no structural identity reliance
1 parent 63afe83 commit 1184da6

File tree

3 files changed

+84
-10
lines changed

3 files changed

+84
-10
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
251251
ty::RawPtr(_, b_mutbl) => {
252252
return self.coerce_to_raw_ptr(a, b, b_mutbl);
253253
}
254-
ty::Ref(_, _, mutbl_b) => {
255-
return self.coerce_to_ref(a, b, mutbl_b);
254+
ty::Ref(r_b, _, mutbl_b) => {
255+
return self.coerce_to_ref(a, b, mutbl_b, r_b);
256256
}
257257
ty::Adt(pin, _)
258258
if self.tcx.features().pin_ergonomics()
@@ -343,15 +343,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
343343
a: Ty<'tcx>,
344344
b: Ty<'tcx>,
345345
mutbl_b: hir::Mutability,
346+
r_b: ty::Region<'tcx>,
346347
) -> CoerceResult<'tcx> {
347348
debug!("coerce_to_ref(a={:?}, b={:?})", a, b);
348349
debug_assert!(self.shallow_resolve(a) == a);
349350
debug_assert!(self.shallow_resolve(b) == b);
350351

351-
let mt_a = match *a.kind() {
352-
ty::Ref(_, ty, mutbl) => {
352+
let (r_a, mt_a) = match *a.kind() {
353+
ty::Ref(r_a, ty, mutbl) => {
353354
coerce_mutbls(mutbl, mutbl_b)?;
354-
ty::TypeAndMut { ty, mutbl }
355+
(r_a, ty::TypeAndMut { ty, mutbl })
355356
}
356357
_ => return self.unify(a, b),
357358
};
@@ -368,6 +369,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
368369
// 3. `[T]`, autoref'd ty: `&M [T]`
369370
// - `&M [T]` does unify with `&M [T]`
370371
let mut first_error = None;
372+
let mut r_borrow_var = None;
371373
let mut autoderef = self.autoderef(self.cause.span, a);
372374
let found = autoderef.by_ref().find_map(|(deref_ty, autoderefs)| {
373375
if autoderefs == 0 {
@@ -376,9 +378,28 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
376378
return None;
377379
}
378380

379-
let coercion = RegionVariableOrigin::Coercion(self.cause.span);
380-
let r_borrow = self.next_region_var(coercion);
381-
let autorefd_deref_ty = Ty::new_ref(self.tcx, r_borrow, deref_ty, mutbl_b);
381+
// The logic here really shouldn't exist. We don't care about free
382+
// lifetimes during HIR typeck. Unfortunately later parts of this
383+
// function rely on structural identity of the autoref'd deref'd ty.
384+
//
385+
// This means that what region we use here actually impacts whether
386+
// we emit a reborrow coercion or not which can affect diagnostics
387+
// and capture analysis (which in turn affects borrowck).
388+
let r = if !self.use_lub {
389+
r_b
390+
} else if autoderefs == 1 {
391+
r_a
392+
} else {
393+
if r_borrow_var.is_none() {
394+
// create var lazily, at most once
395+
let coercion = RegionVariableOrigin::Coercion(self.cause.span);
396+
let r = self.next_region_var(coercion);
397+
r_borrow_var = Some(r);
398+
}
399+
r_borrow_var.unwrap()
400+
};
401+
402+
let autorefd_deref_ty = Ty::new_ref(self.tcx, r, deref_ty, mutbl_b);
382403

383404
// Note that we unify the autoref'd `Target` type with `b` rather than
384405
// the `Target` type with the pointee of `b`. This is necessary
@@ -417,8 +438,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
417438
if coerced_a == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
418439
// As a special case, if we would produce `&'a *x`, that's
419440
// a total no-op. We end up with the type `&'a T` just as
420-
// we started with. In that case, just skip it
421-
// altogether. This is just an optimization.
441+
// we started with. In that case, just skip it altogether.
442+
//
443+
// Unfortunately, this can actually effect capture analysis
444+
// which in turn means this effects borrow checking. This can
445+
// also effect diagnostics.
446+
// FIXME(BoxyUwU): we should always emit reborrow coercions
422447
//
423448
// Note that for `&mut`, we DO want to reborrow --
424449
// otherwise, this would be a move, which might be an
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ edition: 2024
2+
3+
// We avoid emitting reborrow coercions if it seems like it would
4+
// not result in a different lifetime on the borrow. This can effect
5+
// capture analysis resulting in borrow checking errors.
6+
7+
fn foo<'a>(b: &'a ()) -> impl Fn() {
8+
|| {
9+
expected::<&()>(b);
10+
}
11+
}
12+
13+
// No reborrow of `b` is emitted which means our closure captures
14+
// `b` by ref resulting in an upvar of `&&'a ()`
15+
fn bar<'a>(b: &'a ()) -> impl Fn() {
16+
|| {
17+
//~^ ERROR: closure may outlive the current function
18+
expected::<&'a ()>(b);
19+
}
20+
}
21+
22+
fn expected<T>(_: T) {}
23+
24+
fn main() {}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0373]: closure may outlive the current function, but it borrows `b`, which is owned by the current function
2+
--> $DIR/structural_identity_dependent_reborrows.rs:16:5
3+
|
4+
LL | || {
5+
| ^^ may outlive borrowed value `b`
6+
LL |
7+
LL | expected::<&'a ()>(b);
8+
| - `b` is borrowed here
9+
|
10+
note: closure is returned here
11+
--> $DIR/structural_identity_dependent_reborrows.rs:16:5
12+
|
13+
LL | / || {
14+
LL | |
15+
LL | | expected::<&'a ()>(b);
16+
LL | | }
17+
| |_____^
18+
help: to force the closure to take ownership of `b` (and any other referenced variables), use the `move` keyword
19+
|
20+
LL | move || {
21+
| ++++
22+
23+
error: aborting due to 1 previous error
24+
25+
For more information about this error, try `rustc --explain E0373`.

0 commit comments

Comments
 (0)