diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 21b0af9420fab..8667f36c749e5 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -1059,6 +1059,25 @@ enum region_ {
     re_named(ident)
 }
 
+#[auto_serialize]
+#[auto_deserialize]
+enum Onceness {
+    Once,
+    Many
+}
+
+impl Onceness : cmp::Eq {
+    pure fn eq(other: &Onceness) -> bool {
+        match (self, *other) {
+            (Once, Once) | (Many, Many) => true,
+            _ => false
+        }
+    }
+    pure fn ne(other: &Onceness) -> bool {
+        !self.eq(other)
+    }
+}
+
 #[auto_serialize]
 #[auto_deserialize]
 enum ty_ {
@@ -1070,7 +1089,7 @@ enum ty_ {
     ty_ptr(mt),
     ty_rptr(@region, mt),
     ty_rec(~[ty_field]),
-    ty_fn(proto, purity, @~[ty_param_bound], fn_decl),
+    ty_fn(proto, purity, Onceness, @~[ty_param_bound], fn_decl),
     ty_tup(~[@Ty]),
     ty_path(@path, node_id),
     ty_fixed_length(@Ty, Option<uint>),
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index a8b0f44451481..7c8909668f535 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -524,10 +524,11 @@ fn noop_fold_ty(t: ty_, fld: ast_fold) -> ty_ {
       ty_ptr(mt) => ty_ptr(fold_mt(mt, fld)),
       ty_rptr(region, mt) => ty_rptr(region, fold_mt(mt, fld)),
       ty_rec(fields) => ty_rec(vec::map(fields, |f| fold_field(*f, fld))),
-      ty_fn(proto, purity, bounds, decl) =>
-        ty_fn(proto, purity,
-              @vec::map(*bounds,
-                        |x| fold_ty_param_bound(*x, fld)),
+      ty_fn(proto, purity, onceness, bounds, decl) =>
+        ty_fn(proto,
+              purity,
+              onceness,
+              @vec::map(*bounds, |x| fold_ty_param_bound(*x, fld)),
               fold_fn_decl(decl, fld)),
       ty_tup(tys) => ty_tup(vec::map(tys, |ty| fld.fold_ty(*ty))),
       ty_path(path, id) => ty_path(fld.fold_path(path), fld.new_id(id)),
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 6615bc751694b..63545a6960872 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -287,7 +287,7 @@ impl Parser {
 
     pure fn id_to_str(id: ident) -> @~str { self.sess.interner.get(id) }
 
-    fn parse_ty_fn(purity: ast::purity) -> ty_ {
+    fn parse_ty_fn(purity: ast::purity, onceness: ast::Onceness) -> ty_ {
         let proto, bounds;
         if self.eat_keyword(~"extern") {
             self.expect_keyword(~"fn");
@@ -298,7 +298,17 @@ impl Parser {
             proto = self.parse_fn_ty_proto();
             bounds = self.parse_optional_ty_param_bounds();
         };
-        ty_fn(proto, purity, bounds, self.parse_ty_fn_decl())
+        ty_fn(proto, purity, onceness, bounds, self.parse_ty_fn_decl())
+    }
+
+    fn parse_ty_fn_with_onceness(purity: ast::purity) -> ty_ {
+        let onceness = self.parse_optional_onceness();
+        self.parse_ty_fn(purity, onceness)
+    }
+
+    fn parse_ty_fn_with_purity_and_onceness() -> ty_ {
+        let purity = self.parse_optional_purity();
+        self.parse_ty_fn_with_onceness(purity)
     }
 
     fn parse_ty_fn_decl() -> fn_decl {
@@ -526,15 +536,18 @@ impl Parser {
             let region = self.parse_region_with_sep();
             let mt = self.parse_mt();
             ty_rptr(region, mt)
+        } else if self.eat_keyword(~"once") {
+            self.parse_ty_fn(ast::impure_fn, ast::Once)
         } else if self.eat_keyword(~"pure") {
-            self.parse_ty_fn(ast::pure_fn)
+            self.parse_ty_fn_with_onceness(ast::pure_fn)
         } else if self.eat_keyword(~"unsafe") {
-            self.parse_ty_fn(ast::unsafe_fn)
+            self.parse_ty_fn_with_onceness(ast::unsafe_fn)
         } else if self.is_keyword(~"fn") {
-            self.parse_ty_fn(ast::impure_fn)
+            self.parse_ty_fn_with_onceness(ast::impure_fn)
         } else if self.eat_keyword(~"extern") {
             self.expect_keyword(~"fn");
-            ty_fn(proto_bare, ast::impure_fn, @~[], self.parse_ty_fn_decl())
+            ty_fn(proto_bare, ast::impure_fn, ast::Many, @~[],
+                  self.parse_ty_fn_decl())
         } else if self.token == token::MOD_SEP || is_ident(self.token) {
             let path = self.parse_path_with_tps(colons_before_params);
             ty_path(path, self.get_id())
@@ -2275,6 +2288,20 @@ impl Parser {
             self.get_id()), span: self.last_span}
     }
 
+    fn parse_optional_purity() -> ast::purity {
+        if self.eat_keyword(~"pure") {
+            ast::pure_fn
+        } else if self.eat_keyword(~"unsafe") {
+            ast::unsafe_fn
+        } else {
+            ast::impure_fn
+        }
+    }
+
+    fn parse_optional_onceness() -> ast::Onceness {
+        if self.eat_keyword(~"once") { ast::Once } else { ast::Many }
+    }
+
     fn parse_optional_ty_param_bounds() -> @~[ty_param_bound] {
         let mut bounds = ~[];
         if self.eat(token::COLON) {
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 53c1ce1c7f535..0d139b101d8f3 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -427,6 +427,7 @@ fn strict_keyword_table() -> HashMap<~str, ()> {
         ~"if", ~"impl",
         ~"let", ~"log", ~"loop",
         ~"match", ~"mod", ~"move", ~"mut",
+        ~"once",
         ~"priv", ~"pub", ~"pure",
         ~"ref", ~"return",
         ~"struct",
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 3836c21ff2449..e0b9958bcb748 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -394,8 +394,9 @@ fn print_type_ex(s: ps, &&ty: @ast::Ty, print_colons: bool) {
         commasep(s, inconsistent, elts, print_type);
         pclose(s);
       }
-      ast::ty_fn(proto, purity, bounds, d) => {
-        print_ty_fn(s, Some(proto), purity, bounds, d, None, None, None);
+      ast::ty_fn(proto, purity, onceness, bounds, d) => {
+        print_ty_fn(s, Some(proto), purity, onceness, bounds, d, None, None,
+                    None);
       }
       ast::ty_path(path, _) => print_path(s, path, print_colons),
       ast::ty_fixed_length(t, v) => {
@@ -804,7 +805,7 @@ fn print_ty_method(s: ps, m: ast::ty_method) {
     hardbreak_if_not_bol(s);
     maybe_print_comment(s, m.span.lo);
     print_outer_attributes(s, m.attrs);
-    print_ty_fn(s, None, m.purity,
+    print_ty_fn(s, None, m.purity, ast::Many,
                 @~[], m.decl, Some(m.ident), Some(m.tps),
                 Some(m.self_ty.node));
     word(s.s, ~";");
@@ -1273,7 +1274,7 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
         cbox(s, indent_unit);
         // head-box, will be closed by print-block at start
         ibox(s, 0u);
-        word(s.s, fn_header_info_to_str(None, None, Some(proto),
+        word(s.s, fn_header_info_to_str(None, None, Some(proto), ast::Many,
                                         ast::inherited));
         print_fn_args_and_ret(s, decl, *cap_clause, None);
         space(s.s);
@@ -1606,12 +1607,15 @@ fn print_self_ty(s: ps, self_ty: ast::self_ty_) -> bool {
     return true;
 }
 
-fn print_fn(s: ps, decl: ast::fn_decl, purity: Option<ast::purity>,
+fn print_fn(s: ps,
+            decl: ast::fn_decl,
+            purity: Option<ast::purity>,
             name: ast::ident,
             typarams: ~[ast::ty_param],
             opt_self_ty: Option<ast::self_ty_>,
             vis: ast::visibility) {
-    head(s, fn_header_info_to_str(opt_self_ty, purity, None, vis));
+    head(s, fn_header_info_to_str(opt_self_ty, purity, None, ast::Many,
+                                  vis));
     print_ident(s, name);
     print_type_params(s, typarams);
     print_fn_args_and_ret(s, decl, ~[], opt_self_ty);
@@ -1831,14 +1835,17 @@ fn print_arg(s: ps, input: ast::arg) {
     end(s);
 }
 
-fn print_ty_fn(s: ps, opt_proto: Option<ast::proto>, purity: ast::purity,
+fn print_ty_fn(s: ps,
+               opt_proto: Option<ast::proto>,
+               purity: ast::purity,
+               onceness: ast::Onceness,
                bounds: @~[ast::ty_param_bound],
                decl: ast::fn_decl, id: Option<ast::ident>,
                tps: Option<~[ast::ty_param]>,
                opt_self_ty: Option<ast::self_ty_>) {
     ibox(s, indent_unit);
     word(s.s, fn_header_info_to_str(opt_self_ty, Some(purity), opt_proto,
-                                    ast::inherited));
+                                    onceness, ast::inherited));
     print_bounds(s, bounds);
     match id { Some(id) => { word(s.s, ~" "); print_ident(s, id); } _ => () }
     match tps { Some(tps) => print_type_params(s, tps), _ => () }
@@ -2062,6 +2069,7 @@ fn next_comment(s: ps) -> Option<comments::cmnt> {
 fn fn_header_info_to_str(opt_sty: Option<ast::self_ty_>,
                          opt_purity: Option<ast::purity>,
                          opt_p: Option<ast::proto>,
+                         onceness: ast::Onceness,
                          vis: ast::visibility) -> ~str {
 
     let mut s = visibility_qualified(vis, ~"");
@@ -2082,6 +2090,11 @@ fn fn_header_info_to_str(opt_sty: Option<ast::self_ty_>,
 
     str::push_str(&mut s, opt_proto_to_str(opt_p));
 
+    match onceness {
+        ast::Once => str::push_str(&mut s, ~"once "),
+        ast::Many => {}
+    }
+
     return s;
 }
 
@@ -2101,6 +2114,13 @@ pure fn purity_to_str(p: ast::purity) -> ~str {
     }
 }
 
+pure fn onceness_to_str(o: ast::Onceness) -> ~str {
+    match o {
+        ast::Once => ~"once",
+        ast::Many => ~"many"
+    }
+}
+
 fn print_purity(s: ps, p: ast::purity) {
     match p {
       ast::impure_fn => (),
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 2dbe2b16044ef..be6fb4cefa83e 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -203,7 +203,7 @@ fn visit_ty<E>(t: @Ty, e: E, v: vt<E>) {
       ty_tup(ts) => for ts.each |tt| {
         v.visit_ty(*tt, e, v);
       },
-      ty_fn(_, _, bounds, decl) => {
+      ty_fn(_, _, _, bounds, decl) => {
         for decl.inputs.each |a| { v.visit_ty(a.ty, e, v); }
         visit_ty_param_bounds(bounds, e, v);
         v.visit_ty(decl.output, e, v);
diff --git a/src/rustc/metadata/tydecode.rs b/src/rustc/metadata/tydecode.rs
index 14aef6db1adbd..f15c254e71c00 100644
--- a/src/rustc/metadata/tydecode.rs
+++ b/src/rustc/metadata/tydecode.rs
@@ -387,6 +387,14 @@ fn parse_purity(c: char) -> purity {
     }
 }
 
+fn parse_onceness(c: char) -> ast::Onceness {
+    match c {
+        'o' => ast::Once,
+        'm' => ast::Many,
+        _ => fail ~"parse_onceness: bad onceness"
+    }
+}
+
 fn parse_arg(st: @pstate, conv: conv_did) -> ty::arg {
     {mode: parse_mode(st),
      ty: parse_ty(st, conv)}
@@ -406,6 +414,7 @@ fn parse_mode(st: @pstate) -> ast::mode {
 fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy {
     let proto = parse_proto(st);
     let purity = parse_purity(next(st));
+    let onceness = parse_onceness(next(st));
     let bounds = parse_bounds(st, conv);
     assert (next(st) == '[');
     let mut inputs: ~[ty::arg] = ~[];
@@ -418,6 +427,7 @@ fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy {
     return FnTyBase {
         meta: FnMeta {purity: purity,
                       proto: proto,
+                      onceness: onceness,
                       bounds: bounds,
                       ret_style: ret_style},
         sig: FnSig {inputs: inputs,
diff --git a/src/rustc/metadata/tyencode.rs b/src/rustc/metadata/tyencode.rs
index e69815379737c..725b8c41f5cbb 100644
--- a/src/rustc/metadata/tyencode.rs
+++ b/src/rustc/metadata/tyencode.rs
@@ -349,9 +349,17 @@ fn enc_purity(w: io::Writer, p: purity) {
     }
 }
 
+fn enc_onceness(w: io::Writer, o: Onceness) {
+    match o {
+        Once => w.write_char('o'),
+        Many => w.write_char('m')
+    }
+}
+
 fn enc_ty_fn(w: io::Writer, cx: @ctxt, ft: ty::FnTy) {
     enc_proto(w, cx, ft.meta.proto);
     enc_purity(w, ft.meta.purity);
+    enc_onceness(w, ft.meta.onceness);
     enc_bounds(w, cx, ft.meta.bounds);
     w.write_char('[');
     for ft.sig.inputs.each |arg| {
diff --git a/src/rustc/middle/lint.rs b/src/rustc/middle/lint.rs
index 192b2924cb14d..9d28e35acb0de 100644
--- a/src/rustc/middle/lint.rs
+++ b/src/rustc/middle/lint.rs
@@ -841,7 +841,7 @@ fn check_fn_deprecated_modes(tcx: ty::ctxt, fn_ty: ty::t, decl: ast::fn_decl,
                         let span = arg_ast.ty.span;
                         // Recurse to check fn-type argument
                         match arg_ast.ty.node {
-                            ast::ty_fn(_, _, _, decl) => {
+                            ast::ty_fn(_, _, _, _, decl) => {
                                 check_fn_deprecated_modes(tcx, arg_ty.ty,
                                                           decl, span, id);
                             }
@@ -856,7 +856,7 @@ fn check_fn_deprecated_modes(tcx: ty::ctxt, fn_ty: ty::t, decl: ast::fn_decl,
                                 // Functions with preceding sigil are parsed
                                 // as pointers of functions
                                 match mt.ty.node {
-                                    ast::ty_fn(_, _, _, decl) => {
+                                    ast::ty_fn(_, _, _, _, decl) => {
                                         check_fn_deprecated_modes(
                                             tcx, arg_ty.ty,
                                             decl, span, id);
@@ -889,7 +889,7 @@ fn check_item_deprecated_modes(tcx: ty::ctxt, it: @ast::item) {
     match it.node {
         ast::item_ty(ty, _) => {
             match ty.node {
-                ast::ty_fn(_, _, _, decl) => {
+                ast::ty_fn(_, _, _, _, decl) => {
                     let fn_ty = ty::node_id_to_type(tcx, it.id);
                     check_fn_deprecated_modes(
                         tcx, fn_ty, decl, ty.span, it.id)
diff --git a/src/rustc/middle/region.rs b/src/rustc/middle/region.rs
index 415cd12536c05..a98ec2181d923 100644
--- a/src/rustc/middle/region.rs
+++ b/src/rustc/middle/region.rs
@@ -624,8 +624,8 @@ fn determine_rp_in_ty(ty: @ast::Ty,
         }
       }
 
-      ast::ty_fn(ast::proto_bare, _, _, _) |
-      ast::ty_fn(ast::proto_block, _, _, _) if cx.anon_implies_rp => {
+      ast::ty_fn(ast::proto_bare, _, _, _, _) |
+      ast::ty_fn(ast::proto_block, _, _, _, _) if cx.anon_implies_rp => {
         debug!("referenced bare fn type with regions %s",
                pprust::ty_to_str(ty, cx.sess.intr()));
         cx.add_rp(cx.item_id, cx.add_variance(rv_contravariant));
@@ -672,8 +672,8 @@ fn determine_rp_in_ty(ty: @ast::Ty,
     match ty.node {
       ast::ty_box(mt) | ast::ty_uniq(mt) => {
         match mt.ty.node {
-          ast::ty_fn(ast::proto_bare, _, _, _) |
-          ast::ty_fn(ast::proto_block, _, _, _) => {
+          ast::ty_fn(ast::proto_bare, _, _, _, _) |
+          ast::ty_fn(ast::proto_block, _, _, _, _) => {
             do cx.with(cx.item_id, false) {
                 visit_mt(mt, cx, visitor);
             }
@@ -706,7 +706,7 @@ fn determine_rp_in_ty(ty: @ast::Ty,
         }
       }
 
-      ast::ty_fn(_, _, bounds, decl) => {
+      ast::ty_fn(_, _, _, bounds, decl) => {
         // fn() binds the & region, so do not consider &T types that
         // appear *inside* a fn() type to affect the enclosing item:
         do cx.with(cx.item_id, false) {
diff --git a/src/rustc/middle/trans/foreign.rs b/src/rustc/middle/trans/foreign.rs
index 8fa23ef8fabd8..933fb7eedfba1 100644
--- a/src/rustc/middle/trans/foreign.rs
+++ b/src/rustc/middle/trans/foreign.rs
@@ -1000,6 +1000,7 @@ fn trans_intrinsic(ccx: @crate_ctxt, decl: ValueRef, item: @ast::foreign_item,
                               proto:
                                   ty::proto_vstore(ty::vstore_slice(
                                       ty::re_bound(ty::br_anon(0)))),
+                              onceness: ast::Many,
                               bounds: @~[],
                               ret_style: ast::return_val},
                 sig: FnSig {inputs: ~[{mode: ast::expl(ast::by_val),
diff --git a/src/rustc/middle/trans/monomorphize.rs b/src/rustc/middle/trans/monomorphize.rs
index 4a569e2cf4de4..0c93c610b681d 100644
--- a/src/rustc/middle/trans/monomorphize.rs
+++ b/src/rustc/middle/trans/monomorphize.rs
@@ -250,6 +250,7 @@ fn normalize_for_monomorphization(tcx: ty::ctxt, ty: ty::t) -> Option<ty::t> {
                 tcx,
                 FnTyBase {meta: FnMeta {purity: ast::impure_fn,
                                         proto: fty.meta.proto,
+                                        onceness: ast::Many,
                                         bounds: @~[],
                                         ret_style: ast::return_val},
                           sig: FnSig {inputs: ~[],
@@ -261,6 +262,7 @@ fn normalize_for_monomorphization(tcx: ty::ctxt, ty: ty::t) -> Option<ty::t> {
                 tcx,
                 FnTyBase {meta: FnMeta {purity: ast::impure_fn,
                                         proto: box_proto,
+                                        onceness: ast::Many,
                                         bounds: @~[],
                                         ret_style: ast::return_val},
                           sig: FnSig {inputs: ~[],
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index 4391388331c63..80f48eace8f45 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -126,6 +126,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 type_err_to_str, note_and_explain_type_err;
 export expected_found;
 export type_needs_drop;
@@ -186,6 +187,7 @@ export terr_proto_mismatch;
 export terr_ret_style_mismatch;
 export terr_fn, terr_trait;
 export purity_to_str;
+export onceness_to_str;
 export param_tys_in_type;
 export eval_repeat_count;
 export fn_proto, proto_bare, proto_vstore;
@@ -504,11 +506,14 @@ impl fn_proto : cmp::Eq {
  *
  * - `purity` is the function's effect (pure, impure, unsafe).
  * - `proto` is the protocol (fn@, fn~, etc).
+ * - `onceness` indicates whether the function can be called one time or many
+ *   times.
  * - `bounds` is the parameter bounds on the function's upvars.
  * - `ret_style` indicates whether the function returns a value or fails. */
 struct FnMeta {
     purity: ast::purity,
     proto: fn_proto,
+    onceness: ast::Onceness,
     bounds: @~[param_bound],
     ret_style: ret_style
 }
@@ -679,6 +684,7 @@ enum type_err {
     terr_mismatch,
     terr_ret_style_mismatch(expected_found<ast::ret_style>),
     terr_purity_mismatch(expected_found<purity>),
+    terr_onceness_mismatch(expected_found<Onceness>),
     terr_mutability,
     terr_proto_mismatch(expected_found<ty::fn_proto>),
     terr_box_mutability,
@@ -3326,6 +3332,11 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str {
                  purity_to_str(values.expected),
                  purity_to_str(values.found))
         }
+        terr_onceness_mismatch(values) => {
+            fmt!("expected %s fn but found %s fn",
+                 onceness_to_str(values.expected),
+                 onceness_to_str(values.found))
+        }
         terr_proto_mismatch(values) => {
             fmt!("expected %s closure, found %s closure",
                  proto_ty_to_str(cx, values.expected),
diff --git a/src/rustc/middle/typeck/astconv.rs b/src/rustc/middle/typeck/astconv.rs
index d793955e447dc..b18b032e0a616 100644
--- a/src/rustc/middle/typeck/astconv.rs
+++ b/src/rustc/middle/typeck/astconv.rs
@@ -208,7 +208,8 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope Copy Owned>(
               _ => ()
             }
           }
-          ast::ty_fn(ast::proto_block, purity, ast_bounds, ast_fn_decl) => {
+          ast::ty_fn(ast::proto_block, purity, onceness, ast_bounds,
+                     ast_fn_decl) => {
             let new_proto;
             match vst {
                 ty::vstore_fixed(_) => {
@@ -223,9 +224,15 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope Copy Owned>(
 
             // Run through the normal function type conversion process.
             let bounds = collect::compute_bounds(self.ccx(), ast_bounds);
-            let fn_decl = ty_of_fn_decl(self, rscope, new_proto, purity,
+            let fn_decl = ty_of_fn_decl(self,
+                                        rscope,
+                                        new_proto,
+                                        purity,
+                                        onceness,
                                         bounds,
-                                        ast_fn_decl, None, span);
+                                        ast_fn_decl,
+                                        None,
+                                        span);
             return ty::mk_fn(tcx, fn_decl);
           }
           _ => ()
@@ -309,10 +316,10 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope Copy Owned>(
         };
         ty::mk_rec(tcx, flds)
       }
-      ast::ty_fn(proto, purity, ast_bounds, decl) => {
+      ast::ty_fn(proto, purity, onceness, ast_bounds, decl) => {
         let bounds = collect::compute_bounds(self.ccx(), ast_bounds);
         let fn_decl = ty_of_fn_decl(self, rscope, proto, purity,
-                                    bounds, decl, None,
+                                    onceness, bounds, decl, None,
                                     ast_ty.span);
         ty::mk_fn(tcx, fn_decl)
       }
@@ -476,6 +483,7 @@ fn ty_of_fn_decl<AC: ast_conv, RS: region_scope Copy Owned>(
     self: AC, rscope: RS,
     ast_proto: ast::proto,
     purity: ast::purity,
+    onceness: ast::Onceness,
     bounds: @~[ty::param_bound],
     decl: ast::fn_decl,
     expected_tys: expected_tys,
@@ -508,6 +516,7 @@ fn ty_of_fn_decl<AC: ast_conv, RS: region_scope Copy Owned>(
         FnTyBase {
             meta: FnMeta {purity: purity,
                           proto: proto,
+                          onceness: onceness,
                           bounds: bounds,
                           ret_style: decl.cf},
             sig: FnSig {inputs: input_tys,
diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs
index ae123f8dc5a0c..09225b4fef8a9 100644
--- a/src/rustc/middle/typeck/check.rs
+++ b/src/rustc/middle/typeck/check.rs
@@ -1310,7 +1310,10 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         // block syntax lambdas; that is, lambdas without explicit
         // protos.
         let expected_sty = unpack_expected(fcx, expected, |x| Some(x));
-        let (expected_tys, expected_purity, expected_proto) =
+        let (expected_tys,
+             expected_purity,
+             expected_proto,
+             expected_onceness) =
             match expected_sty {
               Some(ty::ty_fn(ref fn_ty)) => {
                 let {fn_ty, _} =
@@ -1320,10 +1323,14 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                 (Some({inputs: fn_ty.sig.inputs,
                        output: fn_ty.sig.output}),
                  fn_ty.meta.purity,
-                 fn_ty.meta.proto)
+                 fn_ty.meta.proto,
+                 fn_ty.meta.onceness)
               }
               _ => {
-                (None, ast::impure_fn, ty::proto_vstore(ty::vstore_box))
+                (None,
+                 ast::impure_fn,
+                 ty::proto_vstore(ty::vstore_box),
+                 ast::Many)
               }
             };
 
@@ -1334,17 +1341,25 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         // XXX: This is a hack.
         let ast_proto = ast_proto_opt.get_default(ast::proto_box);
         let ast_purity = ast::impure_fn;
+        let ast_onceness = ast::Many;
 
         // construct the function type
-        let mut fn_ty = astconv::ty_of_fn_decl(fcx, fcx,
-                                               ast_proto, ast_purity, @~[],
-                                               decl, expected_tys, expr.span);
+        let mut fn_ty = astconv::ty_of_fn_decl(fcx,
+                                               fcx,
+                                               ast_proto,
+                                               ast_purity,
+                                               ast_onceness,
+                                               @~[],
+                                               decl,
+                                               expected_tys,
+                                               expr.span);
 
         // Patch up the function declaration, if necessary.
         match ast_proto_opt {
           None => {
             fn_ty.meta.purity = expected_purity;
             fn_ty.meta.proto = expected_proto;
+            fn_ty.meta.onceness = expected_onceness;
           }
           Some(_) => { }
         }
@@ -2802,6 +2817,7 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) {
             meta: FnMeta {purity: ast::impure_fn,
                           proto: ty::proto_vstore(ty::vstore_slice(
                               ty::re_bound(ty::br_anon(0)))),
+                          onceness: ast::Many,
                           bounds: @~[],
                           ret_style: ast::return_val},
             sig: FnSig {inputs: ~[{mode: ast::expl(ast::by_val),
@@ -2825,6 +2841,7 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) {
     let fty = ty::mk_fn(tcx, FnTyBase {
         meta: FnMeta {purity: ast::impure_fn,
                       proto: ty::proto_bare,
+                      onceness: ast::Many,
                       bounds: @~[],
                       ret_style: ast::return_val},
         sig: FnSig {inputs: inputs,
diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs
index b82a55dc86bdf..04f7aae0bf2a0 100644
--- a/src/rustc/middle/typeck/collect.rs
+++ b/src/rustc/middle/typeck/collect.rs
@@ -134,6 +134,7 @@ fn get_enum_variant_types(ccx: @crate_ctxt,
                 result_ty = Some(ty::mk_fn(tcx, FnTyBase {
                     meta: FnMeta {purity: ast::pure_fn,
                                   proto: ty::proto_vstore(ty::vstore_box),
+                                  onceness: ast::Many,
                                   bounds: @~[],
                                   ret_style: ast::return_val},
                     sig: FnSig {inputs: args,
@@ -604,7 +605,7 @@ fn convert_struct(ccx: @crate_ctxt,
         let t_dtor = ty::mk_fn(
             tcx,
             ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare,
-                          ast::impure_fn, @~[],
+                          ast::impure_fn, ast::Many, @~[],
                           ast_util::dtor_dec(), None, dtor.span));
         write_ty_to_tcx(tcx, dtor.node.id, t_dtor);
         tcx.tcache.insert(local_def(dtor.node.id),
@@ -643,6 +644,7 @@ fn convert_struct(ccx: @crate_ctxt,
                     meta: FnMeta {
                         purity: ast::pure_fn,
                         proto: ty::proto_bare,
+                        onceness: ast::Many,
                         bounds: @~[],
                         ret_style: ast::return_val,
                     },
@@ -682,9 +684,15 @@ fn ty_of_method(ccx: @crate_ctxt,
                 rp: Option<ty::region_variance>) -> ty::method {
     {ident: m.ident,
      tps: ty_param_bounds(ccx, m.tps),
-     fty: ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare,
-                        m.purity, @~[],
-                        m.decl, None, m.span),
+     fty: ty_of_fn_decl(ccx,
+                        type_rscope(rp),
+                        ast::proto_bare,
+                        m.purity,
+                        ast::Many,
+                        @~[],
+                        m.decl,
+                        None,
+                        m.span),
      self_ty: m.self_ty.node,
      vis: m.vis,
      def_id: local_def(m.id)}
@@ -696,8 +704,15 @@ fn ty_of_ty_method(self: @crate_ctxt,
                    id: ast::def_id) -> ty::method {
     {ident: m.ident,
      tps: ty_param_bounds(self, m.tps),
-     fty: ty_of_fn_decl(self, type_rscope(rp), ast::proto_bare, m.purity,
-                        @~[], m.decl, None, m.span),
+     fty: ty_of_fn_decl(self,
+                        type_rscope(rp),
+                        ast::proto_bare,
+                        m.purity,
+                        ast::Many,
+                        @~[],
+                        m.decl,
+                        None,
+                        m.span),
      // assume public, because this is only invoked on trait methods
      self_ty: m.self_ty.node,
      vis: ast::public,
@@ -752,9 +767,15 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
       }
       ast::item_fn(decl, purity, tps, _) => {
         let bounds = ty_param_bounds(ccx, tps);
-        let tofd = ty_of_fn_decl(ccx, empty_rscope,
-                                 ast::proto_bare, purity, @~[],
-                                 decl, None, it.span);
+        let tofd = ty_of_fn_decl(ccx,
+                                 empty_rscope,
+                                 ast::proto_bare,
+                                 purity,
+                                 ast::Many,
+                                 @~[],
+                                 decl,
+                                 None,
+                                 it.span);
         let tpt = {bounds: bounds,
                    region_param: None,
                    ty: ty::mk_fn(ccx.tcx, tofd)};
@@ -910,6 +931,7 @@ fn ty_of_foreign_fn_decl(ccx: @crate_ctxt,
     let t_fn = ty::mk_fn(ccx.tcx, FnTyBase {
         meta: FnMeta {purity: purity,
                       proto: ty::proto_bare,
+                      onceness: ast::Many,
                       bounds: @~[],
                       ret_style: ast::return_val},
         sig: FnSig {inputs: input_tys,
diff --git a/src/rustc/middle/typeck/infer/combine.rs b/src/rustc/middle/typeck/infer/combine.rs
index 342a2ce2b76cd..87f313f68bd9b 100644
--- a/src/rustc/middle/typeck/infer/combine.rs
+++ b/src/rustc/middle/typeck/infer/combine.rs
@@ -46,6 +46,7 @@
 
 use to_str::ToStr;
 use ty::{FnTyBase, FnMeta, FnSig};
+use syntax::ast::Onceness;
 
 trait combine {
     fn infcx() -> infer_ctxt;
@@ -72,6 +73,7 @@ trait combine {
     fn protos(p1: ty::fn_proto, p2: ty::fn_proto) -> cres<ty::fn_proto>;
     fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style>;
     fn purities(a: purity, b: purity) -> cres<purity>;
+    fn oncenesses(a: Onceness, b: Onceness) -> cres<Onceness>;
     fn contraregions(a: ty::Region, b: ty::Region) -> cres<ty::Region>;
     fn regions(a: ty::Region, b: ty::Region) -> cres<ty::Region>;
     fn vstores(vk: ty::terr_vstore_kind,
@@ -311,10 +313,14 @@ fn super_fn_metas<C:combine>(
     do self.protos(a_f.proto, b_f.proto).chain |p| {
         do self.ret_styles(a_f.ret_style, b_f.ret_style).chain |rs| {
             do self.purities(a_f.purity, b_f.purity).chain |purity| {
-                Ok(FnMeta {purity: purity,
-                           proto: p,
-                           bounds: a_f.bounds, // XXX: This is wrong!
-                           ret_style: rs})
+                do self.oncenesses(a_f.onceness, b_f.onceness).chain
+                        |onceness| {
+                    Ok(FnMeta {purity: purity,
+                               proto: p,
+                               onceness: onceness,
+                               bounds: a_f.bounds, // XXX: This is wrong!
+                               ret_style: rs})
+                }
             }
         }
     }
diff --git a/src/rustc/middle/typeck/infer/glb.rs b/src/rustc/middle/typeck/infer/glb.rs
index 77e753fa2204f..0e81d97426f36 100644
--- a/src/rustc/middle/typeck/infer/glb.rs
+++ b/src/rustc/middle/typeck/infer/glb.rs
@@ -1,6 +1,7 @@
 use combine::*;
 use lattice::*;
 use to_str::ToStr;
+use syntax::ast::{Many, Once};
 
 enum Glb = combine_fields;  // "greatest lower bound" (common subtype)
 
@@ -97,6 +98,13 @@ impl Glb: combine {
         }
     }
 
+    fn oncenesses(a: Onceness, b: Onceness) -> cres<Onceness> {
+        match (a, b) {
+            (Many, _) | (_, Many) => Ok(Many),
+            (Once, Once) => Ok(Once)
+        }
+    }
+
     fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style> {
         match (r1, r2) {
           (ast::return_val, ast::return_val) => {
diff --git a/src/rustc/middle/typeck/infer/lub.rs b/src/rustc/middle/typeck/infer/lub.rs
index dcff863a126f0..285b05736c63e 100644
--- a/src/rustc/middle/typeck/infer/lub.rs
+++ b/src/rustc/middle/typeck/infer/lub.rs
@@ -1,6 +1,7 @@
 use combine::*;
 use lattice::*;
 use to_str::ToStr;
+use syntax::ast::{Many, Once};
 
 enum Lub = combine_fields;  // "subtype", "subregion" etc
 
@@ -80,6 +81,13 @@ impl Lub: combine {
         }
     }
 
+    fn oncenesses(a: Onceness, b: Onceness) -> cres<Onceness> {
+        match (a, b) {
+            (Once, _) | (_, Once) => Ok(Once),
+            (Many, Many) => Ok(Many)
+        }
+    }
+
     fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style> {
         match (r1, r2) {
           (ast::return_val, _) |
diff --git a/src/rustc/middle/typeck/infer/sub.rs b/src/rustc/middle/typeck/infer/sub.rs
index 3677911b5eaa1..9e27235b30d1a 100644
--- a/src/rustc/middle/typeck/infer/sub.rs
+++ b/src/rustc/middle/typeck/infer/sub.rs
@@ -93,6 +93,12 @@ impl Sub: combine {
         })
     }
 
+    fn oncenesses(a: Onceness, b: Onceness) -> cres<Onceness> {
+        self.lub().oncenesses(a, b).compare(b, || {
+            ty::terr_onceness_mismatch(expected_found(&self, a, b))
+        })
+    }
+
     fn ret_styles(a: ret_style, b: ret_style) -> cres<ret_style> {
         self.lub().ret_styles(a, b).compare(b, || {
             ty::terr_ret_style_mismatch(expected_found(&self, a, b))
diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs
index 75e21a5297b51..c7062baaea5ec 100644
--- a/src/rustc/util/ppaux.rs
+++ b/src/rustc/util/ppaux.rs
@@ -19,7 +19,8 @@ use syntax::codemap;
 use syntax::codemap::span;
 use syntax::print::pprust;
 use syntax::print::pprust::{path_to_str, proto_to_str,
-                            mode_to_str, purity_to_str};
+                            mode_to_str, purity_to_str,
+                            onceness_to_str};
 use syntax::{ast, ast_util};
 use syntax::ast_map;
 
@@ -266,14 +267,24 @@ fn ty_to_str(cx: ctxt, typ: t) -> ~str {
         };
         modestr + ty_to_str(cx, ty)
     }
-    fn fn_to_str(cx: ctxt, purity: ast::purity, proto: ty::fn_proto,
+    fn fn_to_str(cx: ctxt,
+                 purity: ast::purity,
+                 proto: ty::fn_proto,
+                 onceness: ast::Onceness,
                  ident: Option<ast::ident>,
-                 inputs: ~[arg], output: t, cf: ast::ret_style) -> ~str {
+                 inputs: ~[arg],
+                 output: t,
+                 cf: ast::ret_style) -> ~str {
         let mut s;
 
         s = match purity {
-          ast::impure_fn => ~"",
-          _ => purity_to_str(purity) + ~" "
+            ast::impure_fn => ~"",
+            _ => purity_to_str(purity) + ~" "
+        };
+
+        s += match onceness {
+            ast::Many => ~"",
+            ast::Once => onceness_to_str(onceness) + ~" "
         };
 
         s += ~"fn";
@@ -298,8 +309,13 @@ fn ty_to_str(cx: ctxt, typ: t) -> ~str {
     }
     fn method_to_str(cx: ctxt, m: method) -> ~str {
         return fn_to_str(
-            cx, m.fty.meta.purity, m.fty.meta.proto, Some(m.ident),
-            m.fty.sig.inputs, m.fty.sig.output,
+            cx,
+            m.fty.meta.purity,
+            m.fty.meta.proto,
+            m.fty.meta.onceness,
+            Some(m.ident),
+            m.fty.sig.inputs,
+            m.fty.sig.output,
             m.fty.meta.ret_style) + ~";";
     }
     fn field_to_str(cx: ctxt, f: field) -> ~str {
@@ -347,8 +363,14 @@ fn ty_to_str(cx: ctxt, typ: t) -> ~str {
         ~"(" + str::connect(strs, ~",") + ~")"
       }
       ty_fn(ref f) => {
-        fn_to_str(cx, f.meta.purity, f.meta.proto, None, f.sig.inputs,
-                  f.sig.output, f.meta.ret_style)
+        fn_to_str(cx,
+                  f.meta.purity,
+                  f.meta.proto,
+                  f.meta.onceness,
+                  None,
+                  f.sig.inputs,
+                  f.sig.output,
+                  f.meta.ret_style)
       }
       ty_infer(infer_ty) => infer_ty.to_str(),
       ty_param({idx: id, _}) => {
diff --git a/src/test/compile-fail/once-fn-subtyping.rs b/src/test/compile-fail/once-fn-subtyping.rs
new file mode 100644
index 0000000000000..1b789b4df3d9c
--- /dev/null
+++ b/src/test/compile-fail/once-fn-subtyping.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let f: &once fn() = ||();
+    let g: &fn() = f;  //~ ERROR mismatched types
+    let h: &fn() = ||();
+    let i: &once fn() = h;  // ok
+}
+