Skip to content

Commit 7be286e

Browse files
committed
Retain the types differ in mutability diagnostics
1 parent 3f75db3 commit 7be286e

2 files changed

Lines changed: 74 additions & 44 deletions

File tree

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 70 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,26 @@ fn success<'tcx>(
114114
Ok(InferOk { value: (adj, target), obligations })
115115
}
116116

117+
/// Data extracted from a reference (pinned or not) for coercion to a reference (pinned or not).
118+
struct CoerceMaybePinnedRef<'tcx> {
119+
/// coercion source, must be a pinned (i.e. `Pin<&T>` or `Pin<&mut T>`) or normal reference (`&T` or `&mut T`)
120+
a: Ty<'tcx>,
121+
/// coercion target, must be a pinned (i.e. `Pin<&T>` or `Pin<&mut T>`) or normal reference (`&T` or `&mut T`)
122+
b: Ty<'tcx>,
123+
/// referent type of the source
124+
a_ty: Ty<'tcx>,
125+
/// pinnedness of the source
126+
a_pin: ty::Pinnedness,
127+
/// mutability of the source
128+
a_mut: ty::Mutability,
129+
/// region of the source
130+
a_r: ty::Region<'tcx>,
131+
/// pinnedness of the target
132+
b_pin: ty::Pinnedness,
133+
/// mutability of the target
134+
b_mut: ty::Mutability,
135+
}
136+
117137
/// Whether to force a leak check to occur in `Coerce::unify_raw`.
118138
/// Note that leak checks may still occur evn with `ForceLeakCheck::No`.
119139
///
@@ -269,21 +289,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
269289
return self.coerce_to_raw_ptr(a, b, b_mutbl);
270290
}
271291
ty::Ref(r_b, _, mutbl_b) => {
272-
if self.tcx.features().pin_ergonomics()
273-
&& let Ok(pin_coerce) =
274-
self.commit_if_ok(|_| self.coerce_pin_ref_to_ref(a, b, mutbl_b))
275-
{
276-
return Ok(pin_coerce);
292+
if let Some(pin_ref_to_ref) = self.maybe_pin_ref_to_ref(a, b) {
293+
return self.coerce_pin_ref_to_ref(pin_ref_to_ref);
277294
}
278295
return self.coerce_to_ref(a, b, r_b, mutbl_b);
279296
}
280-
_ if self.tcx.features().pin_ergonomics()
281-
&& let Some((_, ty::Pinnedness::Pinned, mut_b, _)) = b.maybe_pinned_ref() =>
282-
{
283-
let pin_coerce = self.commit_if_ok(|_| self.coerce_to_pin_ref(a, b, mut_b));
284-
if pin_coerce.is_ok() {
285-
return pin_coerce;
286-
}
297+
_ if let Some(to_pin_ref) = self.maybe_to_pin_ref(a, b) => {
298+
return self.coerce_to_pin_ref(to_pin_ref);
287299
}
288300
_ => {}
289301
}
@@ -805,37 +817,62 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
805817
PredicateObligation::new(self.tcx, self.cause.clone(), self.param_env, pred)
806818
}
807819

