about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorTim Chevalier <chevalier@alum.wellesley.edu>2012-11-16 19:22:48 -0800
committerTim Chevalier <chevalier@alum.wellesley.edu>2012-11-21 12:44:51 -0800
commitef833d41014481acdfc19bfd50f27f662dba8a2b (patch)
tree33d854748f777d562f64c94d45b0b79f6ff1945c /src
parent77ef4e717667ddbe925a81efc85994662183a445 (diff)
downloadrust-ef833d41014481acdfc19bfd50f27f662dba8a2b.tar.gz
rust-ef833d41014481acdfc19bfd50f27f662dba8a2b.zip
Introduce a T_err type for type errors
This allows more errors to be non-fatal, as per #1871.

I only went through and started changing span_fatal to span_err in
check.rs. There are probably more errors that could be made
non-fatal. So if you see derived type errors appearing from now on,
file a bug!

r=graydon

Closes #1871
Diffstat (limited to 'src')
-rw-r--r--src/librustc/metadata/tyencode.rs1
-rw-r--r--src/librustc/middle/trans/reflect.rs1
-rw-r--r--src/librustc/middle/trans/type_of.rs1
-rw-r--r--src/librustc/middle/ty.rs42
-rw-r--r--src/librustc/middle/typeck/check.rs287
-rw-r--r--src/librustc/middle/typeck/check/method.rs2
-rw-r--r--src/librustc/middle/typeck/check/vtable.rs8
-rw-r--r--src/librustc/middle/typeck/coherence.rs4
-rw-r--r--src/librustc/middle/typeck/infer.rs38
-rw-r--r--src/librustc/util/ppaux.rs3
-rw-r--r--src/test/compile-fail/cast-from-nil.rs2
-rw-r--r--src/test/compile-fail/cast-to-nil.rs2
-rw-r--r--src/test/compile-fail/extern-no-call.rs2
-rw-r--r--src/test/compile-fail/issue-1871.rs7
-rw-r--r--src/test/compile-fail/issue-2149.rs2
15 files changed, 256 insertions, 146 deletions
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index 922fff18e9e..232fb7c3b3d 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -318,6 +318,7 @@ fn enc_sty(w: io::Writer, cx: @ctxt, st: ty::sty) {
           debug!("~~~~ %s", ~"]");
           w.write_char(']');
       }
+      ty::ty_err => fail ~"Shouldn't encode error type"
     }
 }
 
diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs
index 5724c5ada22..5c4380ffe85 100644
--- a/src/librustc/middle/trans/reflect.rs
+++ b/src/librustc/middle/trans/reflect.rs
@@ -266,6 +266,7 @@ impl reflector {
           // Miscallaneous extra types
           ty::ty_trait(_, _, _) => self.leaf(~"trait"),
           ty::ty_infer(_) => self.leaf(~"infer"),
+          ty::ty_err => self.leaf(~"err"),
           ty::ty_param(p) => self.visit(~"param", ~[self.c_uint(p.idx)]),
           ty::ty_self => self.leaf(~"self"),
           ty::ty_type => self.leaf(~"type"),
diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs
index 7b5a912ed7f..604bad2312f 100644
--- a/src/librustc/middle/trans/type_of.rs
+++ b/src/librustc/middle/trans/type_of.rs
@@ -179,6 +179,7 @@ fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef {
       ty::ty_self => cx.tcx.sess.unimpl(~"type_of: ty_self"),
       ty::ty_infer(*) => cx.tcx.sess.bug(~"type_of with ty_infer"),
       ty::ty_param(*) => cx.tcx.sess.bug(~"type_of with ty_param"),
+      ty::ty_err(*) => cx.tcx.sess.bug(~"type_of with ty_err")
     };
 
     cx.lltypes.insert(t, llty);
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index fe8df3e2ada..cb567028ac9 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -57,7 +57,7 @@ export lookup_item_type;
 export lookup_public_fields;
 export method;
 export method_idx;
-export mk_class;
+export mk_class, mk_err;
 export mk_ctxt;
 export mk_with_id, type_def_id;
 export mt;
@@ -87,6 +87,7 @@ export ty_fn_proto, ty_fn_purity, ty_fn_ret, ty_fn_ret_style, tys_in_fn_ty;
 export ty_int, mk_int, mk_mach_int, mk_char;
 export mk_i8, mk_u8, mk_i16, mk_u16, mk_i32, mk_u32, mk_i64, mk_u64;
 export mk_f32, mk_f64;
+export ty_err;
 export ty_estr, mk_estr, type_is_str;
 export ty_evec, mk_evec, type_is_vec;
 export ty_unboxed_vec, mk_unboxed_vec, mk_mut_unboxed_vec;
@@ -127,7 +128,7 @@ export kind_is_owned;
 export meta_kind, kind_lteq, type_kind;
 export operators;
 export type_err, terr_vstore_kind;
-export terr_onceness_mismatch;
+export terr_mismatch, terr_onceness_mismatch;
 export type_err_to_str, note_and_explain_type_err;
 export expected_found;
 export type_needs_drop;
@@ -673,6 +674,9 @@ enum sty {
     ty_self, // special, implicit `self` type parameter
 
     ty_infer(InferTy), // soething used only during inference/typeck
+    ty_err, // Also only used during inference/typeck, to represent
+            // the type of an erroneous expression (helps cut down
+            // on non-useful type error messages)
 
     // "Fake" types, used for trans purposes
     ty_type, // type_desc*
@@ -1062,7 +1066,7 @@ fn mk_t_with_id(cx: ctxt, +st: sty, o_def_id: Option<ast::def_id>) -> t {
       }
       ty_nil | ty_bot | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) |
       ty_estr(_) | ty_type | ty_opaque_closure_ptr(_) |
-      ty_opaque_box => (),
+      ty_opaque_box | ty_err => (),
       ty_param(_) => flags |= has_params as uint,
       ty_infer(_) => flags |= needs_infer as uint,
       ty_self => flags |= has_self as uint,
@@ -1094,6 +1098,8 @@ fn mk_t_with_id(cx: ctxt, +st: sty, o_def_id: Option<ast::def_id>) -> t {
 
 fn mk_nil(cx: ctxt) -> t { mk_t(cx, ty_nil) }
 
+fn mk_err(cx: ctxt) -> t { mk_t(cx, ty_err) }
+
 fn mk_bot(cx: ctxt) -> t { mk_t(cx, ty_bot) }
 
 fn mk_bool(cx: ctxt) -> t { mk_t(cx, ty_bool) }
@@ -1301,7 +1307,7 @@ fn maybe_walk_ty(ty: t, f: fn(t) -> bool) {
     match get(ty).sty {
       ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
       ty_estr(_) | ty_type | ty_opaque_box | ty_self |
-      ty_opaque_closure_ptr(_) | ty_infer(_) | ty_param(_) => {
+      ty_opaque_closure_ptr(_) | ty_infer(_) | ty_param(_) | ty_err => {
       }
       ty_box(tm) | ty_evec(tm, _) | ty_unboxed_vec(tm) |
       ty_ptr(tm) | ty_rptr(_, tm) => {
@@ -1386,7 +1392,7 @@ fn fold_sty(sty: &sty, fldop: fn(t) -> t) -> sty {
             ty_class(did, fold_substs(substs, fldop))
         }
         ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
-        ty_estr(_) | ty_type | ty_opaque_closure_ptr(_) |
+        ty_estr(_) | ty_type | ty_opaque_closure_ptr(_) | ty_err |
         ty_opaque_box | ty_infer(_) | ty_param(*) | ty_self => {
             *sty
         }
@@ -1794,7 +1800,7 @@ fn type_needs_drop(cx: ctxt, ty: t) -> bool {
       ty_trait(_, _, vstore_fixed(_)) |
       ty_trait(_, _, vstore_slice(_)) => false,
 
-      ty_param(*) | ty_infer(*) => true,
+      ty_param(*) | ty_infer(*) | ty_err => true,
 
       ty_evec(mt, vstore_fixed(_)) => type_needs_drop(cx, mt.ty),
       ty_unboxed_vec(mt) => type_needs_drop(cx, mt.ty),
@@ -2270,7 +2276,7 @@ fn type_kind(cx: ctxt, ty: t) -> Kind {
         cx.sess.bug(~"Asked to compute kind of a type variable");
       }
       ty_type | ty_opaque_closure_ptr(_)
-      | ty_opaque_box | ty_unboxed_vec(_) => {
+      | ty_opaque_box | ty_unboxed_vec(_) | ty_err => {
         cx.sess.bug(~"Asked to compute kind of fictitious type");
       }
     };
@@ -2341,7 +2347,7 @@ fn type_size(cx: ctxt, ty: t) -> uint {
         cx.sess.bug(~"Asked to compute kind of a type variable");
       }
       ty_type | ty_opaque_closure_ptr(_)
-      | ty_opaque_box | ty_unboxed_vec(_) => {
+      | ty_opaque_box | ty_unboxed_vec(_) | ty_err => {
         cx.sess.bug(~"Asked to compute kind of fictitious type");
       }
     }
@@ -2384,6 +2390,7 @@ fn is_instantiable(cx: ctxt, r_ty: t) -> bool {
           ty_estr(_) |
           ty_fn(_) |
           ty_infer(_) |
+          ty_err |
           ty_param(_) |
           ty_self |
           ty_type |
@@ -2589,7 +2596,7 @@ fn type_is_pod(cx: ctxt, ty: t) -> bool {
         result = false;
       }
 
-      ty_infer(*) | ty_self(*) => {
+      ty_infer(*) | ty_self(*) | ty_err => {
         cx.sess.bug(~"non concrete type in type_is_pod");
       }
     }
@@ -2862,6 +2869,8 @@ impl sty : to_bytes::IterBytes {
 
           ty_rptr(ref r, ref mt) =>
           to_bytes::iter_bytes_3(&24u8, r, mt, lsb0, f),
+
+          ty_err => 25u8.iter_bytes(lsb0, f)
         }
     }
 }
@@ -3357,7 +3366,8 @@ fn ty_sort_str(cx: ctxt, t: t) -> ~str {
       ty_infer(IntVar(_)) => ~"integral variable",
       ty_infer(FloatVar(_)) => ~"floating-point variable",
       ty_param(_) => ~"type parameter",
-      ty_self => ~"self"
+      ty_self => ~"self",
+      ty_err => ~"type error"
     }
 }
 
@@ -4787,6 +4797,12 @@ impl sty : cmp::Eq {
                     _ => false
                 }
             }
+            ty_err => {
+                match (*other) {
+                    ty_err => true,
+                    _ => false
+                }
+            }
             ty_param(e0a) => {
                 match (*other) {
                     ty_param(e0b) => e0a == e0b,
@@ -4944,6 +4960,12 @@ impl sty : cmp::Eq {
                     _ => false
                 }
             }
+            ty_err => {
+                match (*other) {
+                    ty_err => true,
+                    _ => false
+                }
+            }
             ty_param(e0a) => {
                 match (*other) {
                     ty_param(e0b) => e0a == e0b,
diff --git a/src/librustc/middle/typeck/check.rs b/src/librustc/middle/typeck/check.rs
index a733d8e6de9..5a45e13a16f 100644
--- a/src/librustc/middle/typeck/check.rs
+++ b/src/librustc/middle/typeck/check.rs
@@ -702,16 +702,6 @@ impl @fn_ctxt {
         self.inh.node_type_substs.find(id)
     }
 
-    fn report_mismatched_types(sp: span, e: ty::t, a: ty::t,
-                               err: &ty::type_err) {
-        self.ccx.tcx.sess.span_err(
-            sp,
-            fmt!("mismatched types: expected `%s` but found `%s` (%s)",
-                 self.infcx().ty_to_str(e),
-                 self.infcx().ty_to_str(a),
-                 ty::type_err_to_str(self.ccx.tcx, err)));
-        ty::note_and_explain_type_err(self.ccx.tcx, err);
-    }
 
     fn mk_subty(a_is_expected: bool, span: span,
                 sub: ty::t, sup: ty::t) -> Result<(), ty::type_err> {
@@ -775,6 +765,17 @@ impl @fn_ctxt {
         rp.map(
             |_rp| self.infcx().next_region_var_with_lb(span, lower_bound))
     }
+
+    fn type_error_message(sp: span, mk_msg: fn(~str) -> ~str,
+                          actual_ty: ty::t, err: Option<&ty::type_err>) {
+        self.infcx().type_error_message(sp, mk_msg, actual_ty, err);
+    }
+
+    fn report_mismatched_types(sp: span, e: ty::t, a: ty::t,
+                               err: &ty::type_err) {
+        self.infcx().report_mismatched_types(sp, e, a, err);
+    }
+
 }
 
 fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> (ty::t, uint) {
@@ -979,6 +980,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         args: ~[@ast::expr],
         deref_args: DerefArgs) -> {fty: ty::t, bot: bool} {
 
+        let tcx = fcx.ccx.tcx;
         let mut bot = false;
 
         // Replace all region parameters in the arguments and return
@@ -987,58 +989,60 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         debug!("check_call_inner: before universal quant., in_fty=%s",
                fcx.infcx().ty_to_str(in_fty));
 
+        let mut formal_tys;
+
         // This is subtle: we expect `fty` to be a function type, which
         // normally introduce a level of binding.  In this case, we want to
         // process the types bound by the function but not by any nested
         // functions.  Therefore, we match one level of structure.
-        let fn_ty =
+        let fty =
             match structure_of(fcx, sp, in_fty) {
               ty::ty_fn(ref fn_ty) => {
-                replace_bound_regions_in_fn_ty(
-                    fcx.ccx.tcx, @Nil, None, fn_ty,
-                    |_br| fcx.infcx().next_region_var(sp,
-                                                      call_expr_id)).fn_ty
+                  let fn_ty = replace_bound_regions_in_fn_ty(tcx, @Nil,
+                      None, fn_ty, |_br| fcx.infcx().next_region_var(sp,
+                                                      call_expr_id)).fn_ty;
+
+                  let supplied_arg_count = args.len();
+
+                  // Grab the argument types, supplying fresh type variables
+                  // if the wrong number of arguments were supplied
+                  let expected_arg_count = fn_ty.sig.inputs.len();
+                  formal_tys = if expected_arg_count == supplied_arg_count {
+                      fn_ty.sig.inputs.map(|a| a.ty)
+                  } else {
+                      tcx.sess.span_err(
+                          sp, fmt!("this function takes %u parameter%s but \
+                                    %u parameter%s supplied",
+                                   expected_arg_count,
+                                   if expected_arg_count == 1 {
+                                       ~""
+                                   } else {
+                                       ~"s"
+                                   },
+                                   supplied_arg_count,
+                                   if supplied_arg_count == 1 {
+                                       ~" was"
+                                   } else {
+                                       ~"s were"
+                                   }));
+                      fcx.infcx().next_ty_vars(supplied_arg_count)
+                  };
+                  ty::mk_fn(tcx, fn_ty)
               }
               _ => {
-                // I would like to make this span_err, but it's
-                // really hard due to the way that expr_bind() is
-                // written.
-                fcx.ccx.tcx.sess.span_fatal(sp, ~"mismatched types: \
-                                            expected function or foreign \
-                                            function but found "
-                                            + fcx.infcx().ty_to_str(in_fty));
+                  fcx.type_error_message(sp, |actual| {
+                      fmt!("expected function or foreign function but \
+                            found `%s`", actual) }, in_fty, None);
+                  // check each arg against "error", in order to set up
+                  // all the node type bindings
+                  formal_tys = args.map(|_x| ty::mk_err(tcx));
+                  ty::mk_err(tcx)
               }
             };
 
-        let fty = ty::mk_fn(fcx.tcx(), fn_ty);
         debug!("check_call_inner: after universal quant., fty=%s",
                fcx.infcx().ty_to_str(fty));
 
-        let supplied_arg_count = args.len();
-
-        // Grab the argument types, supplying fresh type variables
-        // if the wrong number of arguments were supplied
-        let expected_arg_count = fn_ty.sig.inputs.len();
-        let formal_tys = if expected_arg_count == supplied_arg_count {
-            fn_ty.sig.inputs.map(|a| a.ty)
-        } else {
-            fcx.ccx.tcx.sess.span_err(
-                sp, fmt!("this function takes %u parameter%s but %u \
-                          parameter%s supplied", expected_arg_count,
-                         if expected_arg_count == 1u {
-                             ~""
-                         } else {
-                             ~"s"
-                         },
-                         supplied_arg_count,
-                         if supplied_arg_count == 1u {
-                             ~" was"
-                         } else {
-                             ~"s were"
-                         }));
-            fcx.infcx().next_ty_vars(supplied_arg_count)
-        };
-
         // Check the arguments.
         // We do this in a pretty awful way: first we typecheck any arguments
         // that are not anonymous functions, then we typecheck the anonymous
@@ -1129,11 +1133,16 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         // Pull the return type out of the type of the function.
         match structure_of(fcx, sp, fty) {
           ty::ty_fn(ref f) => {
-            bot |= (f.meta.ret_style == ast::noreturn);
-            fcx.write_ty(call_expr_id, f.sig.output);
-            return bot;
+              bot |= (f.meta.ret_style == ast::noreturn);
+              fcx.write_ty(call_expr_id, f.sig.output);
+              return bot;
+          }
+          _ => {
+              fcx.write_ty(call_expr_id, ty::mk_err(fcx.ccx.tcx));
+              fcx.type_error_message(sp, |_actual| {
+                  ~"expected function"}, fty, None);
+              return bot;
           }
-          _ => fcx.ccx.tcx.sess.span_fatal(sp, ~"calling non-function")
         }
     }
 
@@ -1239,8 +1248,15 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
             };
         }
 
+        // A hack, but this prevents multiple errors for the same code
+        // (since check_user_binop calls structurally_resolve_type)
         let (result, rhs_bot) =
-            check_user_binop(fcx, expr, lhs, lhs_t, op, rhs);
+           match ty::deref(fcx.tcx(), lhs_t, false).map(
+                      |tt| structurally_resolved_type(fcx,
+                                                      expr.span, tt.ty)) {
+                Some(t) if ty::get(t).sty == ty::ty_err => (t, false),
+                _ => check_user_binop(fcx, expr, lhs, lhs_t, op, rhs)
+           };
         fcx.write_ty(expr.id, result);
         return lhs_bot | rhs_bot;
     }
@@ -1262,12 +1278,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
           _ => ()
         }
         check_expr(fcx, rhs, None);
-
-        tcx.sess.span_err(
-            ex.span, ~"binary operation " + ast_util::binop_to_str(op) +
-            ~" cannot be applied to type `" +
-            fcx.infcx().ty_to_str(lhs_resolved_t) +
-            ~"`");
+        fcx.type_error_message(ex.span,
+           |actual| {
+               fmt!("binary operation %s cannot be applied to type `%s`",
+                    ast_util::binop_to_str(op), actual)
+           },
+           lhs_resolved_t, None);
 
         // If the or operator is used it might be that the user forgot to
         // supply the do keyword.  Let's be more helpful in that situation.
@@ -1292,10 +1308,11 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                                DontDerefArgs) {
           Some((ret_ty, _)) => ret_ty,
           _ => {
-            fcx.ccx.tcx.sess.span_err(
-                ex.span, fmt!("cannot apply unary operator `%s` to type `%s`",
-                              op_str, fcx.infcx().ty_to_str(rhs_t)));
-            rhs_t
+              fcx.type_error_message(ex.span, |actual| {
+                  fmt!("cannot apply unary operator `%s` to type `%s`",
+                              op_str, actual)
+              }, rhs_t, None);
+              rhs_t
           }
         }
     }
@@ -1454,17 +1471,15 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                 }
             }
             None => {
-                let t_err =
-                    fcx.infcx().resolve_type_vars_if_possible(expr_t);
-                let msg =
-                    fmt!(
-                        "attempted access of field `%s` on type `%s`, \
-                         but no field or method with that name was found",
-                        tcx.sess.str_of(field),
-                        fcx.infcx().ty_to_str(t_err));
-                tcx.sess.span_err(expr.span, msg);
-                // NB: Add bogus type to allow typechecking to continue
-                fcx.write_ty(expr.id, fcx.infcx().next_ty_var());
+                fcx.type_error_message(expr.span,
+                  |actual| {
+                      fmt!("attempted access of field `%s` on type `%s`, but \
+                            no field or method with that name was found",
+                           tcx.sess.str_of(field), actual)
+                  },
+                  expr_t, None);
+                // Add error type for the result
+                fcx.write_ty(expr.id, ty::mk_err(tcx));
             }
         }
 
