Skip to content

Commit f5a544e

Browse files
committed
refactor fn coerce_borrowed_ptr
1 parent a889c93 commit f5a544e

File tree

1 file changed

+58
-139
lines changed

1 file changed

+58
-139
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 58 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}
233-
ty::Ref(r_b, _, mutbl_b) => {
234-
return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
232+
ty::Ref(_, _, mutbl_b) => {
233+
return self.coerce_to_ref(a, b, mutbl_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
}
@@ -310,26 +309,23 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
310309
}
311310
}
312311

313-
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
314-
/// To match `A` with `B`, autoderef will be performed,
315-
/// calling `deref`/`deref_mut` where necessary.
316-
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(
317320
&self,
318321
a: Ty<'tcx>,
319322
b: Ty<'tcx>,
320-
r_b: ty::Region<'tcx>,
321323
mutbl_b: hir::Mutability,
322324
) -> CoerceResult<'tcx> {
323-
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
325+
debug!("coerce_to_ref(a={:?}, b={:?})", a, b);
324326
debug_assert!(self.shallow_resolve(a) == a);
325327
debug_assert!(self.shallow_resolve(b) == b);
326328

327-
// If we have a parameter of type `&M T_a` and the value
328-
// provided is `expr`, we will be adding an implicit borrow,
329-
// meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore,
330-
// to type check, we will construct the type that `&M*expr` would
331-
// yield.
332-
333329
let (r_a, mt_a) = match *a.kind() {
334330
ty::Ref(r_a, ty, mutbl) => {
335331
let mt_a = ty::TypeAndMut { ty, mutbl };
@@ -339,130 +335,53 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
339335
_ => return self.unify(a, b),
340336
};
341337

342-
let span = self.cause.span;
343-
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]`
344349
let mut first_error = None;
345-
let mut r_borrow_var = None;
346-
let mut autoderef = self.autoderef(span, a);
347-
let mut found = None;
348-
349-
for (referent_ty, autoderefs) in autoderef.by_ref() {
350+
let mut autoderef = self.autoderef(self.cause.span, a);
351+
let found = autoderef.by_ref().find_map(|(deref_ty, autoderefs)| {
350352
if autoderefs == 0 {
351-
// Don't let this pass, otherwise it would cause
352-
// &T to autoref to &&T.
353-
continue;
353+
// Don't autoref the first step as otherwise we'd allow
354+
// coercing `&T` to `&&T`.
355+
return None;
354356
}
355357

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

458377
// Extract type or return an error. We return the first error
459378
// we got, which should be from relating the "base" type
460379
// (e.g., in example above, the failure from relating `Vec<T>`
461380
// to the target type), since that should be the least
462381
// confusing.
463-
let Some(InferOk { value: ty, mut obligations }) = found else {
382+
let Some(InferOk { value: coerced_a, mut obligations }) = found else {
464383
if let Some(first_error) = first_error {
465-
debug!("coerce_borrowed_pointer: failed with err = {:?}", first_error);
384+
debug!("coerce_to_ref: failed with err = {:?}", first_error);
466385
return Err(first_error);
467386
} else {
468387
// This may happen in the new trait solver since autoderef requires
@@ -474,7 +393,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
474393
}
475394
};
476395

477-
if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
396+
if coerced_a == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
478397
// As a special case, if we would produce `&'a *x`, that's
479398
// a total no-op. We end up with the type `&'a T` just as
480399
// we started with. In that case, just skip it
@@ -487,7 +406,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
487406
// `self.x`, but we auto-coerce it to `foo(&mut *self.x)`,
488407
// which is a borrow.
489408
assert!(mutbl_b.is_not()); // can only coerce &T -> &U
490-
return success(vec![], ty, obligations);
409+
return success(vec![], coerced_a, obligations);
491410
}
492411

493412
let InferOk { value: mut adjustments, obligations: o } =
@@ -497,15 +416,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
497416

498417
// Now apply the autoref. We have to extract the region out of
499418
// the final ref type we got.
500-
let ty::Ref(..) = ty.kind() else {
501-
span_bug!(span, "expected a ref type, got {:?}", ty);
419+
let ty::Ref(..) = coerced_a.kind() else {
420+
span_bug!(self.cause.span, "expected a ref type, got {:?}", coerced_a);
502421
};
503422
let mutbl = AutoBorrowMutability::new(mutbl_b, self.allow_two_phase);
504-
adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: ty });
423+
adjustments
424+
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: coerced_a });
505425

506-
debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments);
426+
debug!("coerce_to_ref: succeeded coerced_a={:?} adjustments={:?}", coerced_a, adjustments);
507427

508-
success(adjustments, ty, obligations)
428+
success(adjustments, coerced_a, obligations)
509429
}
510430

511431
/// Performs [unsized coercion] by emulating a fulfillment loop on a
@@ -568,9 +488,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
568488
| ty::Tuple(_) => return Err(TypeError::Mismatch),
569489
_ => {}
570490
}
571-
// Additionally, we ignore `&str -> &str` coercions, which happen very
572-
// commonly since strings are one of the most used argument types in Rust,
573-
// we do coercions when type checking call expressions.
491+
// `&str: CoerceUnsized<&str>` does not hold but is encountered frequently
492+
// so we fast path bail out here
574493
if let ty::Ref(_, source_pointee, ty::Mutability::Not) = *source.kind()
575494
&& source_pointee.is_str()
576495
&& let ty::Ref(_, target_pointee, ty::Mutability::Not) = *target.kind()
@@ -809,7 +728,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
809728
/// - `Pin<Box<T>>` as `Pin<&T>`
810729
/// - `Pin<Box<T>>` as `Pin<&mut T>`
811730
#[instrument(skip(self), level = "trace")]
812-
fn coerce_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
731+
fn coerce_to_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
813732
debug_assert!(self.shallow_resolve(a) == a);
814733
debug_assert!(self.shallow_resolve(b) == b);
815734

@@ -1012,13 +931,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
1012931
}
1013932
}
1014933

1015-
fn coerce_raw_ptr(
934+
fn coerce_to_raw_ptr(
1016935
&self,
1017936
a: Ty<'tcx>,
1018937
b: Ty<'tcx>,
1019938
mutbl_b: hir::Mutability,
1020939
) -> CoerceResult<'tcx> {
1021-
debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b);
940+
debug!("coerce_to_raw_ptr(a={:?}, b={:?})", a, b);
1022941
debug_assert!(self.shallow_resolve(a) == a);
1023942
debug_assert!(self.shallow_resolve(b) == b);
1024943

0 commit comments

Comments
 (0)