From edfca5fe9c7b6c12a9d48ea2dffd6fffff8501a4 Mon Sep 17 00:00:00 2001 From: Donough Liu Date: Mon, 27 Apr 2020 01:41:38 +0800 Subject: [PATCH 1/3] Move branch point upwards to avoid unnecessary mk_ptr() --- src/librustc_typeck/check/coercion.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 3d665123f6767..c1a3ff518b338 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -771,10 +771,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::RawPtr(mt) => (false, mt), _ => return self.unify_and(a, b, identity), }; + coerce_mutbls(mt_a.mutbl, mutbl_b)?; // Check that the types which they point at are compatible. let a_unsafe = self.tcx.mk_ptr(ty::TypeAndMut { mutbl: mutbl_b, ty: mt_a.ty }); - coerce_mutbls(mt_a.mutbl, mutbl_b)?; // Although references and unsafe ptrs have the same // representation, we still register an Adjust::DerefRef so that // regionck knows that the region for `a` must be valid here. From a9340b1f69b0a301214934cd9b24dcf348522988 Mon Sep 17 00:00:00 2001 From: Donough Liu Date: Tue, 28 Apr 2020 10:58:06 +0800 Subject: [PATCH 2/3] Rename function to `suggest_deref_ref_or_into` because it's suggesting derefence instructions --- src/librustc_typeck/check/demand.rs | 2 +- src/librustc_typeck/check/expr.rs | 2 +- src/librustc_typeck/check/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index c75283e419a6d..8c3a010303768 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -25,7 +25,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { self.annotate_expected_due_to_let_ty(err, expr); self.suggest_compatible_variants(err, expr, expected, expr_ty); - self.suggest_ref_or_into(err, expr, expected, expr_ty); + self.suggest_deref_ref_or_into(err, expr, expected, expr_ty); if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { return; } diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index d287589789e2d..92ddfbff824cd 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) { let expr = expr.peel_drop_temps(); - self.suggest_ref_or_into(&mut err, expr, expected_ty, ty); + self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty); extend_err(&mut err); // Error possibly reported in `check_assign` so avoid emitting error again. err.emit_unless(self.is_assign_to_bool(expr, expected_ty)); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5877c6d269ad6..adbab3d4cb620 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5029,7 +5029,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - pub fn suggest_ref_or_into( + pub fn suggest_deref_ref_or_into( &self, err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, From a9858791134253d004971664fd7e9bd9b0983723 Mon Sep 17 00:00:00 2001 From: Donough Liu Date: Wed, 29 Apr 2020 11:41:34 +0800 Subject: [PATCH 3/3] Suggest deref when coercing `ty::Ref` to `ty::RawPtr` --- src/librustc_typeck/check/coercion.rs | 6 ++-- src/librustc_typeck/check/demand.rs | 38 ++++++++++++++++++++++++- src/test/ui/issues/issue-32122-1.fixed | 17 +++++++++++ src/test/ui/issues/issue-32122-1.rs | 17 +++++++++++ src/test/ui/issues/issue-32122-1.stderr | 16 +++++++++++ src/test/ui/issues/issue-32122-2.fixed | 28 ++++++++++++++++++ src/test/ui/issues/issue-32122-2.rs | 28 ++++++++++++++++++ src/test/ui/issues/issue-32122-2.stderr | 16 +++++++++++ 8 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/issues/issue-32122-1.fixed create mode 100644 src/test/ui/issues/issue-32122-1.rs create mode 100644 src/test/ui/issues/issue-32122-1.stderr create mode 100644 src/test/ui/issues/issue-32122-2.fixed create mode 100644 src/test/ui/issues/issue-32122-2.rs create mode 100644 src/test/ui/issues/issue-32122-2.stderr diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index c1a3ff518b338..5d1a1a164855d 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -74,7 +74,7 @@ use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; use smallvec::{smallvec, SmallVec}; use std::ops::Deref; -struct Coerce<'a, 'tcx> { +pub struct Coerce<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, cause: ObligationCause<'tcx>, use_lub: bool, @@ -124,7 +124,7 @@ fn success<'tcx>( } impl<'f, 'tcx> Coerce<'f, 'tcx> { - fn new( + pub fn new( fcx: &'f FnCtxt<'f, 'tcx>, cause: ObligationCause<'tcx>, allow_two_phase: AllowTwoPhase, @@ -132,7 +132,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Coerce { fcx, cause, allow_two_phase, use_lub: false } } - fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { + pub fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { self.commit_if_ok(|_| { if self.use_lub { self.at(&self.cause, self.fcx.param_env).lub(b, a) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 8c3a010303768..bfc5419cc92bf 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -1,3 +1,4 @@ +use crate::check::coercion::Coerce; use crate::check::FnCtxt; use rustc_infer::infer::InferOk; use rustc_trait_selection::infer::InferCtxtExt as _; @@ -8,8 +9,9 @@ use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::{is_range_literal, Node}; +use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::adjustment::AllowTwoPhase; -use rustc_middle::ty::{self, AssocItem, Ty}; +use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -539,6 +541,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Some((sp, "consider removing the borrow", code)); } } + ( + _, + &ty::RawPtr(TypeAndMut { ty: _, mutbl: hir::Mutability::Not }), + &ty::Ref(_, _, hir::Mutability::Not), + ) => { + let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable); + // We don't ever need two-phase here since we throw out the result of the coercion + let coerce = Coerce::new(self, cause, AllowTwoPhase::No); + + if let Some(steps) = + coerce.autoderef(sp, checked_ty).skip(1).find_map(|(referent_ty, steps)| { + coerce + .unify( + coerce.tcx.mk_ptr(ty::TypeAndMut { + mutbl: hir::Mutability::Not, + ty: referent_ty, + }), + expected, + ) + .ok() + .map(|_| steps) + }) + { + // The pointer type implements `Copy` trait so the suggestion is always valid. + if let Ok(code) = sm.span_to_snippet(sp) { + if code.starts_with('&') { + let derefs = "*".repeat(steps - 1); + let message = "consider dereferencing the reference"; + let suggestion = format!("&{}{}", derefs, code[1..].to_string()); + return Some((sp, message, suggestion)); + } + } + } + } _ if sp == expr.span && !is_macro => { // Check for `Deref` implementations by constructing a predicate to // prove: `::Output == U` diff --git a/src/test/ui/issues/issue-32122-1.fixed b/src/test/ui/issues/issue-32122-1.fixed new file mode 100644 index 0000000000000..4fc5f64ff9a45 --- /dev/null +++ b/src/test/ui/issues/issue-32122-1.fixed @@ -0,0 +1,17 @@ +// run-rustfix +use std::ops::Deref; + +struct Foo(u8); + +impl Deref for Foo { + type Target = u8; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn main() { + let a = Foo(0); + // Should suggest `&*` when coercing &ty to *const ty + let _: *const u8 = &*a; //~ ERROR mismatched types +} diff --git a/src/test/ui/issues/issue-32122-1.rs b/src/test/ui/issues/issue-32122-1.rs new file mode 100644 index 0000000000000..3c4859f07a2e7 --- /dev/null +++ b/src/test/ui/issues/issue-32122-1.rs @@ -0,0 +1,17 @@ +// run-rustfix +use std::ops::Deref; + +struct Foo(u8); + +impl Deref for Foo { + type Target = u8; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn main() { + let a = Foo(0); + // Should suggest `&*` when coercing &ty to *const ty + let _: *const u8 = &a; //~ ERROR mismatched types +} diff --git a/src/test/ui/issues/issue-32122-1.stderr b/src/test/ui/issues/issue-32122-1.stderr new file mode 100644 index 0000000000000..313de275c53ee --- /dev/null +++ b/src/test/ui/issues/issue-32122-1.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/issue-32122-1.rs:16:24 + | +LL | let _: *const u8 = &a; + | --------- ^^ + | | | + | | expected `u8`, found struct `Foo` + | | help: consider dereferencing the reference: `&*a` + | expected due to this + | + = note: expected raw pointer `*const u8` + found reference `&Foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/issues/issue-32122-2.fixed b/src/test/ui/issues/issue-32122-2.fixed new file mode 100644 index 0000000000000..cee0e59297657 --- /dev/null +++ b/src/test/ui/issues/issue-32122-2.fixed @@ -0,0 +1,28 @@ +// run-rustfix +use std::ops::Deref; +struct Bar(u8); +struct Foo(Bar); +struct Emm(Foo); +impl Deref for Bar{ + type Target = u8; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Emm { + type Target = Foo; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +fn main() { + let a = Emm(Foo(Bar(0))); + // Should suggest `&***` even when deref is pretty deep + let _: *const u8 = &***a; //~ ERROR mismatched types +} diff --git a/src/test/ui/issues/issue-32122-2.rs b/src/test/ui/issues/issue-32122-2.rs new file mode 100644 index 0000000000000..39e9df4224e74 --- /dev/null +++ b/src/test/ui/issues/issue-32122-2.rs @@ -0,0 +1,28 @@ +// run-rustfix +use std::ops::Deref; +struct Bar(u8); +struct Foo(Bar); +struct Emm(Foo); +impl Deref for Bar{ + type Target = u8; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Emm { + type Target = Foo; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +fn main() { + let a = Emm(Foo(Bar(0))); + // Should suggest `&***` even when deref is pretty deep + let _: *const u8 = &a; //~ ERROR mismatched types +} diff --git a/src/test/ui/issues/issue-32122-2.stderr b/src/test/ui/issues/issue-32122-2.stderr new file mode 100644 index 0000000000000..959a49507e4f5 --- /dev/null +++ b/src/test/ui/issues/issue-32122-2.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/issue-32122-2.rs:27:24 + | +LL | let _: *const u8 = &a; + | --------- ^^ + | | | + | | expected `u8`, found struct `Emm` + | | help: consider dereferencing the reference: `&***a` + | expected due to this + | + = note: expected raw pointer `*const u8` + found reference `&Emm` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`.