@@ -1802,10 +1817,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                           field");
                   }
                   _ => {
-                    tcx.sess.span_err(
-                        expr.span,
-                        fmt!("type %s cannot be dereferenced",
-                             fcx.infcx().ty_to_str(oprnd_t)));
+                      fcx.type_error_message(expr.span, |actual| {
+                          fmt!("type %s cannot be dereferenced", actual)
+                      }, oprnd_t, None);
                   }
                 }
               }
@@ -1958,7 +1972,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         // appear in the context of a call, so we get the expected type of the
         // parameter. The catch here is that we need to validate two things:
         // 1. a closure that returns a bool is expected
-        // 2. the cloure that was given returns unit
+        // 2. the closure that was given returns unit
         let expected_sty = unpack_expected(fcx, expected, |x| Some(x));
         let inner_ty = match expected_sty {
           Some(ty::ty_fn(fty)) => {
@@ -1966,10 +1980,14 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                                fty.sig.output, ty::mk_bool(tcx)) {
               result::Ok(_) => (),
               result::Err(_) => {
-                tcx.sess.span_fatal(
-                    expr.span, fmt!("a `loop` function's last argument \
-                                     should return `bool`, not `%s`",
-                                    fcx.infcx().ty_to_str(fty.sig.output)));
+                   fcx.type_error_message(expr.span,
+                      |actual| {
+                          fmt!("a `loop` function's last argument \
+                                should return `bool`, not `%s`", actual)
+                      },
+                      fty.sig.output, None);
+                fcx.write_ty(id, ty::mk_err(tcx));
+                return true;
               }
             }
             ty::mk_fn(tcx, FnTyBase {
@@ -1978,11 +1996,22 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                             ..fty.sig}
             })
           }
