Skip to content

Commit 8e8794f

Browse files
committed
non-behaviour changing cleanups
1 parent 642c19b commit 8e8794f

File tree

3 files changed

+140
-150
lines changed

3 files changed

+140
-150
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 91 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
179179
})
180180
}
181181

182-
#[instrument(skip(self))]
182+
#[instrument(skip(self), ret)]
183183
fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
184184
// First, remove any resolved type variables (at the top level, at least):
185185
let a = self.shallow_resolve(a);
@@ -223,21 +223,20 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
223223
}
224224
}
225225

226-
// Examine the supertype and consider type-specific coercions, such
227-
// as auto-borrowing, coercing pointer mutability, a `dyn*` coercion,
228-
// or pin-ergonomics.
226+
// Examine the target type and consider type-specific coercions, such
227+
// as auto-borrowing, coercing pointer mutability, or pin-ergonomics.
229228
match *b.kind() {
230229
ty::RawPtr(_, b_mutbl) => {
231-
return self.coerce_raw_ptr(a, b, b_mutbl);
230+
return self.coerce_to_raw_ptr(a, b, b_mutbl);
232231
}
233232
ty::Ref(r_b, _, mutbl_b) => {
234-
return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
233+
return self.coerce_to_ref(a, b, mutbl_b, r_b);
235234
}
236235
ty::Adt(pin, _)
237236
if self.tcx.features().pin_ergonomics()
238237
&& self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) =>
239238
{
240-
let pin_coerce = self.commit_if_ok(|_| self.coerce_pin_ref(a, b));
239+
let pin_coerce = self.commit_if_ok(|_| self.coerce_to_pin_ref(a, b));
241240
if pin_coerce.is_ok() {
242241
return pin_coerce;
243242
}
@@ -281,22 +280,21 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
281280
debug_assert!(self.shallow_resolve(b) == b);
282281

283282
if b.is_ty_var() {
284-
// Two unresolved type variables: create a `Coerce` predicate.
285-
let target_ty = if self.use_lub { self.next_ty_var(self.cause.span) } else { b };
286-
287283
let mut obligations = PredicateObligations::with_capacity(2);
288-
for &source_ty in &[a, b] {
289-
if source_ty != target_ty {
290-
obligations.push(Obligation::new(
291-
self.tcx(),
292-
self.cause.clone(),
293-
self.param_env,
294-
ty::Binder::dummy(ty::PredicateKind::Coerce(ty::CoercePredicate {
295-
a: source_ty,
296-
b: target_ty,
297-
})),
298-
));
299-
}
284+
let mut push_coerce_obligation = |a, b| {
285+
obligations.push(Obligation::new(
286+
self.tcx(),
287+
self.cause.clone(),
288+
self.param_env,
289+
ty::Binder::dummy(ty::PredicateKind::Coerce(ty::CoercePredicate { a, b })),
290+
));
291+
};
292+
293+
let target_ty = self.use_lub.then(|| self.next_ty_var(self.cause.span)).unwrap_or(b);
294+
295+
push_coerce_obligation(a, target_ty);
296+
if self.use_lub {
297+
push_coerce_obligation(b, target_ty);
300298
}
301299

302300
debug!(
@@ -311,159 +309,99 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
311309
}
312310
}
313311

314-
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
315-
/// To match `A` with `B`, autoderef will be performed,
316-
/// calling `deref`/`deref_mut` where necessary.
317-
fn coerce_borrowed_pointer(
312+
/// Handles coercing some arbitrary type `a` to some reference (`b`). This
313+
/// handles a few cases:
314+
/// - Introducing reborrows to give more flexible lifetimes
315+
/// - Deref coercions to allow `&T` to coerce to `&T::Target`
316+
/// - Coercing mutable references to immutable references
317+
/// These coercions can be freely intermixed, for example we are able to
318+
/// coerce `&mut T` to `&mut T::Target`.
319+
fn coerce_to_ref(
318320
&self,
319321
a: Ty<'tcx>,
320322
b: Ty<'tcx>,
321-
r_b: ty::Region<'tcx>,
322323
mutbl_b: hir::Mutability,
324+
r_b: ty::Region<'tcx>,
323325
) -> CoerceResult<'tcx> {
324-
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
326+
debug!("coerce_to_ref(a={:?}, b={:?})", a, b);
325327
debug_assert!(self.shallow_resolve(a) == a);
326328
debug_assert!(self.shallow_resolve(b) == b);
327329

328-
// If we have a parameter of type `&M T_a` and the value
329-
// provided is `expr`, we will be adding an implicit borrow,
330-
// meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore,
331-
// to type check, we will construct the type that `&M*expr` would
332-
// yield.
333-
334330
let (r_a, mt_a) = match *a.kind() {
335331
ty::Ref(r_a, ty, mutbl) => {
336-
let mt_a = ty::TypeAndMut { ty, mutbl };
337-
coerce_mutbls(mt_a.mutbl, mutbl_b)?;
338-
(r_a, mt_a)
332+
coerce_mutbls(mutbl, mutbl_b)?;
333+
(r_a, ty::TypeAndMut { ty, mutbl })
339334
}
340335
_ => return self.unify(a, b),
341336
};
342337

343-
let span = self.cause.span;
344-
338+
// Look at each step in the `Deref` chain and check if
339+
// any of the autoref'd `Target` types unify with the
340+
// coercion target.
341+
//
342+
// For example when coercing from `&mut Vec<T>` to `&M [T]` we
343+
// have three deref steps:
344+
// 1. `&mut Vec<T>`, skip autoref
345+
// 2. `Vec<T>`, autoref'd ty: `&M Vec<T>`
346+
// - `&M Vec<T>` does not unify with `&M [T]`
347+
// 3. `[T]`, autoref'd ty: `&M [T]`
348+
// - `&M [T]` does unify with `&M [T]`
345349
let mut first_error = None;
346350
let mut r_borrow_var = None;
347-
let mut autoderef = self.autoderef(span, a);
348-
let mut found = None;
349-
350-
for (referent_ty, autoderefs) in autoderef.by_ref() {
351+
let mut autoderef = self.autoderef(self.cause.span, a);
352+
let found = autoderef.by_ref().find_map(|(deref_ty, autoderefs)| {
351353
if autoderefs == 0 {
352-
// Don't let this pass, otherwise it would cause
353-
// &T to autoref to &&T.
354-
continue;
354+
// Don't autoref the first step as otherwise we'd allow
355+
// coercing `&T` to `&&T`.
356+
return None;
355357
}
356358

357-
// At this point, we have deref'd `a` to `referent_ty`. So
358-
// imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`.
359-
// In the autoderef loop for `&'a mut Vec<T>`, we would get
360-
// three callbacks:
359+
// The logic here really shouldn't exist. We don't care about free
360+
// lifetimes during HIR typeck. Unfortunately later parts of this
361+
// function rely on structural identity of the autoref'd deref'd ty.
361362
//
362-
// - `&'a mut Vec<T>` -- 0 derefs, just ignore it
363-
// - `Vec<T>` -- 1 deref
364-
// - `[T]` -- 2 deref
365-
//
366-
// At each point after the first callback, we want to
367-
// check to see whether this would match out target type
368-
// (`&'b mut [T]`) if we autoref'd it. We can't just
369-
// compare the referent types, though, because we still
370-
// have to consider the mutability. E.g., in the case
371-
// we've been considering, we have an `&mut` reference, so
372-
// the `T` in `[T]` needs to be unified with equality.
373-
//
374-
// Therefore, we construct reference types reflecting what
375-
// the types will be after we do the final auto-ref and
376-
// compare those. Note that this means we use the target
377-
// mutability [1], since it may be that we are coercing
378-
// from `&mut T` to `&U`.
379-
//
380-
// One fine point concerns the region that we use. We
381-
// choose the region such that the region of the final
382-
// type that results from `unify` will be the region we
383-
// want for the autoref:
384-
//
385-
// - if in sub mode, that means we want to use `'b` (the
386-
// region from the target reference) for both
387-
// pointers [2]. This is because sub mode (somewhat
388-
// arbitrarily) returns the subtype region. In the case
389-
// where we are coercing to a target type, we know we
390-
// want to use that target type region (`'b`) because --
391-
// for the program to type-check -- it must be the
392-
// smaller of the two.
393-
// - One fine point. It may be surprising that we can
394-
// use `'b` without relating `'a` and `'b`. The reason
395-
// that this is ok is that what we produce is
396-
// effectively a `&'b *x` expression (if you could
397-
// annotate the region of a borrow), and regionck has
398-
// code that adds edges from the region of a borrow
399-
// (`'b`, here) into the regions in the borrowed
400-
// expression (`*x`, here). (Search for "link".)
401-
// - if in lub mode, things can get fairly complicated. The
402-
// easiest thing is just to make a fresh
403-
// region variable [4], which effectively means we defer
404-
// the decision to region inference (and regionck, which will add
405-
// some more edges to this variable). However, this can wind up
406-
// creating a crippling number of variables in some cases --
407-
// e.g., #32278 -- so we optimize one particular case [3].
408-
// Let me try to explain with some examples:
409-
// - The "running example" above represents the simple case,
410-
// where we have one `&` reference at the outer level and
411-
// ownership all the rest of the way down. In this case,
412-
// we want `LUB('a, 'b)` as the resulting region.
413-
// - However, if there are nested borrows, that region is
414-
// too strong. Consider a coercion from `&'a &'x Rc<T>` to
415-
// `&'b T`. In this case, `'a` is actually irrelevant.
416-
// The pointer we want is `LUB('x, 'b`). If we choose `LUB('a,'b)`
417-
// we get spurious errors (`ui/regions-lub-ref-ref-rc.rs`).
418-
// (The errors actually show up in borrowck, typically, because
419-
// this extra edge causes the region `'a` to be inferred to something
420-
// too big, which then results in borrowck errors.)
421-
// - We could track the innermost shared reference, but there is already
422-
// code in regionck that has the job of creating links between
423-
// the region of a borrow and the regions in the thing being
424-
// borrowed (here, `'a` and `'x`), and it knows how to handle
425-
// all the various cases. So instead we just make a region variable
426-
// and let regionck figure it out.
363+
// This means that what region we use here actually impacts whether
364+
// we emit a reborrow coercion or not which can affect diagnostics
365+
// and capture analysis (which in turn affects borrowck).
427366
let r = if !self.use_lub {
428-
r_b // [2] above
367+
r_b
429368
} else if autoderefs == 1 {
430-
r_a // [3] above
369+
r_a
431370
} else {
432371
if r_borrow_var.is_none() {
433372
// create var lazily, at most once
434-
let coercion = RegionVariableOrigin::Coercion(span);
373+
let coercion = RegionVariableOrigin::Coercion(self.cause.span);
435374
let r = self.next_region_var(coercion);
436-
r_borrow_var = Some(r); // [4] above
375+
r_borrow_var = Some(r);
437376
}
438377
r_borrow_var.unwrap()
439378
};
440-
let derefd_ty_a = Ty::new_ref(
441-
self.tcx,
442-
r,
443-
referent_ty,
444-
mutbl_b, // [1] above
445-
);
446-
match self.unify_raw(derefd_ty_a, b) {
447-
Ok(ok) => {
448-
found = Some(ok);
449-
break;
450-
}
379+
380+
let autorefd_deref_ty = Ty::new_ref(self.tcx, r, deref_ty, mutbl_b);
381+
382+
// Note that we unify the autoref'd `Target` type with `b` rather than
383+
// the `Target` type with the pointee of `b`. This is necessary
384+
// to properly account for the differing variances of the pointees
385+
// of `&` vs `&mut` references.
386+
match self.unify_raw(autorefd_deref_ty, b) {
387+
Ok(ok) => Some(ok),
451388
Err(err) => {
452389
if first_error.is_none() {
453390
first_error = Some(err);
454391
}
392+
None
455393
}
456394
}
457-
}
395+
});
458396

459397
// Extract type or return an error. We return the first error
460398
// we got, which should be from relating the "base" type
461399
// (e.g., in example above, the failure from relating `Vec<T>`
462400
// to the target type), since that should be the least
463401
// confusing.
464-
let Some(InferOk { value: ty, mut obligations }) = found else {
402+
let Some(InferOk { value: coerced_a, mut obligations }) = found else {
465403
if let Some(first_error) = first_error {
466-
debug!("coerce_borrowed_pointer: failed with err = {:?}", first_error);
404+
debug!("coerce_to_ref: failed with err = {:?}", first_error);
467405
return Err(first_error);
468406
} else {
469407
// This may happen in the new trait solver since autoderef requires
@@ -475,11 +413,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
475413
}
476414
};
477415

478-
if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
416+
if coerced_a == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
479417
// As a special case, if we would produce `&'a *x`, that's
480418
// a total no-op. We end up with the type `&'a T` just as
481-
// we started with. In that case, just skip it
482-
// altogether. This is just an optimization.
419+
// we started with. In that case, just skip it altogether.
420+
//
421+
// Unfortunately, this can actually effect capture analysis
422+
// which in turn means this effects borrow checking. This can
423+
// also effect diagnostics.
424+
// FIXME(BoxyUwU): we should always emit reborrow coercions
483425
//
484426
// Note that for `&mut`, we DO want to reborrow --
485427
// otherwise, this would be a move, which might be an
@@ -488,25 +430,25 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
488430
// `self.x`, but we auto-coerce it to `foo(&mut *self.x)`,
489431
// which is a borrow.
490432
assert!(mutbl_b.is_not()); // can only coerce &T -> &U
491-
return success(vec![], ty, obligations);
433+
return success(vec![], coerced_a, obligations);
492434
}
493435

494436
let InferOk { value: mut adjustments, obligations: o } =
495437
self.adjust_steps_as_infer_ok(&autoderef);
496438
obligations.extend(o);
497439
obligations.extend(autoderef.into_obligations());
498440

499-
// Now apply the autoref. We have to extract the region out of
500-
// the final ref type we got.
501-
let ty::Ref(..) = ty.kind() else {
502-
span_bug!(span, "expected a ref type, got {:?}", ty);
441+
// Now apply the autoref
442+
let ty::Ref(..) = coerced_a.kind() else {
443+
span_bug!(self.cause.span, "expected a ref type, got {:?}", coerced_a);
503444
};
504445
let mutbl = AutoBorrowMutability::new(mutbl_b, self.allow_two_phase);
505-
adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: ty });
446+
adjustments
447+
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: coerced_a });
506448

507-
debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments);
449+
debug!("coerce_to_ref: succeeded coerced_a={:?} adjustments={:?}", coerced_a, adjustments);
508450

