diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs
index 75321f595f444..db625124be453 100644
--- a/src/comp/front/ast.rs
+++ b/src/comp/front/ast.rs
@@ -307,7 +307,8 @@ type mt = rec(@ty ty, mutability mut);
 type ty_field = rec(ident ident, mt mt);
 type ty_arg = rec(mode mode, @ty ty);
 type ty_method = rec(proto proto, ident ident,
-                     vec[ty_arg] inputs, @ty output);
+                     vec[ty_arg] inputs, @ty output,
+                     controlflow cf);
 type ty = spanned[ty_];
 tag ty_ {
     ty_nil;
@@ -330,7 +331,7 @@ tag ty_ {
     ty_chan(@ty);
     ty_tup(vec[mt]);
     ty_rec(vec[ty_field]);
-    ty_fn(proto, vec[ty_arg], @ty);
+    ty_fn(proto, vec[ty_arg], @ty, controlflow);
     ty_obj(vec[ty_method]);
     ty_path(path, ann);
     ty_type;
diff --git a/src/comp/front/creader.rs b/src/comp/front/creader.rs
index 068c548798f15..da6d3b1131634 100644
--- a/src/comp/front/creader.rs
+++ b/src/comp/front/creader.rs
@@ -13,6 +13,8 @@ import middle::ty;
 import back::x86;
 import util::common;
 import util::common::span;
+import util::common::a_bang;
+import util::common::a_ty;
 
 import std::str;
 import std::uint;
@@ -47,6 +49,8 @@ type str_def = fn(str) -> ast::def_id;
 type pstate = rec(vec[u8] data, int crate,
                   mutable uint pos, uint len, ty::ctxt tcx);
 
+type ty_or_bang = util::common::ty_or_bang[ty::t];
+
 fn peek(@pstate st) -> u8 {
     ret st.data.(st.pos);
 }
@@ -64,9 +68,17 @@ fn parse_ty_data(vec[u8] data, int crate_num, uint pos, uint len,
     ret result;
 }
 
+fn parse_ty_or_bang(@pstate st, str_def sd) -> ty_or_bang {
+    alt (peek(st) as char) {
+        case ('!') { auto ignore = next(st); ret a_bang[ty::t]; }
+        case (_)   { ret a_ty[ty::t](parse_ty(st, sd)); }
+    }
+}
+
 fn parse_ty(@pstate st, str_def sd) -> ty::t {
     alt (next(st) as char) {
         case ('n') { ret ty::mk_nil(st.tcx); }
+        case ('z') { ret ty::mk_bot(st.tcx); }
         case ('b') { ret ty::mk_bool(st.tcx); }
         case ('i') { ret ty::mk_int(st.tcx); }
         case ('u') { ret ty::mk_uint(st.tcx); }
@@ -127,11 +139,11 @@ fn parse_ty(@pstate st, str_def sd) -> ty::t {
         }
         case ('F') {
             auto func = parse_ty_fn(st, sd);
-            ret ty::mk_fn(st.tcx, ast::proto_fn, func._0, func._1);
+            ret ty::mk_fn(st.tcx, ast::proto_fn, func._0, func._1, func._2);
         }
         case ('W') {
             auto func = parse_ty_fn(st, sd);
-            ret ty::mk_fn(st.tcx, ast::proto_iter, func._0, func._1);
+            ret ty::mk_fn(st.tcx, ast::proto_iter, func._0, func._1, func._2);
         }
         case ('N') {
             auto abi;
@@ -159,9 +171,10 @@ fn parse_ty(@pstate st, str_def sd) -> ty::t {
                 }
                 auto func = parse_ty_fn(st, sd);
                 methods += [rec(proto=proto,
-                                   ident=name,
-                                   inputs=func._0,
-                                   output=func._1)];
+                                ident=name,
+                                inputs=func._0,
+                                output=func._1,
+                                cf=func._2)];
             }
             st.pos += 1u;
             ret ty::mk_obj(st.tcx, methods);
@@ -240,7 +253,8 @@ fn parse_hex(@pstate st) -> uint {
     ret n;
 }
 
-fn parse_ty_fn(@pstate st, str_def sd) -> tup(vec[ty::arg], ty::t) {
+fn parse_ty_fn(@pstate st, str_def sd) -> tup(vec[ty::arg], ty::t,
+                                              ast::controlflow) {
     assert (next(st) as char == '[');
     let vec[ty::arg] inputs = [];
     while (peek(st) as char != ']') {
@@ -252,7 +266,15 @@ fn parse_ty_fn(@pstate st, str_def sd) -> tup(vec[ty::arg], ty::t) {
         inputs += [rec(mode=mode, ty=parse_ty(st, sd))];
     }
     st.pos = st.pos + 1u;
-    ret tup(inputs, parse_ty(st, sd));
+    auto res = parse_ty_or_bang(st, sd);
+    alt (res) {
+        case (a_bang[ty::t]) {
+            ret tup(inputs, ty::mk_bot(st.tcx), ast::noreturn);
+        }
+        case (a_ty[ty::t](?t)) {
+            ret tup(inputs, t, ast::return);
+        }
+    }
 }
 
 
@@ -550,7 +572,7 @@ fn get_tag_variants(ty::ctxt tcx, ast::def_id def)
         auto ctor_ty = item_type(item, external_crate_id, tcx);
         let vec[ty::t] arg_tys = [];
         alt (ty::struct(tcx, ctor_ty)) {
-            case (ty::ty_fn(_, ?args, _)) {
+            case (ty::ty_fn(_, ?args, _, _)) {
                 for (ty::arg a in args) {
                     arg_tys += [a.ty];
                 }
diff --git a/src/comp/front/parser.rs b/src/comp/front/parser.rs
index 1774762dcf86f..80397f2a65bbc 100644
--- a/src/comp/front/parser.rs
+++ b/src/comp/front/parser.rs
@@ -12,6 +12,8 @@ import util::common::filename;
 import util::common::span;
 import util::common::new_str_hash;
 import util::data::interner;
+import util::common::a_bang;
+import util::common::a_ty;
 
 tag restriction {
     UNRESTRICTED;
@@ -23,10 +25,7 @@ tag file_type {
     SOURCE_FILE;
 }
 
-tag ty_or_bang {
-    a_ty(@ast::ty);
-    a_bang;
-}
+type ty_or_bang = util::common::ty_or_bang[@ast::ty];
 
 state type parser =
     state obj {
@@ -351,14 +350,24 @@ fn parse_ty_fn(ast::proto proto, &parser p, uint lo)
     parse_constrs(p);
 
     let @ast::ty output;
+    auto cf = ast::return;
     if (p.peek() == token::RARROW) {
         p.bump();
-        output = parse_ty(p);
+        auto tmp = parse_ty_or_bang(p);
+        alt (tmp) {
+            case (a_ty[@ast::ty](?t)) {
+                output = t;
+            }
+            case (a_bang[@ast::ty]) {
+                output = @spanned(lo, inputs.span.hi, ast::ty_bot);
+                cf = ast::noreturn;
+            }
+        }
     } else {
         output = @spanned(lo, inputs.span.hi, ast::ty_nil);
     }
 
-    ret ast::ty_fn(proto, inputs.node, output);
+    ret ast::ty_fn(proto, inputs.node, output, cf);
 }
 
 fn parse_proto(&parser p) -> ast::proto {
@@ -377,9 +386,9 @@ fn parse_ty_obj(&parser p, &mutable uint hi) -> ast::ty_ {
         auto f = parse_ty_fn(proto, p, flo);
         expect(p, token::SEMI);
         alt (f) {
-            case (ast::ty_fn(?proto, ?inputs, ?output)) {
+            case (ast::ty_fn(?proto, ?inputs, ?output, ?cf)) {
                 ret rec(proto=proto, ident=ident,
-                        inputs=inputs, output=output);
+                        inputs=inputs, output=output, cf=cf);
             }
         }
         fail;
@@ -457,8 +466,8 @@ fn parse_ty_constrs(@ast::ty t, &parser p) -> @ast::ty {
 
 fn parse_ty_or_bang(&parser p) -> ty_or_bang {
     alt (p.peek()) {
-        case (token::NOT) { p.bump(); ret a_bang; }
-        case (_)         { ret a_ty(parse_ty(p)); }
+        case (token::NOT) { p.bump(); ret a_bang[@ast::ty]; }
+        case (_)         { ret a_ty[@ast::ty](parse_ty(p)); }
     }
 }
 
@@ -530,7 +539,7 @@ fn parse_ty(&parser p) -> @ast::ty {
         auto flo = p.get_last_lo_pos();
         t = parse_ty_fn(ast::proto_fn, p, flo);
         alt (t) {
-            case (ast::ty_fn(_, _, ?out)) {
+            case (ast::ty_fn(_, _, ?out, _)) {
                 hi = out.span.hi;
             }
         }
@@ -538,7 +547,7 @@ fn parse_ty(&parser p) -> @ast::ty {
         auto flo = p.get_last_lo_pos();
         t = parse_ty_fn(ast::proto_iter, p, flo);
         alt (t) {
-            case (ast::ty_fn(_, _, ?out)) {
+            case (ast::ty_fn(_, _, ?out, _)) {
                 hi = out.span.hi;
             }
         }
@@ -1735,15 +1744,16 @@ fn parse_fn_decl(&parser p, ast::purity purity) -> ast::fn_decl {
         p.bump();
         res = parse_ty_or_bang(p);
     } else {
-        res = a_ty(@spanned(inputs.span.lo, inputs.span.hi, ast::ty_nil));
+        res = a_ty[@ast::ty](@spanned(inputs.span.lo, inputs.span.hi,
+                                      ast::ty_nil));
     }
 
     alt (res) {
-        case (a_ty(?t)) {
+        case (a_ty[@ast::ty](?t)) {
             ret rec(inputs=inputs.node, output=t,
               purity=purity, cf=ast::return);
         }
-        case (a_bang) {
+        case (a_bang[@ast::ty]) {
             ret rec(inputs=inputs.node,
                     output=@spanned(p.get_lo_pos(),
                                     p.get_hi_pos(), ast::ty_bot),
diff --git a/src/comp/middle/fold.rs b/src/comp/middle/fold.rs
index f0de3c4b3cbcf..9d48d50b4e65a 100644
--- a/src/comp/middle/fold.rs
+++ b/src/comp/middle/fold.rs
@@ -67,7 +67,7 @@ type ast_fold[ENV] =
      (fn(&ENV e, &span sp,
          ast::proto proto,
          &vec[rec(ast::mode mode, @ty ty)] inputs,
-         &@ty output) -> @ty)                     fold_ty_fn,
+         &@ty output, &controlflow cf) -> @ty)    fold_ty_fn,
 
      (fn(&ENV e, &span sp, &ast::path p,
          &ann a) -> @ty)                          fold_ty_path,
@@ -423,12 +423,12 @@ fn fold_ty[ENV](&ENV env, &ast_fold[ENV] fld, &@ty t) -> @ty {
             let vec[ast::ty_method] meths_ = [];
             for (ast::ty_method m in meths) {
                 auto tfn = fold_ty_fn(env_, fld, t.span, m.proto,
-                                      m.inputs, m.output);
+                                      m.inputs, m.output, m.cf);
                 alt (tfn.node) {
-                    case (ast::ty_fn(?p, ?ins, ?out)) {
+                    case (ast::ty_fn(?p, ?ins, ?out, ?cf)) {
                         vec::push[ast::ty_method]
                             (meths_, rec(proto=p, inputs=ins,
-                                         output=out with m));
+                                         output=out, cf=cf with m));
                     }
                 }
             }
@@ -440,8 +440,8 @@ fn fold_ty[ENV](&ENV env, &ast_fold[ENV] fld, &@ty t) -> @ty {
             ret fld.fold_ty_path(env_, t.span, pth_, ann);
         }
 
-        case (ast::ty_fn(?proto, ?inputs, ?output)) {
-            ret fold_ty_fn(env_, fld, t.span, proto, inputs, output);
+        case (ast::ty_fn(?proto, ?inputs, ?output, ?cf)) {
+            ret fold_ty_fn(env_, fld, t.span, proto, inputs, output, cf);
         }
 
         case (ast::ty_chan(?ty)) {
@@ -459,7 +459,7 @@ fn fold_ty[ENV](&ENV env, &ast_fold[ENV] fld, &@ty t) -> @ty {
 fn fold_ty_fn[ENV](&ENV env, &ast_fold[ENV] fld, &span sp,
                    ast::proto proto,
                    &vec[rec(ast::mode mode, @ty ty)] inputs,
-                   &@ty output) -> @ty {
+                   &@ty output, &controlflow cf) -> @ty {
     auto output_ = fold_ty(env, fld, output);
     let vec[rec(ast::mode mode, @ty ty)] inputs_ = [];
     for (rec(ast::mode mode, @ty ty) input in inputs) {
@@ -467,7 +467,7 @@ fn fold_ty_fn[ENV](&ENV env, &ast_fold[ENV] fld, &span sp,
         auto input_ = rec(ty=ty_ with input);
         inputs_ += [input_];
     }
-    ret fld.fold_ty_fn(env, sp, proto, inputs_, output_);
+    ret fld.fold_ty_fn(env, sp, proto, inputs_, output_, cf);
 }
 
 fn fold_decl[ENV](&ENV env, &ast_fold[ENV] fld, &@decl d) -> @decl {
@@ -1264,8 +1264,8 @@ fn identity_fold_ty_obj[ENV](&ENV env, &span sp,
 fn identity_fold_ty_fn[ENV](&ENV env, &span sp,
                             ast::proto proto,
                             &vec[rec(ast::mode mode, @ty ty)] inputs,
-                            &@ty output) -> @ty {
-    ret @respan(sp, ast::ty_fn(proto, inputs, output));
+                            &@ty output, &controlflow cf) -> @ty {
+    ret @respan(sp, ast::ty_fn(proto, inputs, output, cf));
 }
 
 fn identity_fold_ty_path[ENV](&ENV env, &span sp, &ast::path p,
@@ -1742,7 +1742,7 @@ fn new_identity_fold[ENV]() -> ast_fold[ENV] {
          fold_ty_tup     = bind identity_fold_ty_tup[ENV](_,_,_),
          fold_ty_rec     = bind identity_fold_ty_rec[ENV](_,_,_),
          fold_ty_obj     = bind identity_fold_ty_obj[ENV](_,_,_),
-         fold_ty_fn      = bind identity_fold_ty_fn[ENV](_,_,_,_,_),
+         fold_ty_fn      = bind identity_fold_ty_fn[ENV](_,_,_,_,_,_),
          fold_ty_path    = bind identity_fold_ty_path[ENV](_,_,_,_),
          fold_ty_chan    = bind identity_fold_ty_chan[ENV](_,_,_),
          fold_ty_port    = bind identity_fold_ty_port[ENV](_,_,_),
diff --git a/src/comp/middle/metadata.rs b/src/comp/middle/metadata.rs
index 7f518506254bc..97b2f3d7c4a31 100644
--- a/src/comp/middle/metadata.rs
+++ b/src/comp/middle/metadata.rs
@@ -144,6 +144,7 @@ mod Encode {
     fn enc_sty(&io::writer w, &@ctxt cx, &ty::sty st) {
         alt (st) {
             case (ty::ty_nil) { w.write_char('n'); }
+            case (ty::ty_bot) { w.write_char('z'); }
             case (ty::ty_bool) { w.write_char('b'); }
             case (ty::ty_int) { w.write_char('i'); }
             case (ty::ty_uint) { w.write_char('u'); }
@@ -193,9 +194,9 @@ mod Encode {
                 }
                 w.write_char(']');
             }
-            case (ty::ty_fn(?proto,?args,?out)) {
+            case (ty::ty_fn(?proto,?args,?out,?cf)) {
                 enc_proto(w, proto);
-                enc_ty_fn(w, cx, args, out);
+                enc_ty_fn(w, cx, args, out, cf);
             }
             case (ty::ty_native_fn(?abi,?args,?out)) {
                 w.write_char('N');
@@ -207,14 +208,14 @@ mod Encode {
                     case (ast::native_abi_cdecl) { w.write_char('c'); }
                     case (ast::native_abi_llvm) { w.write_char('l'); }
                 }
-                enc_ty_fn(w, cx, args, out);
+                enc_ty_fn(w, cx, args, out, ast::return);
             }
             case (ty::ty_obj(?methods)) {
                 w.write_str("O[");
                 for (ty::method m in methods) {
                     enc_proto(w, m.proto);
                     w.write_str(m.ident);
-                    enc_ty_fn(w, cx, m.inputs, m.output);
+                    enc_ty_fn(w, cx, m.inputs, m.output, m.cf);
                 }
                 w.write_char(']');
             }
@@ -250,14 +251,22 @@ mod Encode {
         }
     }
 
-    fn enc_ty_fn(&io::writer w, &@ctxt cx, &vec[ty::arg] args, &ty::t out) {
+    fn enc_ty_fn(&io::writer w, &@ctxt cx, &vec[ty::arg] args, &ty::t out,
+                 &ast::controlflow cf) {
         w.write_char('[');
         for (ty::arg arg in args) {
             if (arg.mode == ty::mo_alias) { w.write_char('&'); }
             enc_ty(w, cx, arg.ty);
         }
         w.write_char(']');
-        enc_ty(w, cx, out);
+        alt (cf) {
+            case (ast::noreturn) {
+                w.write_char('!');
+            }
+            case (_) {
+                enc_ty(w, cx, out);
+            }
+        }
     }
 
 }
diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs
index 0ad368db9d4e3..201e456dbc195 100644
--- a/src/comp/middle/resolve.rs
+++ b/src/comp/middle/resolve.rs
@@ -521,7 +521,7 @@ fn lookup_in_scope(&env e, list[scope] sc, &span sp, &ident id, namespace ns)
             }
         }
     }
-    fail;
+    e.sess.bug("reached unreachable code in lookup_in_scope"); // sigh
 }
 
 fn lookup_in_ty_params(&ident id, &vec[ast::ty_param] ty_params)
@@ -757,7 +757,7 @@ fn lookup_in_local_mod(&env e, def_id defid, &ident id, namespace ns,
             }
         }
     }
-    fail;
+    e.sess.bug("reached unreachable code in lookup_in_regular_mod"); // sigh
 }
 
 fn lookup_in_mie(&env e, &mod_index_entry mie, namespace ns)
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 23c74e0633615..137a614a098c0 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -807,7 +807,7 @@ fn type_of_inner(&@crate_ctxt cx, &ast::span sp, &ty::t t) -> TypeRef {
             }
             llty = T_struct(tys);
         }
-        case (ty::ty_fn(?proto, ?args, ?out)) {
+        case (ty::ty_fn(?proto, ?args, ?out, _)) {
             llty = T_fn_pair(cx.tn, type_of_fn(cx, sp, proto, args, out, 0u));
         }
         case (ty::ty_native_fn(?abi, ?args, ?out)) {
@@ -882,7 +882,7 @@ fn type_of_arg(@local_ctxt cx, &ast::span sp, &ty::arg arg) -> TypeRef {
 fn type_of_ty_param_count_and_ty(@local_ctxt lcx, &ast::span sp,
                                  &ty::ty_param_count_and_ty tpt) -> TypeRef {
     alt (ty::struct(lcx.ccx.tcx, tpt._1)) {
-        case (ty::ty_fn(?proto, ?inputs, ?output)) {
+        case (ty::ty_fn(?proto, ?inputs, ?output, _)) {
             auto llfnty = type_of_fn(lcx.ccx, sp, proto,
                                      inputs, output, tpt._0);
             ret T_fn_pair(lcx.ccx.tn, llfnty);
@@ -2106,7 +2106,7 @@ fn make_free_glue(&@block_ctxt cx, ValueRef v0, &ty::t t) {
             rslt = trans_non_gc_free(cx_, b);
         }
 
-        case (ty::ty_fn(_,_,_)) {
+        case (ty::ty_fn(_,_,_,_)) {
 
             auto box_cell =
                 cx.build.GEP(v0,
@@ -2178,7 +2178,7 @@ fn make_drop_glue(&@block_ctxt cx, ValueRef v0, &ty::t t) {
             rslt = decr_refcnt_maybe_free(cx, box_cell, v0, t);
         }
 
-        case (ty::ty_fn(_,_,_)) {
+        case (ty::ty_fn(_,_,_,_)) {
 
             auto box_cell =
                 cx.build.GEP(v0,
@@ -2642,7 +2642,7 @@ fn iter_structural_ty_full(&@block_ctxt cx,
                     // N-ary variant.
                     auto fn_ty = variant.ctor_ty;
                     alt (ty::struct(bcx.fcx.lcx.ccx.tcx, fn_ty)) {
-                        case (ty::ty_fn(_, ?args, _)) {
+                        case (ty::ty_fn(_, ?args, _, _)) {
                             auto j = 0;
                             for (ty::arg a in args) {
                                 auto v = [C_int(0), C_int(j as int)];
@@ -2692,7 +2692,7 @@ fn iter_structural_ty_full(&@block_ctxt cx,
 
             ret res(next_cx, C_nil());
         }
-        case (ty::ty_fn(_,_,_)) {
+        case (ty::ty_fn(_,_,_,_)) {
             auto box_cell_a =
                 cx.build.GEP(av,
                              [C_int(0),
@@ -4382,7 +4382,7 @@ fn trans_path(&@block_ctxt cx, &ast::path p, &ast::ann ann) -> lval_result {
         case (ast::def_variant(?tid, ?vid)) {
             auto v_tyt = ty::lookup_item_type(cx.fcx.lcx.ccx.tcx, vid);
             alt (ty::struct(cx.fcx.lcx.ccx.tcx, v_tyt._1)) {
-                case (ty::ty_fn(_, _, _)) {
+                case (ty::ty_fn(_, _, _, _)) {
                     // N-ary variant.
                     ret lval_generic_fn(cx, v_tyt, vid, ann);
                 }
@@ -4425,7 +4425,7 @@ fn trans_path(&@block_ctxt cx, &ast::path p, &ast::ann ann) -> lval_result {
             ret lval_generic_fn(cx, tyt, did, ann);
         }
         case (_) {
-            cx.fcx.lcx.ccx.sess.unimpl("def variant in trans");
+            cx.fcx.lcx.ccx.sess.span_unimpl(cx.sp, "def variant in trans");
         }
     }
 }
@@ -6413,7 +6413,7 @@ fn is_terminated(&@block_ctxt cx) -> bool {
 
 fn arg_tys_of_fn(&@crate_ctxt ccx, ast::ann ann) -> vec[ty::arg] {
     alt (ty::struct(ccx.tcx, ty::ann_to_type(ccx.tcx.node_types, ann))) {
-        case (ty::ty_fn(_, ?arg_tys, _)) {
+        case (ty::ty_fn(_, ?arg_tys, _, _)) {
             ret arg_tys;
         }
     }
@@ -6422,7 +6422,7 @@ fn arg_tys_of_fn(&@crate_ctxt ccx, ast::ann ann) -> vec[ty::arg] {
 
 fn ret_ty_of_fn_ty(&@crate_ctxt ccx, ty::t t) -> ty::t {
     alt (ty::struct(ccx.tcx, t)) {
-        case (ty::ty_fn(_, _, ?ret_ty)) {
+        case (ty::ty_fn(_, _, ?ret_ty, _)) {
             ret ret_ty;
         }
     }
@@ -6574,7 +6574,7 @@ fn trans_vtbl(@local_ctxt cx,
 
         auto llfnty = T_nil();
         alt (ty::struct(cx.ccx.tcx, node_ann_type(cx.ccx, m.node.ann))) {
-            case (ty::ty_fn(?proto, ?inputs, ?output)) {
+            case (ty::ty_fn(?proto, ?inputs, ?output, _)) {
                 llfnty = type_of_fn_full(cx.ccx, m.span, proto,
                                          some[TypeRef](llself_ty),
                                          inputs, output,
@@ -6934,7 +6934,7 @@ fn decl_fn_and_pair(&@crate_ctxt ccx, &ast::span sp,
     auto llfty;
     auto llpairty;
     alt (ty::struct(ccx.tcx, node_ann_type(ccx, ann))) {
-        case (ty::ty_fn(?proto, ?inputs, ?output)) {
+        case (ty::ty_fn(?proto, ?inputs, ?output, _)) {
             llfty = type_of_fn(ccx, sp, proto, inputs, output,
                                vec::len[ast::ty_param](ty_params));
             llpairty = T_fn_pair(ccx.tn, llfty);
diff --git a/src/comp/middle/tstate/annotate.rs b/src/comp/middle/tstate/annotate.rs
index 743f525401d11..ab1468d87c40b 100644
--- a/src/comp/middle/tstate/annotate.rs
+++ b/src/comp/middle/tstate/annotate.rs
@@ -86,11 +86,12 @@ import util::common::log_expr;
 import util::common::log_block;
 import util::common::log_stmt;
 
-import middle::tstate::aux::fn_info;
-import middle::tstate::aux::fn_info_map;
-import middle::tstate::aux::num_locals;
-import middle::tstate::aux::get_fn_info;
-import middle::tstate::aux::crate_ctxt;
+import aux::fn_info;
+import aux::fn_info_map;
+import aux::num_locals;
+import aux::get_fn_info;
+import aux::crate_ctxt;
+import aux::add_node;
 import middle::tstate::ann::empty_ann;
 
 fn collect_ids_expr(&@expr e, @vec[uint] res) -> () {
@@ -140,7 +141,7 @@ fn node_ids_in_fn(&_fn f, &ident i, &def_id d, &ann a, @vec[uint] res) -> () {
 fn init_vecs(&crate_ctxt ccx, @vec[uint] node_ids, uint len) -> () {
     for (uint i in *node_ids) {
         log(uistr(i) + " |-> " + uistr(len));
-        ccx.node_anns.insert(i, empty_ann(len));
+        add_node(ccx, i, empty_ann(len));
     }
 }
 
diff --git a/src/comp/middle/tstate/auxiliary.rs b/src/comp/middle/tstate/auxiliary.rs
index 39e72c0edc71c..48302344d079f 100644
--- a/src/comp/middle/tstate/auxiliary.rs
+++ b/src/comp/middle/tstate/auxiliary.rs
@@ -1,5 +1,7 @@
 import std::bitv;
+import std::vec;
 import std::vec::len;
+import std::vec::grow;
 import std::vec::pop;
 import std::option;
 import std::option::none;
@@ -156,7 +158,7 @@ type fn_info      = rec(@std::map::hashmap[def_id, var_info] vars,
                         controlflow cf);
 
 /* mapping from node ID to typestate annotation */
-type node_ann_table = @std::map::hashmap[uint, ts_ann];
+type node_ann_table = @vec[ts_ann];
 
 /* mapping from function name to fn_info map */
 type fn_info_map = @std::map::hashmap[def_id, fn_info];
@@ -175,10 +177,26 @@ fn get_fn_info(&crate_ctxt ccx, def_id did) -> fn_info {
     ret ccx.fm.get(did);
 }
 
+fn add_node(&crate_ctxt ccx, uint i, &ts_ann a) -> () {
+    auto sz = len(*(ccx.node_anns)); 
+    if (sz <= i) {
+        grow(*(ccx.node_anns), (i - sz) + 1u, empty_ann(0u));
+    }
+    ccx.node_anns.(i) = a;
+}
+
+fn get_ts_ann(&crate_ctxt ccx, uint i) -> option::t[ts_ann] {
+    if (i < len(*(ccx.node_anns))) {
+        ret some[ts_ann](ccx.node_anns.(i));
+    }
+    else {
+        ret none[ts_ann];
+    }
+}
 /********* utils ********/
 
 fn ann_to_ts_ann(&crate_ctxt ccx, &ann a) -> ts_ann {
-    alt (ccx.node_anns.find(a.id)) {
+    alt (get_ts_ann(ccx, a.id)) {
         case (none[ts_ann])         { 
             log_err ("ann_to_ts_ann: no ts_ann for node_id "
                      + uistr(a.id));
@@ -296,8 +314,8 @@ fn block_poststate(&crate_ctxt ccx, &block b) -> poststate {
 
 /* sets the pre_and_post for an ann */
 fn with_pp(&crate_ctxt ccx, &ann a, pre_and_post p) {
-    ccx.node_anns.insert(a.id, @rec(conditions=p,
-                                    states=empty_states(pps_len(p))));
+    add_node(ccx, a.id, @rec(conditions=p,
+                             states=empty_states(pps_len(p))));
 }
 
 fn set_prestate_ann(&crate_ctxt ccx, &ann a, &prestate pre) -> bool {
@@ -370,8 +388,8 @@ fn num_locals(fn_info m) -> uint {
 }
 
 fn new_crate_ctxt(ty::ctxt cx) -> crate_ctxt {
-    ret rec(tcx=cx, node_anns=@new_uint_hash[ts_ann](),
-            fm=@new_def_hash[fn_info]());
+    let vec[ts_ann] na = [];
+    ret rec(tcx=cx, node_anns=@na, fm=@new_def_hash[fn_info]());
 }
 
 fn controlflow_def_id(&crate_ctxt ccx, &def_id d) -> controlflow {
@@ -381,20 +399,16 @@ fn controlflow_def_id(&crate_ctxt ccx, &def_id d) -> controlflow {
     }
 }
 
-/* conservative approximation: uses the mapping if e refers to a known
-   function or method, assumes returning otherwise.
-   There's no case for fail b/c we assume e is the callee and it
-   seems unlikely that one would apply "fail" to arguments. */
+/* Use e's type to determine whether it returns.
+ If it has a function type with a ! annotation,
+the answer is noreturn. */
 fn controlflow_expr(&crate_ctxt ccx, @expr e) -> controlflow {
-    auto f = expr_ann(e).id;
-    alt (ccx.tcx.def_map.find(f)) {
-        case (some[def](def_fn(?d))) { 
-            ret controlflow_def_id(ccx, d); 
-        }
-        case (some[def](def_obj_field(?d))) { 
-            ret controlflow_def_id(ccx, d);
+    alt (ty::struct(ccx.tcx, ty::ann_to_type(ccx.tcx.node_types,
+                                             expr_ann(e)))) {
+        case (ty::ty_fn(_,_,_,?cf)) {
+            ret cf;
         }
-        case (_)                            {
+        case (_) {
             ret return;
         }
     }
diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs
index 1752fcaf2b13c..a837bd9f9a2c2 100644
--- a/src/comp/middle/tstate/pre_post_conditions.rs
+++ b/src/comp/middle/tstate/pre_post_conditions.rs
@@ -79,7 +79,6 @@ import front::ast::lit;
 import front::ast::init_op;
 import front::ast::controlflow;
 import front::ast::return;
-import front::ast::noreturn;
 import front::ast::_fn;
 import front::ast::_obj;
 import front::ast::crate;
diff --git a/src/comp/middle/tstate/states.rs b/src/comp/middle/tstate/states.rs
index 4af741670b02a..c04ad500574c3 100644
--- a/src/comp/middle/tstate/states.rs
+++ b/src/comp/middle/tstate/states.rs
@@ -235,17 +235,9 @@ fn find_pre_post_state_expr(&fn_ctxt fcx, &prestate pres, @expr e) -> bool {
       /* if this is a failing call, it sets the return value */
        alt (controlflow_expr(fcx.ccx, operator)) {
           case (noreturn) {
-            /*
-            log_err("Call that might fail! to");
-            log_expr_err(*operator);
-            */
             changed = gen_poststate(fcx, a, fcx.id) || changed;
           }
-          case (_) { 
-            /*      log_err("non-failing call, to:");
-            log_expr_err(*operator);
-            */
-          }
+          case (_) { }
       }
       ret changed;
     }
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index 67fb829d4ad79..b92b7fb61db7d 100644
--- a/src/comp/middle/ty.rs
+++ b/src/comp/middle/ty.rs
@@ -13,6 +13,7 @@ import std::option::some;
 import driver::session;
 import front::ast;
 import front::ast::mutability;
+import front::ast::controlflow;
 import front::creader;
 import middle::metadata;
 import util::common;
@@ -49,7 +50,8 @@ type field = rec(ast::ident ident, mt mt);
 type method = rec(ast::proto proto,
                   ast::ident ident,
                   vec[arg] inputs,
-                  t output);
+                  t output,
+                  controlflow cf);
 
 tag any_item {
     any_item_rust(@ast::item);
@@ -76,7 +78,7 @@ type ty_ctxt = ctxt;    // Needed for disambiguation from unify::ctxt.
 // Convert from method type to function type.  Pretty easy; we just drop
 // 'ident'.
 fn method_ty_to_fn_ty(&ctxt cx, method m) -> t {
-    ret mk_fn(cx, m.proto, m.inputs, m.output);
+    ret mk_fn(cx, m.proto, m.inputs, m.output, m.cf);
 }
 
 // Never construct these manually. These are interned.
@@ -113,7 +115,7 @@ tag sty {
     ty_task;
     ty_tup(vec[mt]);
     ty_rec(vec[field]);
-    ty_fn(ast::proto, vec[arg], t);
+    ty_fn(ast::proto, vec[arg], t, controlflow);
     ty_native_fn(ast::native_abi, vec[arg], t);
     ty_obj(vec[method]);
     ty_var(int);                                    // ephemeral type var
@@ -135,6 +137,7 @@ type unify_handler = obj {
 
 tag type_err {
     terr_mismatch;
+    terr_controlflow_mismatch;
     terr_box_mutability;
     terr_vec_mutability;
     terr_tuple_size(uint, uint);
@@ -358,7 +361,7 @@ fn mk_raw_ty(&ctxt cx, &sty st, &option::t[str] cname) -> raw_t {
             }
         }
 
-        case (ty_fn(_, ?args, ?tt)) {
+        case (ty_fn(_, ?args, ?tt, _)) {
             derive_flags_sig(cx, has_params, has_bound_params,
                              has_vars, has_locals, args, tt);
         }
@@ -462,8 +465,9 @@ fn mk_imm_tup(&ctxt cx, &vec[t] tys) -> t {
 
 fn mk_rec(&ctxt cx, &vec[field] fs) -> t { ret gen_ty(cx, ty_rec(fs)); }
 
-fn mk_fn(&ctxt cx, &ast::proto proto, &vec[arg] args, &t ty) -> t {
-    ret gen_ty(cx, ty_fn(proto, args, ty));
+fn mk_fn(&ctxt cx, &ast::proto proto, &vec[arg] args, &t ty,
+         &controlflow cf) -> t {
+    ret gen_ty(cx, ty_fn(proto, args, ty, cf));
 }
 
 fn mk_native_fn(&ctxt cx, &ast::native_abi abi, &vec[arg] args, &t ty) -> t {
@@ -635,7 +639,7 @@ fn ty_to_str(&ctxt cx, &t typ) -> str {
             }
         }
 
-        case (ty_fn(?proto, ?inputs, ?output)) {
+        case (ty_fn(?proto, ?inputs, ?output, _)) {
             s += fn_to_str(cx, proto, none[ast::ident], inputs, output);
         }
 
@@ -721,7 +725,7 @@ fn walk_ty(&ctxt cx, ty_walk walker, t ty) {
                 walk_ty(cx, walker, fl.mt.ty);
             }
         }
-        case (ty_fn(?proto, ?args, ?ret_ty)) {
+        case (ty_fn(?proto, ?args, ?ret_ty, _)) {
             for (arg a in args) {
                 walk_ty(cx, walker, a.ty);
             }
@@ -805,14 +809,14 @@ fn fold_ty(&ctxt cx, ty_fold fld, t ty_0) -> t {
             }
             ty = copy_cname(cx, mk_rec(cx, new_fields), ty);
         }
-        case (ty_fn(?proto, ?args, ?ret_ty)) {
+        case (ty_fn(?proto, ?args, ?ret_ty, ?cf)) {
             let vec[arg] new_args = [];
             for (arg a in args) {
                 auto new_ty = fold_ty(cx, fld, a.ty);
                 new_args += [rec(mode=a.mode, ty=new_ty)];
             }
             ty = copy_cname(cx, mk_fn(cx, proto, new_args,
-                                      fold_ty(cx, fld, ret_ty)), ty);
+                                      fold_ty(cx, fld, ret_ty), cf), ty);
         }
         case (ty_native_fn(?abi, ?args, ?ret_ty)) {
             let vec[arg] new_args = [];
@@ -832,8 +836,8 @@ fn fold_ty(&ctxt cx, ty_fold fld, t ty_0) -> t {
                                         ty=fold_ty(cx, fld, a.ty))];
                 }
                 new_methods += [rec(proto=m.proto, ident=m.ident,
-                                       inputs=new_args,
-                                       output=fold_ty(cx, fld, m.output))];
+                               inputs=new_args,
+                               output=fold_ty(cx, fld, m.output), cf=m.cf)];
             }
             ty = copy_cname(cx, mk_obj(cx, new_methods), ty);
         }
@@ -885,7 +889,7 @@ fn type_is_structural(&ctxt cx, &t ty) -> bool {
         case (ty_tup(_))    { ret true; }
         case (ty_rec(_))    { ret true; }
         case (ty_tag(_,_))  { ret true; }
-        case (ty_fn(_,_,_)) { ret true; }
+        case (ty_fn(_,_,_,_)) { ret true; }
         case (ty_obj(_))    { ret true; }
         case (_)            { ret false; }
     }
@@ -1206,7 +1210,7 @@ fn hash_type_structure(&sty st) -> uint {
             }
             ret h;
         }
-        case (ty_fn(_, ?args, ?rty)) { ret hash_fn(25u, args, rty); }
+        case (ty_fn(_, ?args, ?rty, _)) { ret hash_fn(25u, args, rty); }
         case (ty_native_fn(_, ?args, ?rty)) { ret hash_fn(26u, args, rty); }
         case (ty_obj(?methods)) {
             auto h = 27u;
@@ -1405,10 +1409,11 @@ fn equal_type_structures(&sty a, &sty b) -> bool {
                 case (_) { ret false; }
             }
         }
-        case (ty_fn(?p_a, ?args_a, ?rty_a)) {
+        case (ty_fn(?p_a, ?args_a, ?rty_a, ?cf_a)) {
             alt (b) {
-                case (ty_fn(?p_b, ?args_b, ?rty_b)) {
+                case (ty_fn(?p_b, ?args_b, ?rty_b, ?cf_b)) {
                     ret p_a == p_b &&
+                        cf_a == cf_b &&
                         equal_fn(args_a, rty_a, args_b, rty_b);
                 }
                 case (_) { ret false; }
@@ -1635,7 +1640,7 @@ fn type_contains_bound_params(&ctxt cx, &t typ) -> bool {
 
 fn ty_fn_args(&ctxt cx, &t fty) -> vec[arg] {
     alt (struct(cx, fty)) {
-        case (ty::ty_fn(_, ?a, _)) { ret a; }
+        case (ty::ty_fn(_, ?a, _, _)) { ret a; }
         case (ty::ty_native_fn(_, ?a, _)) { ret a; }
     }
     fail;
@@ -1643,7 +1648,7 @@ fn ty_fn_args(&ctxt cx, &t fty) -> vec[arg] {
 
 fn ty_fn_proto(&ctxt cx, &t fty) -> ast::proto {
     alt (struct(cx, fty)) {
-        case (ty::ty_fn(?p, _, _)) { ret p; }
+        case (ty::ty_fn(?p, _, _, _)) { ret p; }
     }
     fail;
 }
@@ -1657,7 +1662,7 @@ fn ty_fn_abi(&ctxt cx, &t fty) -> ast::native_abi {
 
 fn ty_fn_ret(&ctxt cx, &t fty) -> t {
     alt (struct(cx, fty)) {
-        case (ty::ty_fn(_, _, ?r)) { ret r; }
+        case (ty::ty_fn(_, _, ?r, _)) { ret r; }
         case (ty::ty_native_fn(_, _, ?r)) { ret r; }
     }
     fail;
@@ -1665,7 +1670,7 @@ fn ty_fn_ret(&ctxt cx, &t fty) -> t {
 
 fn is_fn_ty(&ctxt cx, &t fty) -> bool {
     alt (struct(cx, fty)) {
-        case (ty::ty_fn(_, _, _)) { ret true; }
+        case (ty::ty_fn(_, _, _, _)) { ret true; }
         case (ty::ty_native_fn(_, _, _)) { ret true; }
         case (_) { ret false; }
     }
@@ -2088,12 +2093,32 @@ mod unify {
                 &t expected,
                 &t actual,
                 &vec[arg] expected_inputs, &t expected_output,
-                &vec[arg] actual_inputs, &t actual_output)
+                &vec[arg] actual_inputs, &t actual_output,
+                &controlflow expected_cf, &controlflow actual_cf)
         -> result {
 
         if (e_proto != a_proto) {
             ret ures_err(terr_mismatch, expected, actual);
         }
+        alt (expected_cf) {
+            case (ast::return) { } // ok
+            case (ast::noreturn) {
+                alt (actual_cf) {
+                    case (ast::noreturn) {
+                        // ok
+                    }
+                    case (_) {
+                        /* even though typestate checking is mostly
+                           responsible for checking control flow annotations,
+                           this check is necessary to ensure that the
+                           annotation in an object method matches the
+                           declared object type */
+                        ret ures_err(terr_controlflow_mismatch,
+                                     expected, actual);
+                    }
+                }
+            }
+        }
         auto t = unify_fn_common(cx, expected, actual,
                                  expected_inputs, expected_output,
                                  actual_inputs, actual_output);
@@ -2102,7 +2127,8 @@ mod unify {
                 ret r;
             }
             case (fn_common_res_ok(?result_ins, ?result_out)) {
-                auto t2 = mk_fn(cx.tcx, e_proto, result_ins, result_out);
+                auto t2 = mk_fn(cx.tcx, e_proto, result_ins, result_out,
+                                actual_cf);
                 ret ures_ok(t2);
             }
         }
@@ -2160,14 +2186,16 @@ mod unify {
                           e_meth.proto, a_meth.proto,
                           expected, actual,
                           e_meth.inputs, e_meth.output,
-                          a_meth.inputs, a_meth.output);
+                          a_meth.inputs, a_meth.output,
+                          e_meth.cf, a_meth.cf);
         alt (r) {
             case (ures_ok(?tfn)) {
                 alt (struct(cx.tcx, tfn)) {
-                    case (ty_fn(?proto, ?ins, ?out)) {
+                    case (ty_fn(?proto, ?ins, ?out, ?cf)) {
                         result_meths += [rec(inputs = ins,
-                                                output = out
-                                                with e_meth)];
+                                             output = out,
+                                             cf     = cf
+                                             with e_meth)];
                     }
                 }
             }
@@ -2539,13 +2567,16 @@ mod unify {
                 }
             }
 
-            case (ty::ty_fn(?ep, ?expected_inputs, ?expected_output)) {
+            case (ty::ty_fn(?ep, ?expected_inputs,
+                            ?expected_output, ?expected_cf)) {
                 alt (struct(cx.tcx, actual)) {
-                    case (ty::ty_fn(?ap, ?actual_inputs, ?actual_output)) {
+                    case (ty::ty_fn(?ap, ?actual_inputs,
+                                    ?actual_output, ?actual_cf)) {
                         ret unify_fn(cx, ep, ap,
                                      expected, actual,
                                      expected_inputs, expected_output,
-                                     actual_inputs, actual_output);
+                                     actual_inputs, actual_output,
+                                     expected_cf, actual_cf);
                     }
 
                     case (_) {
@@ -2732,6 +2763,10 @@ fn type_err_to_str(&ty::type_err err) -> str {
         case (terr_mismatch) {
             ret "types differ";
         }
+        case (terr_controlflow_mismatch) {
+            ret "returning function used where non-returning function"
+                + " was expected";
+        }
         case (terr_box_mutability) {
             ret "boxed values differ in mutability";
         }
@@ -2911,7 +2946,7 @@ fn lookup_item_type(ctxt cx, ast::def_id did) -> ty_param_count_and_ty {
 
 fn ret_ty_of_fn_ty(ty_ctxt tcx, t a_ty) -> t {
     alt (ty::struct(tcx, a_ty)) {
-        case (ty::ty_fn(_, _, ?ret_ty)) {
+        case (ty::ty_fn(_, _, ?ret_ty, _)) {
             ret ret_ty;
         }
         case (_) {
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index 5427e5f3b0595..de47fce619d86 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -314,11 +314,11 @@ fn ast_ty_to_ty(&ty::ctxt tcx, &ty_getter getter, &@ast::ty ast_ty) -> ty::t {
             typ = ty::mk_rec(tcx, flds);
         }
 
-        case (ast::ty_fn(?proto, ?inputs, ?output)) {
+        case (ast::ty_fn(?proto, ?inputs, ?output, ?cf)) {
             auto f = bind ast_arg_to_arg(tcx, getter, _);
             auto i = vec::map[ast::ty_arg, arg](f, inputs);
             auto out_ty = ast_ty_to_ty(tcx, getter, output);
-            typ = ty::mk_fn(tcx, proto, i, out_ty);
+            typ = ty::mk_fn(tcx, proto, i, out_ty, cf);
         }
 
         case (ast::ty_path(?path, ?ann)) {
@@ -346,11 +346,13 @@ fn ast_ty_to_ty(&ty::ctxt tcx, &ty_getter getter, &@ast::ty ast_ty) -> ty::t {
             for (ast::ty_method m in meths) {
                 auto ins = vec::map[ast::ty_arg, arg](f, m.inputs);
                 auto out = ast_ty_to_ty(tcx, getter, m.output);
-                vec::push[ty::method](tmeths,
+                let ty::method new_m =
                                   rec(proto=m.proto,
                                       ident=m.ident,
                                       inputs=ins,
-                                      output=out));
+                                      output=out,
+                                      cf=m.cf);
+                vec::push[ty::method](tmeths, new_m);
             }
 
             typ = ty::mk_obj(tcx, ty::sort_methods(tmeths));
@@ -458,7 +460,7 @@ mod collect {
                      &ast::def_id def_id) -> ty::ty_param_count_and_ty {
         auto input_tys = vec::map[ast::arg,arg](ty_of_arg, decl.inputs);
         auto output_ty = convert(decl.output);
-        auto t_fn = ty::mk_fn(cx.tcx, proto, input_tys, output_ty);
+        auto t_fn = ty::mk_fn(cx.tcx, proto, input_tys, output_ty, decl.cf);
         auto ty_param_count = vec::len[ast::ty_param](ty_params);
         auto tpt = tup(ty_param_count, t_fn);
         cx.tcx.tcache.insert(def_id, tpt);
@@ -513,7 +515,7 @@ mod collect {
         auto inputs = vec::map[ast::arg,arg](f, m.node.meth.decl.inputs);
         auto output = convert(m.node.meth.decl.output);
         ret rec(proto=m.node.meth.proto, ident=m.node.ident,
-                inputs=inputs, output=output);
+                inputs=inputs, output=output, cf=m.node.meth.decl.cf);
     }
 
     fn ty_of_obj(@ctxt cx,
@@ -542,7 +544,8 @@ mod collect {
             vec::push[arg](t_inputs, rec(mode=ty::mo_alias, ty=t_field));
         }
 
-        auto t_fn = ty::mk_fn(cx.tcx, ast::proto_fn, t_inputs, t_obj._1);
+        auto t_fn = ty::mk_fn(cx.tcx, ast::proto_fn, t_inputs, t_obj._1,
+                              ast::return);
 
         auto tpt = tup(t_obj._0, t_fn);
         cx.tcx.tcache.insert(ctor_id, tpt);
@@ -675,7 +678,8 @@ mod collect {
                     args += [rec(mode=ty::mo_alias, ty=arg_ty)];
                 }
                 auto tag_t = ty::mk_tag(cx.tcx, tag_id, ty_param_tys);
-                result_ty = ty::mk_fn(cx.tcx, ast::proto_fn, args, tag_t);
+                result_ty = ty::mk_fn(cx.tcx, ast::proto_fn, args, tag_t,
+                                      ast::return);
             }
 
             auto tpt = tup(ty_param_count, result_ty);
@@ -782,7 +786,7 @@ mod collect {
                         // TODO: typechecker botch
                         let vec[arg] no_args = [];
                         auto t = ty::mk_fn(cx.tcx, ast::proto_fn, no_args,
-                                           ty::mk_nil(cx.tcx));
+                                           ty::mk_nil(cx.tcx), ast::return);
                         write::ty_only(cx.tcx, m.node.ann.id, t);
                     }
                 }
@@ -1073,7 +1077,7 @@ fn variant_arg_types(&@crate_ctxt ccx, &span sp, &ast::def_id vid,
 
     auto tpt = ty::lookup_item_type(ccx.tcx, vid);
     alt (struct(ccx.tcx, tpt._1)) {
-        case (ty::ty_fn(_, ?ins, _)) {
+        case (ty::ty_fn(_, ?ins, _, _)) {
             // N-ary variant.
             for (ty::arg arg in ins) {
                 auto arg_ty = bind_params_in_type(ccx.tcx, arg.ty);
@@ -1660,7 +1664,7 @@ fn check_pat(&@stmt_ctxt scx, &@ast::pat pat) {
 
             alt (struct(scx.fcx.ccx.tcx, t)) {
                 // N-ary variants have function types.
-                case (ty::ty_fn(_, ?args, ?tag_ty)) {
+                case (ty::ty_fn(_, ?args, ?tag_ty, _)) {
                     auto arg_len = vec::len[arg](args);
                     auto subpats_len = vec::len[@ast::pat](subpats);
                     if (arg_len != subpats_len) {
@@ -1798,8 +1802,8 @@ fn check_expr(&@stmt_ctxt scx, &@ast::expr expr) {
         auto rt_0 = next_ty_var(scx);
         auto t_0;
         alt (struct(scx.fcx.ccx.tcx, expr_ty(scx.fcx.ccx.tcx, f))) {
-            case (ty::ty_fn(?proto, _, _))   {
-                t_0 = ty::mk_fn(scx.fcx.ccx.tcx, proto, arg_tys_0, rt_0);
+            case (ty::ty_fn(?proto, _, _, ?cf))   {
+                t_0 = ty::mk_fn(scx.fcx.ccx.tcx, proto, arg_tys_0, rt_0, cf);
             }
             case (ty::ty_native_fn(?abi, _, _))   {
                 t_0 = ty::mk_native_fn(scx.fcx.ccx.tcx, abi, arg_tys_0, rt_0);
@@ -2236,8 +2240,9 @@ fn check_expr(&@stmt_ctxt scx, &@ast::expr expr) {
             let vec[ty::arg] arg_tys_1 = [];
             auto rt_1;
             auto fty = expr_ty(scx.fcx.ccx.tcx, f);
+            auto t_1;
             alt (struct(scx.fcx.ccx.tcx, fty)) {
-                case (ty::ty_fn(?proto, ?arg_tys, ?rt)) {
+                case (ty::ty_fn(?proto, ?arg_tys, ?rt, ?cf)) {
                     proto_1 = proto;
                     rt_1 = rt;
 
@@ -2253,14 +2258,14 @@ fn check_expr(&@stmt_ctxt scx, &@ast::expr expr) {
                         }
                         i += 1u;
                     }
+                    t_1 = ty::mk_fn(scx.fcx.ccx.tcx, proto_1, arg_tys_1, rt_1,
+                                    cf);
                 }
                 case (_) {
                     log_err "LHS of bind expr didn't have a function type?!";
                     fail;
                 }
             }
-
-            auto t_1 = ty::mk_fn(scx.fcx.ccx.tcx, proto_1, arg_tys_1, rt_1);
             write::ty_only_fixup(scx, a.id, t_1);
         }
 
@@ -2277,7 +2282,7 @@ fn check_expr(&@stmt_ctxt scx, &@ast::expr expr) {
             auto rt_1 = ty::mk_nil(scx.fcx.ccx.tcx); // FIXME: typestate botch
             auto fty = expr_ty(scx.fcx.ccx.tcx, f);
             alt (struct(scx.fcx.ccx.tcx, fty)) {
-                case (ty::ty_fn(_,_,?rt))           { rt_1 = rt; }
+                case (ty::ty_fn(_,_,?rt,_))         { rt_1 = rt; }
                 case (ty::ty_native_fn(_, _, ?rt))  { rt_1 = rt; }
                 case (_) {
                     log_err "LHS of call expr didn't have a function type?!";
@@ -2321,7 +2326,7 @@ fn check_expr(&@stmt_ctxt scx, &@ast::expr expr) {
             // Check the return type
             auto fty = expr_ty(scx.fcx.ccx.tcx, f);
             alt (struct(scx.fcx.ccx.tcx, fty)) {
-                case (ty::ty_fn(_,_,?rt)) {
+                case (ty::ty_fn(_,_,?rt,_)) {
                     alt (struct(scx.fcx.ccx.tcx, rt)) {
                         case (ty::ty_nil) {
                             // This is acceptable
@@ -2485,7 +2490,7 @@ fn check_expr(&@stmt_ctxt scx, &@ast::expr expr) {
                     }
                     auto meth = methods.(ix);
                     auto t = ty::mk_fn(scx.fcx.ccx.tcx, meth.proto,
-                                       meth.inputs, meth.output);
+                                       meth.inputs, meth.output, meth.cf);
                     write::ty_only_fixup(scx, a.id, t);
                 }
 
diff --git a/src/comp/middle/walk.rs b/src/comp/middle/walk.rs
index 5c0c5b3667f9c..cac5fd0ea3533 100644
--- a/src/comp/middle/walk.rs
+++ b/src/comp/middle/walk.rs
@@ -163,7 +163,7 @@ fn walk_ty(&ast_visitor v, @ast::ty t) {
                 walk_ty(v, f.mt.ty);
             }
         }
-        case (ast::ty_fn(_, ?args, ?out)) {
+        case (ast::ty_fn(_, ?args, ?out, _)) {
             for (ast::ty_arg a in args) {
                 walk_ty(v, a.ty);
             }
diff --git a/src/comp/pretty/pprust.rs b/src/comp/pretty/pprust.rs
index 695f7bf9c275d..d104e862dc807 100644
--- a/src/comp/pretty/pprust.rs
+++ b/src/comp/pretty/pprust.rs
@@ -137,6 +137,7 @@ fn print_type(ps s, &@ast::ty ty) {
     alt (ty.node) {
         case (ast::ty_nil) {wrd(s.s, "()");}
         case (ast::ty_bool) {wrd(s.s, "bool");}
+        case (ast::ty_bot) {wrd(s.s, "_|_");}
         case (ast::ty_int) {wrd(s.s, "int");}
         case (ast::ty_uint) {wrd(s.s, "uint");}
         case (ast::ty_float) {wrd(s.s, "float");}
@@ -188,15 +189,15 @@ fn print_type(ps s, &@ast::ty ty) {
             for (ast::ty_method m in methods) {
                 hbox(s);
                 print_ty_fn(s, m.proto, option::some[str](m.ident),
-                            m.inputs, m.output);
+                            m.inputs, m.output, m.cf);
                 wrd(s.s, ";");
                 end(s.s);
                 line(s.s);
             }
             bclose_c(s, ty.span);
         }
-        case (ast::ty_fn(?proto,?inputs,?output)) {
-            print_ty_fn(s, proto, option::none[str], inputs, output);
+        case (ast::ty_fn(?proto,?inputs,?output,?cf)) {
+            print_ty_fn(s, proto, option::none[str], inputs, output, cf);
         }
         case (ast::ty_path(?path,_)) {
             print_path(s, path);
@@ -978,7 +979,8 @@ fn print_string(ps s, str st) {
 }
 
 fn print_ty_fn(ps s, ast::proto proto, option::t[str] id,
-               vec[ast::ty_arg] inputs, @ast::ty output) {
+               vec[ast::ty_arg] inputs, @ast::ty output,
+               ast::controlflow cf) {
     if (proto == ast::proto_fn) {wrd(s.s, "fn");}
     else {wrd(s.s, "iter");}
     alt (id) {
@@ -998,7 +1000,14 @@ fn print_ty_fn(ps s, ast::proto proto, option::t[str] id,
         space(s.s);
         hbox(s);
         wrd1(s, "->");
-        print_type(s, output);
+        alt (cf) {
+            case (ast::return) {
+                print_type(s, output);
+            }
+            case (ast::noreturn) {
+                wrd1(s, "!");
+            }
+        }
         end(s.s);
     }
 }
diff --git a/src/comp/util/common.rs b/src/comp/util/common.rs
index 090ad57027ec5..4e0714cd42927 100644
--- a/src/comp/util/common.rs
+++ b/src/comp/util/common.rs
@@ -45,6 +45,11 @@ tag ty_mach {
     ty_f64;
 }
 
+tag ty_or_bang[T] {
+    a_ty(T);
+    a_bang;
+}
+
 fn ty_mach_to_str(ty_mach tm) -> str {
     alt (tm) {
         case (ty_u8) { ret "u8"; }