-          _ => {
-            tcx.sess.span_fatal(expr.span, ~"a `loop` function's last \
-                                            argument should be of function \
-                                            type");
-          }
+          _ =>
+              match expected {
+                  Some(expected_t) => {
+                      fcx.type_error_message(expr.span, |actual| {
+                          fmt!("a `loop` function's last \
+                                argument should be of function \
+                                type, not `%s`",
+                               actual)
+                      },
+                                             expected_t, None);
+                      fcx.write_ty(id, ty::mk_err(tcx));
+                      return true;
+                  }
+                  None => fcx.tcx().sess.impossible_case(expr.span,
+                            ~"loop body must have an expected type")
+              }
         };
         match b.node {
           ast::expr_fn_block(decl, body, cap_clause) => {
@@ -2012,13 +2041,21 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         let expected_sty = unpack_expected(fcx, expected, |x| Some(x));
         let inner_ty = match expected_sty {
           Some(ty::ty_fn(fty)) => {
-            ty::mk_fn(tcx, fty)
-          }
-          _ => {
-            tcx.sess.span_fatal(expr.span, ~"Non-function passed to a `do` \
-              function as its last argument, or wrong number of arguments \
-              passed to a `do` function");
+              ty::mk_fn(tcx, fty)
           }
+          _ => match expected {
+                  Some(expected_t) => {
+                      fcx.type_error_message(expr.span, |_actual| {
+                          ~"Non-function passed to a `do` \
+                            function as its last argument, or wrong number \
+                            of arguments passed to a `do` function"
+                      }, expected_t, None);
+                      fcx.write_ty(id, ty::mk_err(tcx));
+                      return true;
+                  }
+                  None => fcx.tcx().sess.impossible_case(expr.span,
+                              ~"do body must have expected type")
+              }
         };
         match b.node {
           ast::expr_fn_block(decl, body, cap_clause) => {
@@ -2067,13 +2104,15 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
 
           _ => {
             if ty::type_is_nil(t_e) {
-                tcx.sess.span_err(expr.span, ~"cast from nil: " +
-                                  fcx.infcx().ty_to_str(t_e) + ~" as " +
-                                  fcx.infcx().ty_to_str(t_1));
+                fcx.type_error_message(expr.span, |actual| {
+                    fmt!("cast from nil: `%s` as `%s`", actual,
+                         fcx.infcx().ty_to_str(t_1))
+                }, t_e, None);
             } else if ty::type_is_nil(t_1) {
-                tcx.sess.span_err(expr.span, ~"cast to nil: " +
-                                  fcx.infcx().ty_to_str(t_e) + ~" as " +
-                                  fcx.infcx().ty_to_str(t_1));
+                fcx.type_error_message(expr.span, |actual| {
+                    fmt!("cast to nil: `%s` as `%s`", actual,
+                         fcx.infcx().ty_to_str(t_1))
+                }, t_e, None);
             }
 
             let t_1_is_scalar = type_is_scalar(fcx, expr.span, t_1);
@@ -2085,10 +2124,10 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                 supported here, then file an enhancement issue and record the
                 issue number in this comment.
                 */
-                tcx.sess.span_err(expr.span,
-                                  ~"non-scalar cast: " +
-                                  fcx.infcx().ty_to_str(t_e) + ~" as " +
-                                  fcx.infcx().ty_to_str(t_1));
+                fcx.type_error_message(expr.span, |actual| {
+                    fmt!("non-scalar cast: `%s` as `%s`", actual,
+                         fcx.infcx().ty_to_str(t_1))
+                }, t_e, None);
             }
           }
         }
@@ -2157,8 +2196,11 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
             let base_fields =  match structure_of(fcx, expr.span, bexpr_t) {
               ty::ty_rec(flds) => flds,
               _ => {
-                tcx.sess.span_fatal(expr.span,
-                                    ~"record update has non-record base");
+                  fcx.type_error_message(expr.span, |_actual| {
+                      ~"record update has non-record base"
+                  }, bexpr_t, None);
+                fcx.write_ty(id, ty::mk_err(tcx));
+                return true;
               }
             };
             fcx.write_ty(id, bexpr_t);
@@ -2171,9 +2213,11 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                     }
                 }
                 if !found {
-                    tcx.sess.span_fatal(f.span,
+                    tcx.sess.span_err(f.span,
                                         ~"unknown field in record update: " +
                                         tcx.sess.str_of(f.node.ident));
+                    fcx.write_ty(id, ty::mk_err(tcx));
+                    return true;
                 }
             }
           }