820+
/// Checks if the given types are compatible for coercion to a pinned reference.
821+
fn maybe_pin_ref_to_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> Option<CoerceMaybePinnedRef<'tcx>> {
822+
if !self.tcx.features().pin_ergonomics() {
823+
return None;
824+
}
825+
if let Some((a_ty, a_pin @ ty::Pinnedness::Pinned, a_mut, a_r)) = a.maybe_pinned_ref()
826+
&& let Some((_, b_pin @ ty::Pinnedness::Not, b_mut, _)) = b.maybe_pinned_ref()
827+
{
828+
return Some(CoerceMaybePinnedRef { a, b, a_ty, a_pin, a_mut, a_r, b_pin, b_mut });
829+
}
830+
debug!("not fitting pinned ref to ref coercion (`{:?}` -> `{:?}`)", a, b);
831+
None
832+
}
833+
808834
/// Coerces from a pinned reference to a normal reference.
809835
#[instrument(skip(self), level = "trace")]
810836
fn coerce_pin_ref_to_ref(
811837
&self,
812-
a: Ty<'tcx>,
813-
b: Ty<'tcx>,
814-
mut_b: hir::Mutability,
838+
CoerceMaybePinnedRef { a, b, a_ty, a_pin, a_mut, a_r, b_pin, b_mut }: CoerceMaybePinnedRef<
839+
'tcx,
840+
>,
815841
) -> CoerceResult<'tcx> {
816842
debug_assert!(self.shallow_resolve(a) == a);
817843
debug_assert!(self.shallow_resolve(b) == b);
818844
debug_assert!(self.tcx.features().pin_ergonomics());
845+
debug_assert_eq!(a_pin, ty::Pinnedness::Pinned);
846+
debug_assert_eq!(b_pin, ty::Pinnedness::Not);
819847

820-
let Some((a_ty, ty::Pinnedness::Pinned, mut_a, r_a)) = a.maybe_pinned_ref() else {
821-
debug!("not fitting pinned ref to ref coercion (`{:?}` -> `{:?}`)", a, b);
822-
return Err(TypeError::Mismatch);
823-
};
824-
825-
coerce_mutbls(mut_a, mut_b)?;
848+
coerce_mutbls(a_mut, b_mut)?;
826849

827-
let a = Ty::new_ref(self.tcx, r_a, a_ty, mut_b);
850+
let a = Ty::new_ref(self.tcx, a_r, a_ty, b_mut);
828851
let mut coerce = self.unify_and(
829852
a,
830853
b,
831854
[Adjustment { kind: Adjust::Deref(DerefAdjustKind::Pin), target: a_ty }],
832-
Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::new(mut_b, self.allow_two_phase))),
855+
Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::new(b_mut, self.allow_two_phase))),
833856
ForceLeakCheck::No,
834857
)?;
835858
coerce.obligations.push(self.unpin_obligation(a_ty));
836859
Ok(coerce)
837860
}
838861

862+
/// Checks if the given types are compatible for coercion to a pinned reference.
863+
fn maybe_to_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> Option<CoerceMaybePinnedRef<'tcx>> {
864+
if !self.tcx.features().pin_ergonomics() {
865+
return None;
866+
}
867+
if let Some((a_ty, a_pin, a_mut, a_r)) = a.maybe_pinned_ref()
868+
&& let Some((_, b_pin @ ty::Pinnedness::Pinned, b_mut, _)) = b.maybe_pinned_ref()
869+
{
870+
return Some(CoerceMaybePinnedRef { a, b, a_ty, a_pin, a_mut, a_r, b_pin, b_mut });
871+
}
872+
debug!("not fitting ref to pinned ref coercion (`{:?}` -> `{:?}`)", a, b);
873+
None
874+
}
875+
839876
/// Applies reborrowing and auto-borrowing that results to `Pin<&T>` or `Pin<&mut T>`:
840877
///
841878
/// Currently we only support the following coercions:
@@ -851,42 +888,35 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
851888
#[instrument(skip(self), level = "trace")]
852889
fn coerce_to_pin_ref(
853890
&self,
854-
a: Ty<'tcx>,
855-
b: Ty<'tcx>,
856-
mut_b: hir::Mutability,
891+
CoerceMaybePinnedRef { a, b, a_ty, a_pin, a_mut, a_r, b_pin, b_mut }: CoerceMaybePinnedRef<
892+
'tcx,
893+
>,
857894
) -> CoerceResult<'tcx> {
858895
debug_assert!(self.shallow_resolve(a) == a);
859896
debug_assert!(self.shallow_resolve(b) == b);
860897
debug_assert!(self.tcx.features().pin_ergonomics());
898+
debug_assert_eq!(b_pin, ty::Pinnedness::Pinned);
861899