509-
success(adjustments, ty, obligations)
451+
success(adjustments, coerced_a, obligations)
510452
}
511453

512454
/// Performs [unsized coercion] by emulating a fulfillment loop on a
@@ -569,9 +511,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
569511
| ty::Tuple(_) => return Err(TypeError::Mismatch),
570512
_ => {}
571513
}
572-
// Additionally, we ignore `&str -> &str` coercions, which happen very
573-
// commonly since strings are one of the most used argument types in Rust,
574-
// we do coercions when type checking call expressions.
514+
// `&str: CoerceUnsized<&str>` does not hold but is encountered frequently
515+
// so we fast path bail out here
575516
if let ty::Ref(_, source_pointee, ty::Mutability::Not) = *source.kind()
576517
&& source_pointee.is_str()
577518
&& let ty::Ref(_, target_pointee, ty::Mutability::Not) = *target.kind()
@@ -810,7 +751,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
810751
/// - `Pin<Box<T>>` as `Pin<&T>`
811752
/// - `Pin<Box<T>>` as `Pin<&mut T>`
812753
#[instrument(skip(self), level = "trace")]
813-
fn coerce_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
754+
fn coerce_to_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
814755
debug_assert!(self.shallow_resolve(a) == a);
815756
debug_assert!(self.shallow_resolve(b) == b);
816757

@@ -1013,13 +954,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
1013954
}
1014955
}
1015956

1016-
fn coerce_raw_ptr(
957+
fn coerce_to_raw_ptr(
1017958
&self,
1018959
a: Ty<'tcx>,
1019960
b: Ty<'tcx>,
1020961
mutbl_b: hir::Mutability,
1021962
) -> CoerceResult<'tcx> {
1022-
debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b);
963+
debug!("coerce_to_raw_ptr(a={:?}, b={:?})", a, b);
1023964
debug_assert!(self.shallow_resolve(a) == a);
1024965
debug_assert!(self.shallow_resolve(b) == b);
1025966

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() {}

0 commit comments

Comments
 (0)