@@ -2220,9 +2264,11 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                                          ~[idx], DontDerefArgs) {
                       Some((ret_ty, _)) => fcx.write_ty(id, ret_ty),
                       _ => {
-                          tcx.sess.span_fatal(
-                              expr.span, ~"cannot index a value of type `" +
-                              fcx.infcx().ty_to_str(base_t) + ~"`");
+                          fcx.type_error_message(expr.span, |actual|
+                              fmt!("cannot index a value of type `%s`",
+                                   actual), base_t, None);
+                          fcx.write_ty(id, ty::mk_err(tcx));
+                          return true;
                       }
                   }
               }
@@ -2247,9 +2293,10 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
 
 fn require_integral(fcx: @fn_ctxt, sp: span, t: ty::t) {
     if !type_is_integral(fcx, sp, t) {
-        fcx.ccx.tcx.sess.span_err(sp, ~"mismatched types: expected \
-                                       integral type but found `"
-                                  + fcx.infcx().ty_to_str(t) + ~"`");
+        fcx.type_error_message(sp, |actual| {
+            fmt!("mismatched types: expected integral type but found `%s`",
+                 actual)
+        }, t, None);
     }
 }
 
@@ -2403,8 +2450,8 @@ fn check_instantiable(tcx: ty::ctxt,
     let item_ty = ty::node_id_to_type(tcx, item_id);
     if !ty::is_instantiable(tcx, item_ty) {
         tcx.sess.span_err(sp, fmt!("this type cannot be instantiated \
-                                    without an instance of itself; \
-                                    consider using `option<%s>`",
+                  without an instance of itself; \
+                  consider using `option<%s>`",
                                    ty_to_str(tcx, item_ty)));
     }
 }