862900
// We need to deref the reference first before we reborrow it to a pinned reference.
863-
let (deref, a_ty, mut_a, r_a, unpin_obligation) = match a.maybe_pinned_ref() {
901+
let (deref, unpin_obligation) = match a_pin {
864902
// no `Unpin` required when reborrowing a pinned reference to a pinned reference
865-
Some((a_ty, ty::Pinnedness::Pinned, mut_a, r_a)) => {
866-
(DerefAdjustKind::Pin, a_ty, mut_a, r_a, None)
867-
}
903+
ty::Pinnedness::Pinned => (DerefAdjustKind::Pin, None),
868904
// `Unpin` required when reborrowing a non-pinned reference to a pinned reference
869-
Some((a_ty, ty::Pinnedness::Not, mut_a, r_a)) => {
870-
(DerefAdjustKind::Builtin, a_ty, mut_a, r_a, Some(self.unpin_obligation(a_ty)))
871-
}
872-
None => {
873-
debug!("can't reborrow {:?} as pinned", a);
874-
return Err(TypeError::Mismatch);
875-
}
905+
ty::Pinnedness::Not => (DerefAdjustKind::Builtin, Some(self.unpin_obligation(a_ty))),
876906
};
877907

878-
coerce_mutbls(mut_a, mut_b)?;
908+
coerce_mutbls(a_mut, b_mut)?;
879909

880910
// update a with b's mutability since we'll be coercing mutability
881-
let a = Ty::new_pinned_ref(self.tcx, r_a, a_ty, mut_b);
911+
let a = Ty::new_pinned_ref(self.tcx, a_r, a_ty, b_mut);
882912

883913
// To complete the reborrow, we need to make sure we can unify the inner types, and if so we
884914
// add the adjustments.
885915
let mut coerce = self.unify_and(
886916
a,
887917
b,
888918
[Adjustment { kind: Adjust::Deref(deref), target: a_ty }],
889-
Adjust::Borrow(AutoBorrow::Pin(mut_b)),
919+
Adjust::Borrow(AutoBorrow::Pin(b_mut)),
890920
ForceLeakCheck::No,
891921
)?;
892922

tests/ui/pin-ergonomics/pin-coercion-check.pin_ergonomics.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ error[E0308]: mismatched types
5656
--> $DIR/pin-coercion-check.rs:27:30
5757
|
5858
LL | |x: Pin<&T>| -> &mut T { x };
59-
| ------ ^ expected `&mut T`, found `Pin<&T>`
59+
| ------ ^ types differ in mutability
6060
| |
6161
| expected `&mut T` because of return type
6262
|
@@ -71,7 +71,7 @@ error[E0308]: mismatched types
7171
--> $DIR/pin-coercion-check.rs:37:30
7272
|
7373
LL | |x: Pin<&U>| -> &mut U { x };
74-
| ------ ^ expected `&mut U`, found `Pin<&U>`
74+
| ------ ^ types differ in mutability
7575
| |
7676
| expected `&mut U` because of return type
7777
|
@@ -125,7 +125,7 @@ error[E0308]: mismatched types
125125
--> $DIR/pin-coercion-check.rs:52:30
126126
|
127127
LL | |x: &T| -> Pin<&mut T> { x };
128-
| ----------- ^ expected `Pin<&mut T>`, found `&T`
128+
| ----------- ^ types differ in mutability
129129
| |
130130
| expected `Pin<&mut T>` because of return type
131131
|
@@ -136,7 +136,7 @@ error[E0308]: mismatched types
136136
--> $DIR/pin-coercion-check.rs:62:30
137137
|
138138
LL | |x: &U| -> Pin<&mut U> { x };
139-
| ----------- ^ expected `Pin<&mut U>`, found `&U`
139+
| ----------- ^ types differ in mutability
140140
| |
141141
| expected `Pin<&mut U>` because of return type
142142
|

0 commit comments

Comments
 (0)