diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index edb67f7fddf8..3ec1106ff20e 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1032,10 +1032,8 @@ pub enum type_err { terr_ref_mutability, terr_vec_mutability, terr_tuple_size(expected_found), + terr_fixed_array_size(expected_found), terr_ty_param_size(expected_found), - terr_record_size(expected_found), - terr_record_mutability, - terr_record_fields(expected_found), terr_arg_count, terr_regions_does_not_outlive(Region, Region), terr_regions_not_same(Region, Region), @@ -3790,8 +3788,8 @@ pub fn ty_sort_string(cx: &ctxt, t: t) -> String { ty_enum(id, _) => format!("enum {}", item_path_str(cx, id)), ty_uniq(_) => "box".to_string(), - ty_vec(_, Some(_)) => "array".to_string(), - ty_vec(_, None) => "unsized array".to_string(), + ty_vec(_, Some(n)) => format!("array of {} elements", n), + ty_vec(_, None) => "slice".to_string(), ty_ptr(_) => "*-ptr".to_string(), ty_rptr(_, _) => "&-ptr".to_string(), ty_bare_fn(_) => "extern fn".to_string(), @@ -3874,27 +3872,18 @@ pub fn type_err_to_str(cx: &ctxt, err: &type_err) -> String { values.expected, values.found) } + terr_fixed_array_size(values) => { + format!("expected an array with a fixed size of {} elements, \ + found one with {} elements", + values.expected, + values.found) + } terr_tuple_size(values) => { format!("expected a tuple with {} elements, \ found one with {} elements", values.expected, values.found) } - terr_record_size(values) => { - format!("expected a record with {} fields, \ - found one with {} fields", - values.expected, - values.found) - } - terr_record_mutability => { - "record elements differ in mutability".to_string() - } - terr_record_fields(values) => { - format!("expected a record with field `{}`, found one \ - with field `{}`", - token::get_ident(values.expected), - token::get_ident(values.found)) - } terr_arg_count => { "incorrect number of function parameters".to_string() } diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index de9379a3aa76..e51eb331cdc3 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -515,6 +515,16 @@ pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C, a: ty::t, b: ty::t) -> cres { + this.tys(a_t, b_t).and_then(|t| { + if sz_a == sz_b { + Ok(ty::mk_vec(tcx, t, Some(sz_a))) + } else { + Err(ty::terr_fixed_array_size(expected_found(this, sz_a, sz_b))) + } + }) + } + (&ty::ty_vec(a_t, sz_a), &ty::ty_vec(b_t, sz_b)) => { this.tys(a_t, b_t).and_then(|t| { if sz_a == sz_b { diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 426c6836778e..0f000e93fc01 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -62,6 +62,7 @@ time of error detection. use std::collections::HashSet; use middle::def; use middle::subst; +use middle::ty_fold::{mod, TypeFoldable}; use middle::ty; use middle::ty::{Region, ReFree}; use middle::typeck::infer; @@ -111,7 +112,7 @@ pub trait ErrorReporting { fn values_str(&self, values: &ValuePairs) -> Option; - fn expected_found_str( + fn expected_found_str( &self, exp_found: &ty::expected_found) -> Option; @@ -396,16 +397,12 @@ impl<'a, 'tcx> ErrorReporting for InferCtxt<'a, 'tcx> { * or None if this is a derived error. */ match *values { - infer::Types(ref exp_found) => { - self.expected_found_str(exp_found) - } - infer::TraitRefs(ref exp_found) => { - self.expected_found_str(exp_found) - } + infer::Types(ref exp_found) => self.expected_found_str(exp_found), + infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found) } } - fn expected_found_str( + fn expected_found_str( &self, exp_found: &ty::expected_found) -> Option @@ -420,9 +417,14 @@ impl<'a, 'tcx> ErrorReporting for InferCtxt<'a, 'tcx> { return None; } + // Only include variable IDs in the diagnostics if there are at least two + // present across both types/traits. + let should_print_var_ids = expected.remaining_type_variables(self.tcx) + .union(&found.remaining_type_variables(self.tcx)).count() > 1; + Some(format!("expected `{}`, found `{}`", - expected.user_string(self.tcx), - found.user_string(self.tcx))) + expected.user_string_with_var_ids(self.tcx, should_print_var_ids), + found.user_string_with_var_ids(self.tcx, should_print_var_ids))) } fn report_param_bound_failure(&self, @@ -1654,6 +1656,7 @@ impl<'a, 'tcx> ErrorReportingHelpers for InferCtxt<'a, 'tcx> { pub trait Resolvable { fn resolve(&self, infcx: &InferCtxt) -> Self; fn contains_error(&self) -> bool; + fn remaining_type_variables(&self, tcx: &ty::ctxt) -> HashSet; } impl Resolvable for ty::t { @@ -1663,6 +1666,22 @@ impl Resolvable for ty::t { fn contains_error(&self) -> bool { ty::type_is_error(*self) } + fn remaining_type_variables(&self, tcx: &ty::ctxt) -> HashSet { + let mut vars = HashSet::new(); + { + let mut folder = ty_fold::BottomUpFolder { + tcx: tcx, + fldop: |t| { + if let ty::ty_infer(var) = ty::get(t).sty { + vars.insert(var); + } + t + } + }; + self.fold_with(&mut folder); + } + vars + } } impl Resolvable for Rc { @@ -1672,6 +1691,9 @@ impl Resolvable for Rc { fn contains_error(&self) -> bool { ty::trait_ref_contains_error(&**self) } + fn remaining_type_variables(&self, _: &ty::ctxt) -> HashSet { + HashSet::new() + } } fn lifetimes_in_scope(tcx: &ty::ctxt, diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index d2a77f906b53..227a9b1bdcc8 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -36,7 +36,8 @@ use syntax::ast; use syntax::codemap; use syntax::codemap::Span; use util::common::indent; -use util::ppaux::{bound_region_to_string, ty_to_string, trait_ref_to_string, Repr}; +use util::ppaux::{bound_region_to_string, ty_to_string}; +use util::ppaux::{trait_ref_to_string, Repr}; use self::coercion::Coerce; use self::combine::{Combine, CombineFields}; @@ -900,31 +901,24 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err: Option<&ty::type_err>) { debug!("hi! expected_ty = {}, actual_ty = {}", expected_ty, actual_ty); - let error_str = err.map_or("".to_string(), |t_err| { - format!(" ({})", ty::type_err_to_str(self.tcx, t_err)) - }); let resolved_expected = expected_ty.map(|e_ty| { self.resolve_type_vars_if_possible(e_ty) }); - if !resolved_expected.map_or(false, |e| { ty::type_is_error(e) }) { - match resolved_expected { - None => { - self.tcx - .sess - .span_err(sp, - format!("{}{}", - mk_msg(None, actual_ty), - error_str).as_slice()) + + match resolved_expected { + Some(t) if ty::type_is_error(t) => (), + _ => { + let error_str = err.map_or("".to_string(), |t_err| { + format!(" ({})", ty::type_err_to_str(self.tcx, t_err)) + }); + + self.tcx.sess.span_err(sp, format!("{}{}", + mk_msg(resolved_expected.map(|t| self.ty_to_string(t)), actual_ty), + error_str).as_slice()); + + for err in err.iter() { + ty::note_and_explain_type_err(self.tcx, *err) } - Some(e) => { - self.tcx.sess.span_err(sp, - format!("{}{}", - mk_msg(Some(self.ty_to_string(e)), actual_ty), - error_str).as_slice()); - } - } - for err in err.iter() { - ty::note_and_explain_type_err(self.tcx, *err) } } } @@ -945,25 +939,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub fn report_mismatched_types(&self, - sp: Span, - e: ty::t, - a: ty::t, + span: Span, + expected: ty::t, + actual: ty::t, err: &ty::type_err) { - let resolved_expected = - self.resolve_type_vars_if_possible(e); - let mk_msg = match ty::get(resolved_expected).sty { - // Don't report an error if expected is ty_err - ty::ty_err => return, - _ => { - // if I leave out : String, it infers &str and complains - |actual: String| { - format!("mismatched types: expected `{}`, found `{}`", - self.ty_to_string(resolved_expected), - actual) - } - } + let trace = TypeTrace { + origin: Misc(span), + values: Types(ty::expected_found { + expected: expected, + found: actual + }) }; - self.type_error_message(sp, mk_msg, a, Some(err)); + self.report_and_explain_type_error(trace, err); } pub fn replace_late_bound_regions_with_fresh_regions(&self, diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 296158dd4a86..572f2c9abf22 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -43,6 +43,9 @@ pub trait Repr { /// Produces a string suitable for showing to the user. pub trait UserString { fn user_string(&self, tcx: &ctxt) -> String; + fn user_string_with_var_ids(&self, tcx: &ctxt, _: bool) -> String { + self.user_string(tcx) + } } pub fn note_and_explain_region(cx: &ctxt, @@ -228,8 +231,14 @@ pub fn mutability_to_string(m: ast::Mutability) -> String { } } +pub fn mt_to_string_with_var_ids(cx: &ctxt, m: &mt, print_var_ids: bool) -> String { + format!("{}{}", + mutability_to_string(m.mutbl), + ty_to_string_with_var_ids(cx, m.ty, print_var_ids)) +} + pub fn mt_to_string(cx: &ctxt, m: &mt) -> String { - format!("{}{}", mutability_to_string(m.mutbl), ty_to_string(cx, m.ty)) + mt_to_string_with_var_ids(cx, m, false) } pub fn trait_store_to_string(cx: &ctxt, s: ty::TraitStore) -> String { @@ -256,14 +265,17 @@ pub fn trait_ref_to_string(cx: &ctxt, trait_ref: &ty::TraitRef) -> String { } pub fn ty_to_string(cx: &ctxt, typ: t) -> String { - fn fn_input_to_string(cx: &ctxt, input: ty::t) -> String { - ty_to_string(cx, input).to_string() - } + ty_to_string_with_var_ids(cx, typ, true) +} + +pub fn ty_to_string_with_var_ids(cx: &ctxt, typ: t, mut print_var_ids: bool) -> String { + print_var_ids = print_var_ids || cx.sess.verbose(); fn bare_fn_to_string(cx: &ctxt, fn_style: ast::FnStyle, abi: abi::Abi, ident: Option, - sig: &ty::FnSig) + sig: &ty::FnSig, + print_var_ids: bool) -> String { let mut s = String::new(); match fn_style { @@ -288,12 +300,12 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { _ => { } } - push_sig_to_string(cx, &mut s, '(', ')', sig, ""); + push_sig_to_string(cx, &mut s, '(', ')', sig, "", print_var_ids); s } - fn closure_to_string(cx: &ctxt, cty: &ty::ClosureTy) -> String { + fn closure_to_string(cx: &ctxt, cty: &ty::ClosureTy, print_var_ids: bool) -> String { let mut s = String::new(); match cty.store { @@ -318,7 +330,7 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { assert_eq!(cty.onceness, ast::Once); s.push_str("proc"); push_sig_to_string(cx, &mut s, '(', ')', &cty.sig, - bounds_str.as_slice()); + bounds_str.as_slice(), print_var_ids); } ty::RegionTraitStore(..) => { match cty.onceness { @@ -326,7 +338,7 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { ast::Once => s.push_str("once ") } push_sig_to_string(cx, &mut s, '|', '|', &cty.sig, - bounds_str.as_slice()); + bounds_str.as_slice(), print_var_ids); } } @@ -338,9 +350,13 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { bra: char, ket: char, sig: &ty::FnSig, - bounds: &str) { + bounds: &str, + print_var_ids: bool) { s.push(bra); - let strs: Vec = sig.inputs.iter().map(|a| fn_input_to_string(cx, *a)).collect(); + let strs = sig.inputs + .iter() + .map(|a| ty_to_string_with_var_ids(cx, *a, print_var_ids)) + .collect::>(); s.push_str(strs.connect(", ").as_slice()); if sig.variadic { s.push_str(", ..."); @@ -355,8 +371,8 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { match sig.output { ty::FnConverging(t) => { if !ty::type_is_nil(t) { - s.push_str(" -> "); - s.push_str(ty_to_string(cx, t).as_slice()); + s.push_str(" -> "); + s.push_str(ty_to_string_with_var_ids(cx, t, print_var_ids).as_slice()); } } ty::FnDiverging => { @@ -365,82 +381,98 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { } } - // if there is an id, print that instead of the structural type: - /*for def_id in ty::type_def_id(typ).iter() { - // note that this typedef cannot have type parameters - return ty::item_path_str(cx, *def_id); - }*/ + fn infer_ty_to_string(ty: ty::InferTy, print_var_ids: bool) -> String { + match ty { + ty::TyVar(ty::TyVid { index: vid }) + | ty::IntVar(ty::IntVid { index: vid }) + | ty::FloatVar(ty::FloatVid { index: vid }) => { + match ty { + ty::TyVar(_) if print_var_ids => format!("_#{}", vid), + ty::TyVar(_) => "_".to_string(), + ty::IntVar(_) => format!("_#{}i", vid), + ty::FloatVar(_) => format!("_#{}f", vid), + _ => unreachable!() + } + } + ty::SkolemizedTy(v) => format!("SkolemizedTy({})", v), + ty::SkolemizedIntTy(v) => format!("SkolemizedIntTy({})", v), + } + } // pretty print the structural type representation: - return match ty::get(typ).sty { - ty_nil => "()".to_string(), - ty_bool => "bool".to_string(), - ty_char => "char".to_string(), - ty_int(t) => ast_util::int_ty_to_string(t, None).to_string(), - ty_uint(t) => ast_util::uint_ty_to_string(t, None).to_string(), - ty_float(t) => ast_util::float_ty_to_string(t).to_string(), - ty_uniq(typ) => format!("Box<{}>", ty_to_string(cx, typ)), - ty_ptr(ref tm) => { - format!("*{} {}", match tm.mutbl { - ast::MutMutable => "mut", - ast::MutImmutable => "const", - }, ty_to_string(cx, tm.ty)) - } - ty_rptr(r, ref tm) => { - let mut buf = region_ptr_to_string(cx, r); - buf.push_str(mt_to_string(cx, tm).as_slice()); - buf - } - ty_open(typ) => format!("opened<{}>", ty_to_string(cx, typ)), - ty_tup(ref elems) => { - let strs: Vec = elems.iter().map(|elem| ty_to_string(cx, *elem)).collect(); - format!("({})", strs.connect(",")) - } - ty_closure(ref f) => { - closure_to_string(cx, &**f) - } - ty_bare_fn(ref f) => { - bare_fn_to_string(cx, f.fn_style, f.abi, None, &f.sig) - } - ty_infer(infer_ty) => infer_ty.to_string(), - ty_err => "[type error]".to_string(), - ty_param(ref param_ty) => { - param_ty.repr(cx) - } - ty_enum(did, ref substs) | ty_struct(did, ref substs) => { - let base = ty::item_path_str(cx, did); - let generics = ty::lookup_item_type(cx, did).generics; - parameterized(cx, base.as_slice(), substs, &generics) - } - ty_trait(box ty::TyTrait { - def_id: did, ref substs, ref bounds - }) => { - let base = ty::item_path_str(cx, did); - let trait_def = ty::lookup_trait_def(cx, did); - let ty = parameterized(cx, base.as_slice(), - substs, &trait_def.generics); - let bound_str = bounds.user_string(cx); - let bound_sep = if bound_str.is_empty() { "" } else { "+" }; - format!("{}{}{}", - ty, - bound_sep, - bound_str) - } - ty_str => "str".to_string(), - ty_unboxed_closure(ref did, _, ref substs) => { - let unboxed_closures = cx.unboxed_closures.borrow(); - unboxed_closures.find(did).map(|cl| { - closure_to_string(cx, &cl.closure_type.subst(cx, substs)) - }).unwrap_or_else(|| "closure".to_string()) - } - ty_vec(t, sz) => { - match sz { - Some(n) => { - format!("[{}, ..{}]", ty_to_string(cx, t), n) - } - None => format!("[{}]", ty_to_string(cx, t)), - } - } + match ty::get(typ).sty { + ty_nil => "()".to_string(), + ty_bool => "bool".to_string(), + ty_char => "char".to_string(), + ty_int(t) => ast_util::int_ty_to_string(t, None).to_string(), + ty_uint(t) => ast_util::uint_ty_to_string(t, None).to_string(), + ty_float(t) => ast_util::float_ty_to_string(t).to_string(), + ty_uniq(typ) => format!("Box<{}>", ty_to_string_with_var_ids(cx, typ, print_var_ids)), + ty_ptr(ref tm) => { + format!("*{} {}", match tm.mutbl { + ast::MutMutable => "mut", + ast::MutImmutable => "const", + }, ty_to_string(cx, tm.ty)) + } + ty_rptr(r, ref tm) => { + let mut buf = region_ptr_to_string(cx, r); + buf.push_str(mt_to_string_with_var_ids(cx, tm, print_var_ids).as_slice()); + buf + } + ty_open(typ) => + format!("opened<{}>", ty_to_string_with_var_ids(cx, typ, print_var_ids)), + ty_tup(ref elems) => { + let strs = elems + .iter() + .map(|elem| ty_to_string_with_var_ids(cx, *elem, print_var_ids)) + .collect::>(); + match strs.as_slice() { + [ref string] => format!("({},)", string), + strs => format!("({})", strs.connect(", ")) + } + } + ty_closure(ref f) => { + closure_to_string(cx, &**f, print_var_ids) + } + ty_bare_fn(ref f) => { + bare_fn_to_string(cx, f.fn_style, f.abi, None, &f.sig, print_var_ids) + } + ty_infer(infer_ty) => infer_ty_to_string(infer_ty, print_var_ids), + ty_err => "[type error]".to_string(), + ty_param(ref param_ty) => param_ty.repr(cx), + ty_enum(did, ref substs) | ty_struct(did, ref substs) => { + let base = ty::item_path_str(cx, did); + let generics = ty::lookup_item_type(cx, did).generics; + parameterized(cx, base.as_slice(), substs, &generics, print_var_ids) + } + ty_trait(box ty::TyTrait { + def_id: did, ref substs, ref bounds + }) => { + let base = ty::item_path_str(cx, did); + let trait_def = ty::lookup_trait_def(cx, did); + let ty = parameterized(cx, base.as_slice(), + substs, &trait_def.generics, print_var_ids); + let bound_str = bounds.user_string(cx); + let bound_sep = if bound_str.is_empty() { "" } else { "+" }; + format!("{}{}{}", + ty, + bound_sep, + bound_str) + } + ty_str => "str".to_string(), + ty_unboxed_closure(ref did, _, ref substs) => { + let unboxed_closures = cx.unboxed_closures.borrow(); + unboxed_closures.find(did).map(|cl| { + closure_to_string(cx, &cl.closure_type.subst(cx, substs), print_var_ids) + }).unwrap_or_else(|| "closure".to_string()) + } + ty_vec(t, sz) => { + let inner_str = ty_to_string_with_var_ids(cx, t, print_var_ids); + match sz { + Some(n) => format!("[{}, ..{}]", inner_str, n), + None => format!("[{}]", inner_str), + } + } } } @@ -460,7 +492,8 @@ pub fn explicit_self_category_to_str(category: &ty::ExplicitSelfCategory) pub fn parameterized(cx: &ctxt, base: &str, substs: &subst::Substs, - generics: &ty::Generics) + generics: &ty::Generics, + print_var_ids: bool) -> String { let mut strs = Vec::new(); @@ -470,15 +503,15 @@ pub fn parameterized(cx: &ctxt, subst::NonerasedRegions(ref regions) => { for &r in regions.iter() { let s = region_to_string(cx, "", false, r); - if !s.is_empty() { - strs.push(s) - } else { + if s.is_empty() { // This happens when the value of the region // parameter is not easily serialized. This may be // because the user omitted it in the first place, // or because it refers to some block in the code, // etc. I'm not sure how best to serialize this. strs.push(format!("'_")); + } else { + strs.push(s) } } } @@ -499,7 +532,7 @@ pub fn parameterized(cx: &ctxt, }; for t in tps[..tps.len() - num_defaults].iter() { - strs.push(ty_to_string(cx, *t)) + strs.push(ty_to_string_with_var_ids(cx, *t, print_var_ids)) } if cx.sess.verbose() { @@ -515,7 +548,7 @@ pub fn parameterized(cx: &ctxt, } if strs.len() > 0u { - format!("{}<{}>", base, strs.connect(",")) + format!("{}<{}>", base, strs.connect(", ")) } else { format!("{}", base) } @@ -710,7 +743,7 @@ impl Repr for ty::TraitRef { let trait_def = ty::lookup_trait_def(tcx, self.def_id); format!("<{} as {}>", self.substs.self_ty().repr(tcx), - parameterized(tcx, base.as_slice(), &self.substs, &trait_def.generics)) + parameterized(tcx, base.as_slice(), &self.substs, &trait_def.generics, false)) } } @@ -1095,9 +1128,12 @@ impl UserString for ty::BuiltinBounds { impl UserString for ty::TraitRef { fn user_string(&self, tcx: &ctxt) -> String { + self.user_string_with_var_ids(tcx, false) + } + fn user_string_with_var_ids(&self, tcx: &ctxt, print_var_ids: bool) -> String { let base = ty::item_path_str(tcx, self.def_id); let trait_def = ty::lookup_trait_def(tcx, self.def_id); - parameterized(tcx, base.as_slice(), &self.substs, &trait_def.generics) + parameterized(tcx, base.as_slice(), &self.substs, &trait_def.generics, print_var_ids) } } @@ -1105,6 +1141,9 @@ impl UserString for ty::t { fn user_string(&self, tcx: &ctxt) -> String { ty_to_string(tcx, *self) } + fn user_string_with_var_ids(&self, tcx: &ctxt, print_var_ids: bool) -> String { + ty_to_string_with_var_ids(tcx, *self, print_var_ids) + } } impl UserString for ast::Ident {