Skip to content

Commit 60d62be

Browse files
committed
Suggest deref when coercing ty::Ref to ty::RawPtr with arbitrary
mutability
1 parent 19ae74d commit 60d62be

File tree

10 files changed

+323
-47
lines changed

10 files changed

+323
-47
lines changed

src/librustc_typeck/check/coercion.rs

Lines changed: 12 additions & 3 deletions
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-
pub struct Coerce<'a, 'tcx> {
77+
struct Coerce<'a, 'tcx> {
7878
fcx: &'a FnCtxt<'a, 'tcx>,
7979
cause: ObligationCause<'tcx>,
8080
use_lub: bool,
@@ -126,15 +126,15 @@ fn success<'tcx>(
126126
}
127127

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

137-
pub fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
137+
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
138138
debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub);
139139
self.commit_if_ok(|_| {
140140
if self.use_lub {
@@ -831,6 +831,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
831831
self.probe(|_| coerce.coerce(source, target)).is_ok()
832832
}
833833

834+
pub fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option<usize> {
835+
let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable);
836+
// We don't ever need two-phase here since we throw out the result of the coercion
837+
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
838+
coerce
839+
.autoderef(rustc_span::DUMMY_SP, expr_ty)
840+
.find_map(|(ty, steps)| coerce.unify(ty, target).ok().map(|_| steps))
841+
}
842+
834843
/// Given some expressions, their known unified type and another expression,
835844
/// tries to unify the types, potentially inserting coercions on any of the
836845
/// provided expressions and returns their LUB (aka "common supertype").

src/librustc_typeck/check/demand.rs

Lines changed: 85 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::check::coercion::Coerce;
21
use crate::check::FnCtxt;
32
use rustc_infer::infer::InferOk;
43
use rustc_trait_selection::infer::InferCtxtExt as _;
@@ -9,7 +8,6 @@ use rustc_ast::util::parser::PREC_POSTFIX;
98
use rustc_errors::{Applicability, DiagnosticBuilder};
109
use rustc_hir as hir;
1110
use rustc_hir::{is_range_literal, Node};
12-
use rustc_middle::traits::ObligationCauseCode;
1311
use rustc_middle::ty::adjustment::AllowTwoPhase;
1412
use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
1513
use rustc_span::symbol::sym;
@@ -376,7 +374,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
376374
expr: &hir::Expr<'_>,
377375
checked_ty: Ty<'tcx>,
378376
expected: Ty<'tcx>,
379-
) -> Option<(Span, &'static str, String)> {
377+
) -> Option<(Span, &'static str, String, Applicability)> {
380378
let sm = self.sess().source_map();
381379
let sp = expr.span;
382380
if sm.is_imported(sp) {
@@ -395,16 +393,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
395393
// `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
396394
let expr = expr.peel_drop_temps();
397395

396+
let remove_prefix = |s: String, prefix: &str| {
397+
if s.starts_with(prefix) { Some(s[prefix.len()..].to_string()) } else { None }
398+
};
399+
398400
match (&expr.kind, &expected.kind, &checked_ty.kind) {
399401
(_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.kind, &check.kind) {
400402
(&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
401403
if let hir::ExprKind::Lit(_) = expr.kind {
402404
if let Ok(src) = sm.span_to_snippet(sp) {
403-
if src.starts_with("b\"") {
405+
if let Some(src) = remove_prefix(src, "b\"") {
404406
return Some((
405407
sp,
406408
"consider removing the leading `b`",
407-
src[1..].to_string(),
409+
format!("\"{}", src),
410+
Applicability::MachineApplicable,
408411
));
409412
}
410413
}
@@ -413,11 +416,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
413416
(&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
414417
if let hir::ExprKind::Lit(_) = expr.kind {
415418
if let Ok(src) = sm.span_to_snippet(sp) {
416-
if src.starts_with('"') {
419+
if let Some(src) = remove_prefix(src, "\"") {
417420
return Some((
418421
sp,
419422
"consider adding a leading `b`",
420-
format!("b{}", src),
423+
format!("b\"{}", src),
424+
Applicability::MachineApplicable,
421425
));
422426
}
423427
}
@@ -470,7 +474,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
470474
let sugg_expr = if needs_parens { format!("({})", src) } else { src };
471475

472476
if let Some(sugg) = self.can_use_as_ref(expr) {
473-
return Some(sugg);
477+
return Some((
478+
sugg.0,
479+
sugg.1,
480+
sugg.2,
481+
Applicability::MachineApplicable,
482+
));
474483
}
475484
let field_name = if is_struct_pat_shorthand_field {
476485
format!("{}: ", sugg_expr)
@@ -495,6 +504,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
495504
"consider dereferencing here to assign to the mutable \
496505
borrowed piece of memory",
497506
format!("*{}", src),
507+
Applicability::MachineApplicable,
498508
));
499509
}
500510
}
@@ -505,11 +515,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
505515
sp,
506516
"consider mutably borrowing here",
507517
format!("{}&mut {}", field_name, sugg_expr),
518+
Applicability::MachineApplicable,
508519
),
509520
hir::Mutability::Not => (
510521
sp,
511522
"consider borrowing here",
512523
format!("{}&{}", field_name, sugg_expr),
524+
Applicability::MachineApplicable,
513525
),
514526
});
515527
}
@@ -526,51 +538,84 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
526538
// We have `&T`, check if what was expected was `T`. If so,
527539
// we may want to suggest removing a `&`.
528540
if sm.is_imported(expr.span) {
529-
if let Ok(code) = sm.span_to_snippet(sp) {
530-
if code.starts_with('&') {
541+
if let Ok(src) = sm.span_to_snippet(sp) {
542+
if let Some(src) = remove_prefix(src, "&") {
531543
return Some((
532544
sp,
533545
"consider removing the borrow",
534-
code[1..].to_string(),
546+
src,
547+
Applicability::MachineApplicable,
535548
));
536549
}
537550
}
538551
return None;
539552
}
540553
if let Ok(code) = sm.span_to_snippet(expr.span) {
541-
return Some((sp, "consider removing the borrow", code));
554+
return Some((
555+
sp,
556+
"consider removing the borrow",
557+
code,
558+
Applicability::MachineApplicable,
559+
));
542560
}
543561
}
544562
(
545563
_,
546-
&ty::RawPtr(TypeAndMut { ty: _, mutbl: hir::Mutability::Not }),
547-
&ty::Ref(_, _, hir::Mutability::Not),
564+
&ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }),
565+
&ty::Ref(_, ty_a, mutbl_a),
548566
) => {
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));
567+
if let Some(steps) = self.deref_steps(ty_a, ty_b) {
568+
// Only suggest valid if dereferencing needed.
569+
if steps > 0 {
570+
// The pointer type implements `Copy` trait so the suggestion is always valid.
571+
if let Ok(src) = sm.span_to_snippet(sp) {
572+
let derefs = "*".repeat(steps);
573+
match mutbl_b {
574+
hir::Mutability::Mut => match mutbl_a {
575+
hir::Mutability::Mut => {
576+
if let Some(src) = remove_prefix(src, "&mut ") {
577+
return Some((
578+
sp,
579+
"consider dereferencing",
580+
format!("&mut {}{}", derefs, src),
581+
Applicability::MachineApplicable,
582+
));
583+
}
584+
}
585+
hir::Mutability::Not => {
586+
if let Some(src) = remove_prefix(src, "&") {
587+
return Some((
588+
sp,
589+
"consider dereferencing",
590+
format!("&mut {}{}", derefs, src),
591+
Applicability::Unspecified,
592+
));
593+
}
594+
}
595+
},
596+
hir::Mutability::Not => match mutbl_a {
597+
hir::Mutability::Mut => {
598+
if let Some(src) = remove_prefix(src, "&mut ") {
599+
return Some((
600+
sp,
601+
"consider dereferencing",
602+
format!("&{}{}", derefs, src),
603+
Applicability::MachineApplicable,
604+
));
605+
}
606+
}
607+
hir::Mutability::Not => {
608+
if let Some(src) = remove_prefix(src, "&") {
609+
return Some((
610+
sp,
611+
"consider dereferencing",
612+
format!("&{}{}", derefs, src),
613+
Applicability::MachineApplicable,
614+
));
615+
}
616+
}
617+
},
618+
}
574619
}
575620
}
576621
}
@@ -616,7 +661,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
616661
} else {
617662
format!("*{}", code)
618663
};
619-
return Some((sp, message, suggestion));
664+
return Some((sp, message, suggestion, Applicability::MachineApplicable));
620665
}
621666
}
622667
}

