Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specific labels when referring to "expected" and "found" types #66389

Merged
merged 6 commits into from
Nov 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
71 changes: 46 additions & 25 deletions src/librustc/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1163,8 +1163,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
Some(values) => {
let (is_simple_error, exp_found) = match values {
ValuePairs::Types(exp_found) => {
let is_simple_err =
exp_found.expected.is_primitive() && exp_found.found.is_primitive();
let is_simple_err = exp_found.expected.is_simple_text()
&& exp_found.found.is_simple_text();

(is_simple_err, Some(exp_found))
}
Expand Down Expand Up @@ -1197,40 +1197,61 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
};

if let Some((expected, found)) = expected_found {
match (terr, is_simple_error, expected == found) {
(&TypeError::Sorts(ref values), false, true) => {
let sort_string = | a_type: Ty<'tcx> |
if let ty::Opaque(def_id, _) = a_type.kind {
format!(" (opaque type at {})", self.tcx.sess.source_map()
.mk_substr_filename(self.tcx.def_span(def_id)))
} else {
format!(" ({})", a_type.sort_string(self.tcx))
};
diag.note_expected_found_extra(
&"type",
expected,
found,
&sort_string(values.expected),
&sort_string(values.found),
);
let expected_label = exp_found.map_or("type".into(), |ef| ef.expected.prefix_string());
let found_label = exp_found.map_or("type".into(), |ef| ef.found.prefix_string());
match (&terr, expected == found) {
(TypeError::Sorts(values), extra) => {
let sort_string = |ty: Ty<'tcx>| match (extra, &ty.kind) {
(true, ty::Opaque(def_id, _)) => format!(
" (opaque type at {})",
self.tcx.sess.source_map()
.mk_substr_filename(self.tcx.def_span(*def_id)),
),
(true, _) => format!(" ({})", ty.sort_string(self.tcx)),
(false, _) => "".to_string(),
};
if !(values.expected.is_simple_text() && values.found.is_simple_text()) || (
exp_found.map_or(false, |ef| {
// This happens when the type error is a subset of the expectation,
// like when you have two references but one is `usize` and the other
// is `f32`. In those cases we still want to show the `note`. If the
// value from `ef` is `Infer(_)`, then we ignore it.
if !ef.expected.is_ty_infer() {
ef.expected != values.expected
} else if !ef.found.is_ty_infer() {
ef.found != values.found
} else {
false
}
})
) {
diag.note_expected_found_extra(
&expected_label,
expected,
&found_label,
found,
&sort_string(values.expected),
&sort_string(values.found),
);
}
}
(TypeError::ObjectUnsafeCoercion(_), ..) => {
(TypeError::ObjectUnsafeCoercion(_), _) => {
diag.note_unsuccessfull_coercion(found, expected);
}
(_, false, _) => {
(_, _) => {
debug!(
"note_type_err: exp_found={:?}, expected={:?} found={:?}",
exp_found, expected, found
);
if let Some(exp_found) = exp_found {
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
if !is_simple_error || terr.must_include_note() {
diag.note_expected_found(&expected_label, expected, &found_label, found);
}

diag.note_expected_found(&"type", expected, found);
}
_ => (),
}
}
if let Some(exp_found) = exp_found {
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
}

// In some (most?) cases cause.body_id points to actual body, but in some cases
// it's a actual definition. According to the comments (e.g. in
Expand Down
56 changes: 56 additions & 0 deletions src/librustc/ty/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! Diagnostics related methods for `TyS`.

use crate::ty::TyS;
use crate::ty::TyKind::*;
use crate::ty::sty::InferTy;

impl<'tcx> TyS<'tcx> {
/// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive.
pub fn is_primitive_ty(&self) -> bool {
match self.kind {
Bool | Char | Str | Int(_) | Uint(_) | Float(_) |
Infer(InferTy::IntVar(_)) | Infer(InferTy::FloatVar(_)) |
Infer(InferTy::FreshIntTy(_)) | Infer(InferTy::FreshFloatTy(_)) => true,
_ => false,
}
}

/// Whether the type is succinctly representable as a type instead of just refered to with a
/// description in error messages. This is used in the main error message.
pub fn is_simple_ty(&self) -> bool {
match self.kind {
Bool | Char | Str | Int(_) | Uint(_) | Float(_) |
Infer(InferTy::IntVar(_)) | Infer(InferTy::FloatVar(_)) |
Infer(InferTy::FreshIntTy(_)) | Infer(InferTy::FreshFloatTy(_)) => true,
Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(),
Tuple(tys) if tys.is_empty() => true,
_ => false,
}
}

/// Whether the type is succinctly representable as a type instead of just refered to with a
/// description in error messages. This is used in the primary span label. Beyond what
/// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
/// ADTs with no type arguments.
pub fn is_simple_text(&self) -> bool {
match self.kind {
Adt(_, substs) => substs.types().next().is_none(),
Ref(_, ty, _) => ty.is_simple_text(),
_ => self.is_simple_ty(),
}
}

/// Whether the type can be safely suggested during error recovery.
pub fn is_suggestable(&self) -> bool {
match self.kind {
Opaque(..) |
FnDef(..) |
FnPtr(..) |
Dynamic(..) |
Closure(..) |
Infer(..) |
Projection(..) => false,
_ => true,
}
}
}
104 changes: 84 additions & 20 deletions src/librustc/ty/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ pub enum UnconstrainedNumeric {
impl<'tcx> fmt::Display for TypeError<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::TypeError::*;
fn report_maybe_different(f: &mut fmt::Formatter<'_>,
expected: &str, found: &str) -> fmt::Result {
fn report_maybe_different(
f: &mut fmt::Formatter<'_>,
expected: &str,
found: &str,
) -> fmt::Result {
// A naive approach to making sure that we're not reporting silly errors such as:
// (expected closure, found closure).
if expected == found {
Expand Down Expand Up @@ -183,46 +186,77 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
}
}

impl<'tcx> TypeError<'tcx> {
pub fn must_include_note(&self) -> bool {
use self::TypeError::*;
match self {
CyclicTy(_) |
UnsafetyMismatch(_) |
Mismatch |
AbiMismatch(_) |
FixedArraySize(_) |
Sorts(_) |
IntMismatch(_) |
FloatMismatch(_) |
VariadicMismatch(_) => false,

Mutability |
TupleSize(_) |
ArgCount |
RegionsDoesNotOutlive(..) |
RegionsInsufficientlyPolymorphic(..) |
RegionsOverlyPolymorphic(..) |
RegionsPlaceholderMismatch |
Traits(_) |
ProjectionMismatched(_) |
ProjectionBoundsLength(_) |
ExistentialMismatch(_) |
ConstMismatch(_) |
IntrinsicCast |
ObjectUnsafeCoercion(_) => true,
}
}
}

impl<'tcx> ty::TyS<'tcx> {
pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> {
match self.kind {
ty::Bool | ty::Char | ty::Int(_) |
ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => self.to_string().into(),
ty::Tuple(ref tys) if tys.is_empty() => self.to_string().into(),
ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => format!("`{}`", self).into(),
ty::Tuple(ref tys) if tys.is_empty() => format!("`{}`", self).into(),

ty::Adt(def, _) => format!("{} `{}`", def.descr(), tcx.def_path_str(def.did)).into(),
ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(),
ty::Array(_, n) => {
ty::Array(t, n) => {
let n = tcx.lift(&n).unwrap();
match n.try_eval_usize(tcx, ty::ParamEnv::empty()) {
Some(n) => {
format!("array of {} element{}", n, pluralize!(n)).into()
}
_ if t.is_simple_ty() => format!("array `{}`", self).into(),
Some(n) => format!("array of {} element{} ", n, pluralize!(n)).into(),
None => "array".into(),
}
}
ty::Slice(ty) if ty.is_simple_ty() => format!("slice `{}`", self).into(),
ty::Slice(_) => "slice".into(),
ty::RawPtr(_) => "*-ptr".into(),
ty::Ref(region, ty, mutbl) => {
ty::Ref(_, ty, mutbl) => {
let tymut = ty::TypeAndMut { ty, mutbl };
let tymut_string = tymut.to_string();
if tymut_string == "_" || //unknown type name,
tymut_string.len() > 10 || //name longer than saying "reference",
region.to_string() != "'_" //... or a complex type
{
format!("{}reference", match mutbl {
hir::Mutability::Mutable => "mutable ",
_ => ""
}).into()
} else {
format!("&{}", tymut_string).into()
if tymut_string != "_" && (
ty.is_simple_text() || tymut_string.len() < "mutable reference".len()
) {
format!("`&{}`", tymut_string).into()
} else { // Unknown type name, it's long or has type arguments
match mutbl {
hir::Mutability::Mutable => "mutable reference",
_ => "reference",
}.into()
}
}
ty::FnDef(..) => "fn item".into(),
ty::FnPtr(_) => "fn pointer".into(),
ty::Dynamic(ref inner, ..) => {
if let Some(principal) = inner.principal() {
format!("trait {}", tcx.def_path_str(principal.def_id())).into()
format!("trait `{}`", tcx.def_path_str(principal.def_id())).into()
} else {
"trait".into()
}
Expand All @@ -246,6 +280,36 @@ impl<'tcx> ty::TyS<'tcx> {
ty::Error => "type error".into(),
}
}

pub fn prefix_string(&self) -> Cow<'static, str> {
match self.kind {
ty::Infer(_) | ty::Error | ty::Bool | ty::Char | ty::Int(_) |
ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => "type".into(),
ty::Tuple(ref tys) if tys.is_empty() => "unit type".into(),
ty::Adt(def, _) => def.descr().into(),
ty::Foreign(_) => "extern type".into(),
ty::Array(..) => "array".into(),
ty::Slice(_) => "slice".into(),
ty::RawPtr(_) => "raw pointer".into(),
ty::Ref(.., mutbl) => match mutbl {
hir::Mutability::Mutable => "mutable reference",
_ => "reference"
}.into(),
ty::FnDef(..) => "fn item".into(),
ty::FnPtr(_) => "fn pointer".into(),
ty::Dynamic(..) => "trait object".into(),
ty::Closure(..) => "closure".into(),
ty::Generator(..) => "generator".into(),
ty::GeneratorWitness(..) => "generator witness".into(),
ty::Tuple(..) => "tuple".into(),
ty::Placeholder(..) => "higher-ranked type".into(),
ty::Bound(..) => "bound type variable".into(),
ty::Projection(_) => "associated type".into(),
ty::UnnormalizedProjection(_) => "associated type".into(),
ty::Param(_) => "type parameter".into(),
ty::Opaque(..) => "opaque type".into(),
}
}
}

impl<'tcx> TyCtxt<'tcx> {
Expand Down
33 changes: 2 additions & 31 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub use self::sty::BoundRegion::*;
pub use self::sty::InferTy::*;
pub use self::sty::RegionKind::*;
pub use self::sty::TyKind::*;
pub use crate::ty::diagnostics::*;

pub use self::binding::BindingMode;
pub use self::binding::BindingMode::*;
Expand Down Expand Up @@ -122,6 +123,7 @@ mod instance;
mod structural_impls;
mod structural_match;
mod sty;
mod diagnostics;

// Data types

Expand Down Expand Up @@ -552,37 +554,6 @@ impl<'tcx> Hash for TyS<'tcx> {
}
}

impl<'tcx> TyS<'tcx> {
pub fn is_primitive_ty(&self) -> bool {
match self.kind {
Bool |
Char |
Int(_) |
Uint(_) |
Float(_) |
Infer(InferTy::IntVar(_)) |
Infer(InferTy::FloatVar(_)) |
Infer(InferTy::FreshIntTy(_)) |
Infer(InferTy::FreshFloatTy(_)) => true,
Ref(_, x, _) => x.is_primitive_ty(),
_ => false,
}
}

pub fn is_suggestable(&self) -> bool {
match self.kind {
Opaque(..) |
FnDef(..) |
FnPtr(..) |
Dynamic(..) |
Closure(..) |
Infer(..) |
Projection(..) => false,
_ => true,
}
}
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::TyS<'tcx> {
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
let ty::TyS {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/ty/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// LL | impl Bar for Foo {
// | ---------------- in this `impl` item
// LL | type Ok = ();
// | ^^^^^^^^^^^^^ expected u32, found ()
// | ^^^^^^^^^^^^^ expected `u32`, found `()`
// |
// = note: expected type `u32`
// found type `()`
Expand All @@ -228,7 +228,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// LL | impl Bar for Foo {
// | ---------------- in this `impl` item
// LL | type Ok = ();
// | ^^^^^^^^^^^^^ expected u32, found ()
// | ^^^^^^^^^^^^^ expected `u32`, found `()`
// ...
// LL | impl Bar2 for Foo2 {
// | ---------------- in this `impl` item
Expand Down
Loading