Skip to content

Commit 4db65a1

Browse files
authored
Rollup merge of #71540 - ldm0:ref2ptr, r=oli-obk
Suggest deref when coercing `ty::Ref` to `ty::RawPtr` Fixes #32122 Currently we do autoderef when casting `ty::Ref` ->`ty::Ref`, but we don't autoderef when casting `ty::Ref` -> `ty::RawPtr`. This PR make the compiler suggests deref when coercing `ty::Ref` to `ty::RawPtr`
2 parents 8bdd235 + a985879 commit 4db65a1

File tree

10 files changed

+166
-8
lines changed

10 files changed

+166
-8
lines changed

src/librustc_typeck/check/coercion.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
7474
use smallvec::{smallvec, SmallVec};
7575
use std::ops::Deref;
7676

77-
struct Coerce<'a, 'tcx> {
77+
pub struct Coerce<'a, 'tcx> {
7878
fcx: &'a FnCtxt<'a, 'tcx>,
7979
cause: ObligationCause<'tcx>,
8080
use_lub: bool,
@@ -124,15 +124,15 @@ fn success<'tcx>(
124124
}
125125

126126
impl<'f, 'tcx> Coerce<'f, 'tcx> {
127-
fn new(
127+
pub fn new(
128128
fcx: &'f FnCtxt<'f, 'tcx>,
129129
cause: ObligationCause<'tcx>,
130130
allow_two_phase: AllowTwoPhase,
131131
) -> Self {
132132
Coerce { fcx, cause, allow_two_phase, use_lub: false }
133133
}
134134

135-
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
135+
pub fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
136136
self.commit_if_ok(|_| {
137137
if self.use_lub {
138138
self.at(&self.cause, self.fcx.param_env).lub(b, a)
@@ -771,10 +771,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
771771
ty::RawPtr(mt) => (false, mt),
772772
_ => return self.unify_and(a, b, identity),
773773
};
774+
coerce_mutbls(mt_a.mutbl, mutbl_b)?;
774775

775776
// Check that the types which they point at are compatible.
776777
let a_unsafe = self.tcx.mk_ptr(ty::TypeAndMut { mutbl: mutbl_b, ty: mt_a.ty });
777-
coerce_mutbls(mt_a.mutbl, mutbl_b)?;
778778
// Although references and unsafe ptrs have the same
779779
// representation, we still register an Adjust::DerefRef so that
780780
// regionck knows that the region for `a` must be valid here.

src/librustc_typeck/check/demand.rs

+38-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::check::coercion::Coerce;
12
use crate::check::FnCtxt;
23
use rustc_infer::infer::InferOk;
34
use rustc_trait_selection::infer::InferCtxtExt as _;
@@ -8,8 +9,9 @@ use rustc_ast::util::parser::PREC_POSTFIX;
89
use rustc_errors::{Applicability, DiagnosticBuilder};
910
use rustc_hir as hir;
1011
use rustc_hir::{is_range_literal, Node};
12+
use rustc_middle::traits::ObligationCauseCode;
1113
use rustc_middle::ty::adjustment::AllowTwoPhase;
12-
use rustc_middle::ty::{self, AssocItem, Ty};
14+
use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
1315
use rustc_span::symbol::sym;
1416
use rustc_span::Span;
1517

@@ -25,7 +27,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2527
) {
2628
self.annotate_expected_due_to_let_ty(err, expr);
2729
self.suggest_compatible_variants(err, expr, expected, expr_ty);
28-
self.suggest_ref_or_into(err, expr, expected, expr_ty);
30+
self.suggest_deref_ref_or_into(err, expr, expected, expr_ty);
2931
if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
3032
return;
3133
}
@@ -539,6 +541,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
539541
return Some((sp, "consider removing the borrow", code));
540542
}
541543
}
544+
(
545+
_,
546+
&ty::RawPtr(TypeAndMut { ty: _, mutbl: hir::Mutability::Not }),
547+
&ty::Ref(_, _, hir::Mutability::Not),
548+
) => {
549+
let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable);
550+
// We don't ever need two-phase here since we throw out the result of the coercion
551+
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
552+
553+
if let Some(steps) =
554+
coerce.autoderef(sp, checked_ty).skip(1).find_map(|(referent_ty, steps)| {
555+
coerce
556+
.unify(
557+
coerce.tcx.mk_ptr(ty::TypeAndMut {
558+
mutbl: hir::Mutability::Not,
559+
ty: referent_ty,
560+
}),
561+
expected,
562+
)
563+
.ok()
564+
.map(|_| steps)
565+
})
566+
{
567+
// The pointer type implements `Copy` trait so the suggestion is always valid.
568+
if let Ok(code) = sm.span_to_snippet(sp) {
569+
if code.starts_with('&') {
570+
let derefs = "*".repeat(steps - 1);
571+
let message = "consider dereferencing the reference";
572+
let suggestion = format!("&{}{}", derefs, code[1..].to_string());
573+
return Some((sp, message, suggestion));
574+
}
575+
}
576+
}
577+
}
542578
_ if sp == expr.span && !is_macro => {
543579
// Check for `Deref` implementations by constructing a predicate to
544580
// prove: `<T as Deref>::Output == U`

src/librustc_typeck/check/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8686

8787
if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
8888
let expr = expr.peel_drop_temps();
89-
self.suggest_ref_or_into(&mut err, expr, expected_ty, ty);
89+
self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty);
9090
extend_err(&mut err);
9191
// Error possibly reported in `check_assign` so avoid emitting error again.
9292
err.emit_unless(self.is_assign_to_bool(expr, expected_ty));

src/librustc_typeck/check/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5029,7 +5029,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
50295029
false
50305030
}
50315031