src/librustc_typeck/check/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5036,8 +5036,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
50365036
expected: Ty<'tcx>,
50375037
found: Ty<'tcx>,
50385038
) {
5039-
if let Some((sp, msg, suggestion)) = self.check_ref(expr, found, expected) {
5040-
err.span_suggestion(sp, msg, suggestion, Applicability::MachineApplicable);
5039+
if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) {
5040+
err.span_suggestion(sp, msg, suggestion, applicability);
50415041
} else if let (ty::FnDef(def_id, ..), true) =
50425042
(&found.kind, self.suggest_fn_call(err, expr, expected, found))
50435043
{

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | let _: *const u8 = &a;
55
| --------- ^^
66
| | |
77
| | expected `u8`, found struct `Foo`
8-
| | help: consider dereferencing the reference: `&*a`
8+
| | help: consider dereferencing: `&*a`
99
| expected due to this
1010
|
1111
= note: expected raw pointer `*const u8`

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | let _: *const u8 = &a;
55
| --------- ^^
66
| | |
77
| | expected `u8`, found struct `Emm`
8-
| | help: consider dereferencing the reference: `&***a`
8+
| | help: consider dereferencing: `&***a`
99
| expected due to this
1010
|
1111
= note: expected raw pointer `*const u8`
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// run-rustfix
2+
use std::ops::Deref;
3+
use std::ops::DerefMut;
4+
struct Bar(u8);
5+
struct Foo(Bar);
6+
struct Emm(Foo);
7+
impl Deref for Bar{
8+
type Target = u8;
9+
fn deref(&self) -> &Self::Target {
10+
&self.0
11+
}
12+
}
13+
impl Deref for Foo {
14+
type Target = Bar;
15+
fn deref(&self) -> &Self::Target {
16+
&self.0
17+
}
18+
}
19+
impl Deref for Emm {
20+
type Target = Foo;
21+
fn deref(&self) -> &Self::Target {
22+
&self.0
23+
}
24+
}
25+
impl DerefMut for Bar{
26+
fn deref_mut(&mut self) -> &mut Self::Target {
27+
&mut self.0
28+
}
29+
}
30+
impl DerefMut for Foo {
31+
fn deref_mut(&mut self) -> &mut Self::Target {
32+
&mut self.0
33+
}
34+
}
35+
impl DerefMut for Emm {
36+
fn deref_mut(&mut self) -> &mut Self::Target {
37+
&mut self.0
38+
}
39+
}
40+
fn main() {
41+
// Suggest dereference with arbitrary mutability
42+
let a = Emm(Foo(Bar(0)));
43+
let _: *const u8 = &***a; //~ ERROR mismatched types
44+
45+
let mut a = Emm(Foo(Bar(0)));
46+
let _: *mut u8 = &mut ***a; //~ ERROR mismatched types
47+
48+
let a = Emm(Foo(Bar(0)));
49+
let _: *const u8 = &***a; //~ ERROR mismatched types
50+
51+
let mut a = Emm(Foo(Bar(0)));
52+
let _: *mut u8 = &mut ***a; //~ ERROR mismatched types
53+
54+
}

0 commit comments

Comments
 (0)