Skip to content

Commit ce8cfd2

Browse files
committed
refactor fn coerce_borrowed_ptr
1 parent 11a6127 commit ce8cfd2

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
@@ -221,21 +221,20 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
221221
}
222222
}
223223

224-
// Examine the supertype and consider type-specific coercions, such
225-
// as auto-borrowing, coercing pointer mutability, a `dyn*` coercion,
226-
// or pin-ergonomics.
224+
// Examine the target type and consider type-specific coercions, such
225+
// as auto-borrowing, coercing pointer mutability, or pin-ergonomics.
227226
match *b.kind() {
228227
ty::RawPtr(_, b_mutbl) => {
229-
return self.coerce_raw_ptr(a, b, b_mutbl);
228+
return self.coerce_to_raw_ptr(a, b, b_mutbl);
230229
}
231-
ty::Ref(r_b, _, mutbl_b) => {
232-
return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
230+
ty::Ref(_, _, mutbl_b) => {
231+
return self.coerce_to_ref(a, b, mutbl_b);
233232
}
234233
ty::Adt(pin, _)
235234
if self.tcx.features().pin_ergonomics()
236235
&& self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) =>
237236
{
238-
let pin_coerce = self.commit_if_ok(|_| self.coerce_pin_ref(a, b));
237+
let pin_coerce = self.commit_if_ok(|_| self.coerce_to_pin_ref(a, b));
239238
if pin_coerce.is_ok() {
240239
return pin_coerce;
241240
}
@@ -308,26 +307,23 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
308307
}
309308
}
310309

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

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

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

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

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

475-
if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
394+
if coerced_a == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
476395
// As a special case, if we would produce `&'a *x`, that's
477396
// a total no-op. We end up with the type `&'a T` just as
478397
// we started with. In that case, just skip it
@@ -485,7 +404,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
485404
// `self.x`, but we auto-coerce it to `foo(&mut *self.x)`,
486405
// which is a borrow.
487406
assert!(mutbl_b.is_not()); // can only coerce &T -> &U
488-
return success(vec![], ty, obligations);
407+
return success(vec![], coerced_a, obligations);
489408
}
490409

491410
let InferOk { value: mut adjustments, obligations: o } =
@@ -495,15 +414,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
495414

496415
// Now apply the autoref. We have to extract the region out of
497416
// the final ref type we got.
498-
let ty::Ref(..) = ty.kind() else {
499-
span_bug!(span, "expected a ref type, got {:?}", ty);
417+
let ty::Ref(..) = coerced_a.kind() else {
418+
span_bug!(self.cause.span, "expected a ref type, got {:?}", coerced_a);
500419
};
501420
let mutbl = AutoBorrowMutability::new(mutbl_b, self.allow_two_phase);
502-
adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: ty });
421+
adjustments
422+
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: coerced_a });
503423

504-
debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments);
424+
debug!("coerce_to_ref: succeeded coerced_a={:?} adjustments={:?}", coerced_a, adjustments);
505425

506-
success(adjustments, ty, obligations)
426+
success(adjustments, coerced_a, obligations)
507427
}
508428

509429
/// Performs [unsized coercion] by emulating a fulfillment loop on a
@@ -566,9 +486,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
566486
| ty::Tuple(_) => return Err(TypeError::Mismatch),
567487
_ => {}
568488
}
569-
// Additionally, we ignore `&str -> &str` coercions, which happen very
570-
// commonly since strings are one of the most used argument types in Rust,
571-
// we do coercions when type checking call expressions.
489+
// `&str: CoerceUnsized<&str>` does not hold but is encountered frequently
490+
// so we fast path bail out here
572491
if let ty::Ref(_, source_pointee, ty::Mutability::Not) = *source.kind()
573492
&& source_pointee.is_str()
574493
&& let ty::Ref(_, target_pointee, ty::Mutability::Not) = *target.kind()
@@ -780,7 +699,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
780699
/// - `Pin<Box<T>>` as `Pin<&T>`
781700
/// - `Pin<Box<T>>` as `Pin<&mut T>`
782701
#[instrument(skip(self), level = "trace")]
783-
fn coerce_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
702+
fn coerce_to_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
784703
debug_assert!(self.shallow_resolve(a) == a);
785704
debug_assert!(self.shallow_resolve(b) == b);
786705

@@ -983,13 +902,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
983902
}
984903
}
985904

986-
fn coerce_raw_ptr(
905+
fn coerce_to_raw_ptr(
987906
&self,
988907
a: Ty<'tcx>,
989908
b: Ty<'tcx>,
990909
mutbl_b: hir::Mutability,
991910
) -> CoerceResult<'tcx> {
992-
debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b);
911+
debug!("coerce_to_raw_ptr(a={:?}, b={:?})", a, b);
993912
debug_assert!(self.shallow_resolve(a) == a);
994913
debug_assert!(self.shallow_resolve(b) == b);
995914

0 commit comments

Comments
 (0)