Skip to content

Commit 0c0365d

Browse files
author
Jakub Bukaj
committed
Improve the readability of diagnostics that involve unresolved type variables
Diagnostics such as the following ``` mismatched types: expected `core::result::Result<uint,()>`, found `core::option::Option<<generic #1>>` <anon>:6 let a: Result<uint, ()> = None; ^~~~ mismatched types: expected `&mut <generic #2>`, found `uint` <anon>:7 f(42u); ^~~ ``` tend to be fairly unappealing to new users. While specific type var IDs are valuable in diagnostics that deal with more than one such variable, in practice many messages only mention one. In those cases, leaving out the specific number makes the messages slightly less terrifying. In addition, type variables have been changed to use the type hole syntax `_` in diagnostics. With a variable ID, they're printed as `_#id` (e.g. `_#1`). In cases where the ID is left out, it's simply `_`. Integer and float variables have an additional suffix after the number, e.g. `_#1i` or `_#3f`.
1 parent 77f44d4 commit 0c0365d

File tree

5 files changed

+210
-163
lines changed

5 files changed

+210
-163
lines changed

src/librustc/middle/ty.rs

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,10 +1032,8 @@ pub enum type_err {
10321032
terr_ref_mutability,
10331033
terr_vec_mutability,
10341034
terr_tuple_size(expected_found<uint>),
1035+
terr_fixed_array_size(expected_found<uint>),
10351036
terr_ty_param_size(expected_found<uint>),
1036-
terr_record_size(expected_found<uint>),
1037-
terr_record_mutability,
1038-
terr_record_fields(expected_found<Ident>),
10391037
terr_arg_count,
10401038
terr_regions_does_not_outlive(Region, Region),
10411039
terr_regions_not_same(Region, Region),
@@ -3790,8 +3788,8 @@ pub fn ty_sort_string(cx: &ctxt, t: t) -> String {
37903788

37913789
ty_enum(id, _) => format!("enum {}", item_path_str(cx, id)),
37923790
ty_uniq(_) => "box".to_string(),
3793-
ty_vec(_, Some(_)) => "array".to_string(),
3794-
ty_vec(_, None) => "unsized array".to_string(),
3791+
ty_vec(_, Some(n)) => format!("array of {} elements", n),
3792+
ty_vec(_, None) => "slice".to_string(),
37953793
ty_ptr(_) => "*-ptr".to_string(),
37963794
ty_rptr(_, _) => "&-ptr".to_string(),
37973795
ty_bare_fn(_) => "extern fn".to_string(),
@@ -3874,27 +3872,18 @@ pub fn type_err_to_str(cx: &ctxt, err: &type_err) -> String {
38743872
values.expected,
38753873
values.found)
38763874
}
3877-
terr_tuple_size(values) => {
3878-
format!("expected a tuple with {} elements, \
3875+
terr_fixed_array_size(values) => {
3876+
format!("expected an array with a fixed size of {} elements, \
38793877
found one with {} elements",
38803878
values.expected,
38813879
values.found)
38823880
}
3883-
terr_record_size(values) => {
3884-
format!("expected a record with {} fields, \
3885-
found one with {} fields",
3881+
terr_tuple_size(values) => {
3882+
format!("expected a tuple with {} elements, \
3883+
found one with {} elements",
38863884
values.expected,
38873885
values.found)
38883886
}
3889-
terr_record_mutability => {
3890-
"record elements differ in mutability".to_string()
3891-
}
3892-
terr_record_fields(values) => {
3893-
format!("expected a record with field `{}`, found one \
3894-
with field `{}`",
3895-
token::get_ident(values.expected),
3896-
token::get_ident(values.found))
3897-
}
38983887
terr_arg_count => {
38993888
"incorrect number of function parameters".to_string()
39003889
}

src/librustc/middle/typeck/infer/combine.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,16 @@ pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C, a: ty::t, b: ty::t) -> cres<t
515515
Ok(ty::mk_rptr(tcx, r, mt))
516516
}
517517

518+
(&ty::ty_vec(a_t, Some(sz_a)), &ty::ty_vec(b_t, Some(sz_b))) => {
519+
this.tys(a_t, b_t).and_then(|t| {
520+
if sz_a == sz_b {
521+
Ok(ty::mk_vec(tcx, t, Some(sz_a)))
522+
} else {
523+
Err(ty::terr_fixed_array_size(expected_found(this, sz_a, sz_b)))
524+
}
525+
})
526+
}
527+
518528
(&ty::ty_vec(a_t, sz_a), &ty::ty_vec(b_t, sz_b)) => {
519529
this.tys(a_t, b_t).and_then(|t| {
520530
if sz_a == sz_b {

src/librustc/middle/typeck/infer/error_reporting.rs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ time of error detection.
6262
use std::collections::HashSet;
6363
use middle::def;
6464
use middle::subst;
65+
use middle::ty_fold::{mod, TypeFoldable};
6566
use middle::ty;
6667
use middle::ty::{Region, ReFree};
6768
use middle::typeck::infer;
@@ -111,7 +112,7 @@ pub trait ErrorReporting {
111112

112113
fn values_str(&self, values: &ValuePairs) -> Option<String>;
113114

114-
fn expected_found_str<T:UserString+Resolvable>(
115+
fn expected_found_str<T: UserString + Resolvable>(
115116
&self,
116117
exp_found: &ty::expected_found<T>)
117118
-> Option<String>;
@@ -396,16 +397,12 @@ impl<'a, 'tcx> ErrorReporting for InferCtxt<'a, 'tcx> {
396397
* or None if this is a derived error.
397398
*/
398399
match *values {
399-
infer::Types(ref exp_found) => {
400-
self.expected_found_str(exp_found)
401-
}
402-
infer::TraitRefs(ref exp_found) => {
403-
self.expected_found_str(exp_found)
404-
}
400+
infer::Types(ref exp_found) => self.expected_found_str(exp_found),
401+
infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found)
405402
}
406403
}
407404

408-
fn expected_found_str<T:UserString+Resolvable>(
405+
fn expected_found_str<T: UserString + Resolvable>(
409406
&self,
410407
exp_found: &ty::expected_found<T>)
411408
-> Option<String>
@@ -420,9 +417,14 @@ impl<'a, 'tcx> ErrorReporting for InferCtxt<'a, 'tcx> {
420417
return None;
421418
}
422419

420+
// Only include variable IDs in the diagnostics if there are at least two
421+
// present across both types/traits.
422+
let should_print_var_ids = expected.remaining_type_variables(self.tcx)
423+
.union(&found.remaining_type_variables(self.tcx)).count() > 1;
424+
423425
Some(format!("expected `{}`, found `{}`",
424-
expected.user_string(self.tcx),
425-
found.user_string(self.tcx)))
426+
expected.user_string_with_var_ids(self.tcx, should_print_var_ids),
427+
found.user_string_with_var_ids(self.tcx, should_print_var_ids)))
426428
}
427429

428430
fn report_param_bound_failure(&self,
@@ -1654,6 +1656,7 @@ impl<'a, 'tcx> ErrorReportingHelpers for InferCtxt<'a, 'tcx> {
16541656
pub trait Resolvable {
16551657
fn resolve(&self, infcx: &InferCtxt) -> Self;
16561658
fn contains_error(&self) -> bool;
1659+
fn remaining_type_variables(&self, tcx: &ty::ctxt) -> HashSet<ty::InferTy>;
16571660
}
16581661

16591662
impl Resolvable for ty::t {
@@ -1663,6 +1666,22 @@ impl Resolvable for ty::t {
16631666
fn contains_error(&self) -> bool {
16641667
ty::type_is_error(*self)
16651668
}
1669+
fn remaining_type_variables(&self, tcx: &ty::ctxt) -> HashSet<ty::InferTy> {
1670+
let mut vars = HashSet::new();
1671+
{
1672+
let mut folder = ty_fold::BottomUpFolder {
1673+
tcx: tcx,
1674+
fldop: |t| {
1675+
if let ty::ty_infer(var) = ty::get(t).sty {
1676+
vars.insert(var);
1677+
}
1678+
t
1679+
}
1680+
};
1681+
self.fold_with(&mut folder);
1682+
}
1683+
vars
1684+
}
16661685
}
16671686

16681687
impl Resolvable for Rc<ty::TraitRef> {
@@ -1672,6 +1691,9 @@ impl Resolvable for Rc<ty::TraitRef> {
16721691
fn contains_error(&self) -> bool {
16731692
ty::trait_ref_contains_error(&**self)
16741693
}
1694+
fn remaining_type_variables(&self, _: &ty::ctxt) -> HashSet<ty::InferTy> {
1695+
HashSet::new()
1696+
}
16751697
}
16761698

16771699
fn lifetimes_in_scope(tcx: &ty::ctxt,

src/librustc/middle/typeck/infer/mod.rs

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ use syntax::ast;
3636
use syntax::codemap;
3737
use syntax::codemap::Span;
3838
use util::common::indent;
39-
use util::ppaux::{bound_region_to_string, ty_to_string, trait_ref_to_string, Repr};
39+
use util::ppaux::{bound_region_to_string, ty_to_string};
40+
use util::ppaux::{trait_ref_to_string, Repr};
4041

4142
use self::coercion::Coerce;
4243
use self::combine::{Combine, CombineFields};
@@ -900,32 +901,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
900901
err: Option<&ty::type_err>) {
901902
debug!("hi! expected_ty = {}, actual_ty = {}", expected_ty, actual_ty);
902903

903-
let error_str = err.map_or("".to_string(), |t_err| {
904-
format!(" ({})", ty::type_err_to_str(self.tcx, t_err))
905-
});
906904
let resolved_expected = expected_ty.map(|e_ty| {
907905
self.resolve_type_vars_if_possible(e_ty)
908906
});
909-
if !resolved_expected.map_or(false, |e| { ty::type_is_error(e) }) {
910-
match resolved_expected {
911-
None => {
912-
self.tcx
913-
.sess
914-
.span_err(sp,
915-
format!("{}{}",
916-
mk_msg(None, actual_ty),
917-
error_str).as_slice())
918-
}
919-
Some(e) => {
920-
self.tcx.sess.span_err(sp,
921-
format!("{}{}",
922-
mk_msg(Some(self.ty_to_string(e)), actual_ty),
923-
error_str).as_slice());
907+
908+
match resolved_expected {
909+
Some(t) if ty::type_is_error(t) => (),
910+
_ => {
911+
let error_str = err.map_or("".to_string(), |t_err| {
912+
format!(" ({})", ty::type_err_to_str(self.tcx, t_err))
913+
});
914+
915+
self.tcx.sess.span_err(sp, format!("{}{}",
916+
mk_msg(resolved_expected.map(|t| self.ty_to_string(t)), actual_ty),
917+
error_str).as_slice());
918+
919+
for err in err.iter() {
920+
ty::note_and_explain_type_err(self.tcx, *err)
924921
}
925922
}
926-
for err in err.iter() {
927-
ty::note_and_explain_type_err(self.tcx, *err)
928-
}
929923
}
930924
}
931925

@@ -945,25 +939,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
945939
}
946940

947941
pub fn report_mismatched_types(&self,
948-
sp: Span,
949-
e: ty::t,
950-
a: ty::t,
942+
span: Span,
943+
expected: ty::t,
944+
actual: ty::t,
951945
err: &ty::type_err) {
952-
let resolved_expected =
953-
self.resolve_type_vars_if_possible(e);
954-
let mk_msg = match ty::get(resolved_expected).sty {
955-
// Don't report an error if expected is ty_err
956-
ty::ty_err => return,
957-
_ => {
958-
// if I leave out : String, it infers &str and complains
959-
|actual: String| {
960-
format!("mismatched types: expected `{}`, found `{}`",
961-
self.ty_to_string(resolved_expected),
962-
actual)
963-
}
964-
}
946+
let trace = TypeTrace {
947+
origin: Misc(span),
948+
values: Types(ty::expected_found {
949+
expected: expected,
950+
found: actual
951+
})
965952
};
966-
self.type_error_message(sp, mk_msg, a, Some(err));
953+
self.report_and_explain_type_error(trace, err);
967954
}
968955

969956
pub fn replace_late_bound_regions_with_fresh_regions(&self,

0 commit comments

Comments
 (0)