@@ -2507,8 +2554,8 @@ fn check_enum_variants(ccx: @crate_ctxt,
         }
     }) {
         ccx.tcx.sess.span_err(sp, ~"illegal recursive enum type; \
-                                   wrap the inner value in a box to \
-                                   make it representable");
+                                 wrap the inner value in a box to \
+                                 make it representable");
     }
 
     // Check that it is possible to instantiate this enum:
@@ -2657,8 +2704,10 @@ fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t {
     match infer::resolve_type(fcx.infcx(), tp, force_tvar) {
         Ok(t_s) if !ty::type_is_ty_var(t_s) => return t_s,
         _ => {
-            fcx.ccx.tcx.sess.span_fatal
-                (sp, ~"the type of this value must be known in this context");
+            fcx.type_error_message(sp, |_actual| {
+                ~"the type of this value must be known in this context"
+            }, tp, None);
+            return ty::mk_err(fcx.tcx());
         }
     }
 }
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index fdcbf8a53e3..78cd6777257 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -697,6 +697,8 @@ impl LookupContext {
                     |m,r| ty::mk_rptr(tcx, r, {ty:self_ty, mutbl:m}))
             }
 
+            ty_err => None,
+
             ty_opaque_closure_ptr(_) | ty_unboxed_vec(_) |
             ty_opaque_box | ty_type | ty_infer(TyVar(_)) => {
                 self.bug(fmt!("Unexpected type: %s",
diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs
index 2c3666419b2..7de2de708cc 100644
--- a/src/librustc/middle/typeck/check/vtable.rs
+++ b/src/librustc/middle/typeck/check/vtable.rs
@@ -476,13 +476,7 @@ fn demand_suptype(vcx: &VtableContext, sp: span, e: ty::t, a: ty::t) {
     match infer::mk_subty(vcx.infcx, false, sp, a, e) {
         result::Ok(()) => {} // Ok.
         result::Err(ref err) => {
-            vcx.tcx().sess.span_err(
-                sp,
-                fmt!("mismatched types: expected `%s` but found `%s` (%s)",
-                     vcx.infcx.ty_to_str(e),
-                     vcx.infcx.ty_to_str(a),
-                     ty::type_err_to_str(vcx.tcx(), err)));
-            ty::note_and_explain_type_err(vcx.tcx(), err);
+            vcx.infcx.report_mismatched_types(sp, e, a, err);
         }
     }
 }
diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs
index 9fc7814422f..97968e11ffa 100644
--- a/src/librustc/middle/typeck/coherence.rs
+++ b/src/librustc/middle/typeck/coherence.rs
@@ -13,7 +13,7 @@ use middle::ty::{DerivedMethodInfo, ProvidedMethodSource, get};
 use middle::ty::{lookup_item_type, subst, t, ty_bot, ty_box, ty_class};
 use middle::ty::{ty_bool, ty_enum, ty_int, ty_nil, ty_ptr, ty_rptr, ty_uint};
 use middle::ty::{ty_float, ty_estr, ty_evec, ty_rec, ty_uniq};
-use middle::ty::{ty_fn, ty_trait, ty_tup, ty_infer};
+use middle::ty::{ty_err, ty_fn, ty_trait, ty_tup, ty_infer};
 use middle::ty::{ty_param, ty_self, ty_type, ty_opaque_box};
 use middle::ty::{ty_opaque_closure_ptr, ty_unboxed_vec, type_is_ty_var};
 use middle::typeck::infer::{infer_ctxt, can_mk_subty};
@@ -76,7 +76,7 @@ fn get_base_type(inference_context: infer_ctxt, span: span, original_type: t)
         ty_estr(*) | ty_evec(*) | ty_rec(*) |
         ty_fn(*) | ty_tup(*) | ty_infer(*) |
         ty_param(*) | ty_self | ty_type | ty_opaque_box |
-        ty_opaque_closure_ptr(*) | ty_unboxed_vec(*) => {
+        ty_opaque_closure_ptr(*) | ty_unboxed_vec(*) | ty_err => {
             debug!("(getting base type) no base type; found %?",
                    get(original_type).sty);
             None
diff --git a/src/librustc/middle/typeck/infer.rs b/src/librustc/middle/typeck/infer.rs
index edb6452ab50..56befd97342 100644
--- a/src/librustc/middle/typeck/infer.rs
+++ b/src/librustc/middle/typeck/infer.rs
@@ -684,5 +684,43 @@ impl infer_ctxt {
           result::Err(_) => typ
         }
     }
+
+    fn type_error_message(sp: span, mk_msg: fn(~str) -> ~str,
+                          actual_ty: ty::t, err: Option<&ty::type_err>) {
+        let actual_ty = self.resolve_type_vars_if_possible(actual_ty);
+
+        // Don't report an error if actual type is ty_err.
+        match ty::get(actual_ty).sty {
+            ty::ty_err => return,
+            _           => ()
+        }
+        let error_str = err.map_default(~"", |t_err|
+                         fmt!(" (%s)",
+                              ty::type_err_to_str(self.tcx, *t_err)));
+        self.tcx.sess.span_err(sp,
+           fmt!("%s%s", mk_msg(self.ty_to_str(actual_ty)),
+                error_str));
+        err.iter(|err|
+             ty::note_and_explain_type_err(self.tcx, *err));
+    }
+
+    fn report_mismatched_types(sp: span, e: ty::t, a: ty::t,
+                               err: &ty::type_err) {
+        // Don't report an error if expected is ty_err
+        let resolved_expected =
+            self.resolve_type_vars_if_possible(e);
+        let mk_msg = match ty::get(resolved_expected).sty {
+            ty::ty_err => return,
+            _ => {
+                // if I leave out : ~str, it infers &str and complains
+                |actual: ~str| {
+                    fmt!("mismatched types: expected `%s` but found `%s`",
+                         self.ty_to_str(resolved_expected), actual)
+                }
+            }
+        };
+        self.type_error_message(sp, mk_msg, a, Some(err));
+    }
+
 }
 
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 198b26c4ecc..57c8f79b78a 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -9,7 +9,7 @@ use middle::ty::{mt, t, param_bound};
 use middle::ty::{re_bound, re_free, re_scope, re_infer, re_static, Region};
 use middle::ty::{ReSkolemized, ReVar};
 use middle::ty::{ty_bool, ty_bot, ty_box, ty_class, ty_enum};
-use middle::ty::{ty_estr, ty_evec, ty_float, ty_fn, ty_trait, ty_int};
+use middle::ty::{ty_err, ty_estr, ty_evec, ty_float, ty_fn, ty_trait, ty_int};
 use middle::ty::{ty_nil, ty_opaque_box, ty_opaque_closure_ptr, ty_param};
 use middle::ty::{ty_ptr, ty_rec, ty_rptr, ty_self, ty_tup};
 use middle::ty::{ty_type, ty_uniq, ty_uint, ty_infer};
@@ -390,6 +390,7 @@ fn ty_to_str(cx: ctxt, typ: t) -> ~str {
                   f.meta.ret_style)
       }
       ty_infer(infer_ty) => infer_ty.to_str(),
+      ty_err => ~"[type error]",
       ty_param({idx: id, _}) => {
         ~"'" + str::from_bytes(~[('a' as u8) + (id as u8)])
       }
diff --git a/src/test/compile-fail/cast-from-nil.rs b/src/test/compile-fail/cast-from-nil.rs
index a76df00d0d5..b7cf35e6f1b 100644
--- a/src/test/compile-fail/cast-from-nil.rs
+++ b/src/test/compile-fail/cast-from-nil.rs
@@ -1,2 +1,2 @@
-// error-pattern: cast from nil: () as u32
+// error-pattern: cast from nil: `()` as `u32`
 fn main() { let u = (assert true) as u32; }
\ No newline at end of file
diff --git a/src/test/compile-fail/cast-to-nil.rs b/src/test/compile-fail/cast-to-nil.rs
index 44c0f5a1da2..5c5d456484a 100644
--- a/src/test/compile-fail/cast-to-nil.rs
+++ b/src/test/compile-fail/cast-to-nil.rs
@@ -1,2 +1,2 @@
-// error-pattern: cast to nil: u32 as ()
+// error-pattern: cast to nil: `u32` as `()`
 fn main() { let u = 0u32 as (); }
\ No newline at end of file
diff --git a/src/test/compile-fail/extern-no-call.rs b/src/test/compile-fail/extern-no-call.rs
index e53f4ee6940..4ed32e16d33 100644
--- a/src/test/compile-fail/extern-no-call.rs
+++ b/src/test/compile-fail/extern-no-call.rs
@@ -1,4 +1,4 @@
-// error-pattern:expected function or foreign function but found *u8
+// error-pattern:expected function or foreign function but found `*u8`
 extern fn f() {
 }
 
diff --git a/src/test/compile-fail/issue-1871.rs b/src/test/compile-fail/issue-1871.rs
index 6937dbdce07..99458b835f0 100644
--- a/src/test/compile-fail/issue-1871.rs
+++ b/src/test/compile-fail/issue-1871.rs
@@ -1,11 +1,12 @@
-// xfail-test
+// Tests that we don't generate a spurious error about f.honk's type
+// being undeterminable
 fn main() {
   let f = 42;
 
   let _g = if f < 5 {
-      f.honk();
+      f.honk() //~ ERROR attempted access of field `honk`
   }
   else {
-    12
+      ()
   };
 }
diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs
index eb8da1519e8..b9ccfc66703 100644
--- a/src/test/compile-fail/issue-2149.rs
+++ b/src/test/compile-fail/issue-2149.rs
@@ -11,5 +11,5 @@ impl<A> ~[A]: vec_monad<A> {
    }
 }
 fn main() {
-    ["hi"].bind({|x| [x] });
+    ["hi"].bind({|x| [x] }); //~ ERROR attempted access of field `bind`
 }