5032-
pub fn suggest_ref_or_into(
5032+
pub fn suggest_deref_ref_or_into(
50335033
&self,
50345034
err: &mut DiagnosticBuilder<'_>,
50355035
expr: &hir::Expr<'_>,
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// run-rustfix
2+
use std::ops::Deref;
3+
4+
struct Foo(u8);
5+
6+
impl Deref for Foo {
7+
type Target = u8;
8+
fn deref(&self) -> &Self::Target {
9+
&self.0
10+
}
11+
}
12+
13+
fn main() {
14+
let a = Foo(0);
15+
// Should suggest `&*` when coercing &ty to *const ty
16+
let _: *const u8 = &*a; //~ ERROR mismatched types
17+
}

src/test/ui/issues/issue-32122-1.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// run-rustfix
2+
use std::ops::Deref;
3+
4+
struct Foo(u8);
5+
6+
impl Deref for Foo {
7+
type Target = u8;
8+
fn deref(&self) -> &Self::Target {
9+
&self.0
10+
}
11+
}
12+
13+
fn main() {
14+
let a = Foo(0);
15+
// Should suggest `&*` when coercing &ty to *const ty
16+
let _: *const u8 = &a; //~ ERROR mismatched types
17+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-32122-1.rs:16:24
3+
|
4+
LL | let _: *const u8 = &a;
5+
| --------- ^^
6+
| | |
7+
| | expected `u8`, found struct `Foo`
8+
| | help: consider dereferencing the reference: `&*a`
9+
| expected due to this
10+
|
11+
= note: expected raw pointer `*const u8`
12+
found reference `&Foo`
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0308`.
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// run-rustfix
2+
use std::ops::Deref;
3+
struct Bar(u8);
4+
struct Foo(Bar);
5+
struct Emm(Foo);
6+
impl Deref for Bar{
7+
type Target = u8;
8+
fn deref(&self) -> &Self::Target {
9+
&self.0
10+
}
11+
}
12+
impl Deref for Foo {
13+
type Target = Bar;
14+
fn deref(&self) -> &Self::Target {
15+
&self.0
16+
}
17+
}
18+
impl Deref for Emm {
19+
type Target = Foo;
20+
fn deref(&self) -> &Self::Target {
21+
&self.0
22+
}
23+
}
24+
fn main() {
25+
let a = Emm(Foo(Bar(0)));
26+
// Should suggest `&***` even when deref is pretty deep
27+
let _: *const u8 = &***a; //~ ERROR mismatched types
28+
}

src/test/ui/issues/issue-32122-2.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// run-rustfix
2+
use std::ops::Deref;
3+
struct Bar(u8);
4+
struct Foo(Bar);
5+
struct Emm(Foo);
6+
impl Deref for Bar{
7+
type Target = u8;
8+
fn deref(&self) -> &Self::Target {
9+
&self.0
10+
}
11+
}
12+
impl Deref for Foo {
13+
type Target = Bar;
14+
fn deref(&self) -> &Self::Target {
15+
&self.0
16+
}
17+
}
18+
impl Deref for Emm {
19+
type Target = Foo;
20+
fn deref(&self) -> &Self::Target {
21+
&self.0
22+
}
23+
}
24+
fn main() {
25+
let a = Emm(Foo(Bar(0)));
26+
// Should suggest `&***` even when deref is pretty deep
27+
let _: *const u8 = &a; //~ ERROR mismatched types
28+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-32122-2.rs:27:24
3+
|
4+
LL | let _: *const u8 = &a;
5+
| --------- ^^
6+
| | |
7+
| | expected `u8`, found struct `Emm`
8+
| | help: consider dereferencing the reference: `&***a`
9+
| expected due to this
10+
|
11+
= note: expected raw pointer `*const u8`
12+
found reference `&Emm`
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)