summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-11-04 20:41:00 -0800
committerNiko Matsakis <niko@alum.mit.edu>2012-11-06 08:56:29 -0800
commitb0ed151539cddb1c191f67f9f9597942919d44eb (patch)
tree976ed7cab726c0cdae81e0a18e28a27cbd5e4664 /src/libsyntax
parent53ec6c3f9b495dd930cd061784251534bef58d74 (diff)
downloadrust-b0ed151539cddb1c191f67f9f9597942919d44eb.tar.gz
rust-b0ed151539cddb1c191f67f9f9597942919d44eb.zip
Cleanup how we handle proto in types, remove unsound subtyping
Fixes #1896 which was never truly fixed, just masked.
The given tests would have failed had they used `~fn()` and
not `@fn()`.  They now result in compilation errors.

Fixes #2978.

Necessary first step for #2202, #2263.
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs45
-rw-r--r--src/libsyntax/fold.rs18
-rw-r--r--src/libsyntax/parse/obsolete.rs66
-rw-r--r--src/libsyntax/parse/parser.rs275
-rw-r--r--src/libsyntax/print/pprust.rs150
-rw-r--r--src/libsyntax/visit.rs12
6 files changed, 308 insertions, 258 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 8667f36c749..8fcb9300b52 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -410,18 +410,24 @@ impl mutability : cmp::Eq {
 
 #[auto_serialize]
 #[auto_deserialize]
-enum proto {
-    proto_bare,    // foreign fn
-    proto_uniq,    // fn~
-    proto_box,     // fn@
-    proto_block,   // fn&
+pub enum Proto {
+    ProtoBare,     // bare functions (deprecated)
+    ProtoUniq,     // ~fn
+    ProtoBox,      // @fn
+    ProtoBorrowed, // &fn
 }
 
-impl proto : cmp::Eq {
-    pure fn eq(other: &proto) -> bool {
+impl Proto : cmp::Eq {
+    pure fn eq(other: &Proto) -> bool {
         (self as uint) == ((*other) as uint)
     }
-    pure fn ne(other: &proto) -> bool { !self.eq(other) }
+    pure fn ne(other: &Proto) -> bool { !self.eq(other) }
+}
+
+impl Proto : to_bytes::IterBytes {
+    pure fn iter_bytes(+lsb0: bool, f: to_bytes::Cb) {
+        (self as uint).iter_bytes(lsb0, f);
+    }
 }
 
 #[auto_serialize]
@@ -444,10 +450,10 @@ enum expr_vstore {
     expr_vstore_slice                  // &[1,2,3,4]
 }
 
-pure fn is_blockish(p: ast::proto) -> bool {
+pure fn is_blockish(p: ast::Proto) -> bool {
     match p {
-      proto_block => true,
-      proto_bare | proto_uniq | proto_box => false
+        ProtoBorrowed => true,
+        ProtoBare | ProtoUniq | ProtoBox => false
     }
 }
 
@@ -678,7 +684,7 @@ enum expr_ {
        (implicit) condition is always true. */
     expr_loop(blk, Option<ident>),
     expr_match(@expr, ~[arm]),
-    expr_fn(proto, fn_decl, blk, capture_clause),
+    expr_fn(Proto, fn_decl, blk, capture_clause),
     expr_fn_block(fn_decl, blk, capture_clause),
     // Inner expr is always an expr_fn_block. We need the wrapping node to
     // easily type this (a function returning nil on the inside but bool on
@@ -1080,19 +1086,30 @@ impl Onceness : cmp::Eq {
 
 #[auto_serialize]
 #[auto_deserialize]
+struct TyFn {
+    proto: Proto,
+    region: Option<@region>,
+    purity: purity,
+    onceness: Onceness,
+    bounds: @~[ty_param_bound],
+    decl: fn_decl
+}
+
+#[auto_serialize]
+#[auto_deserialize]
 enum ty_ {
     ty_nil,
     ty_bot, /* bottom type */
     ty_box(mt),
     ty_uniq(mt),
     ty_vec(mt),
+    ty_fixed_length_vec(mt, uint),
     ty_ptr(mt),
     ty_rptr(@region, mt),
     ty_rec(~[ty_field]),
-    ty_fn(proto, purity, Onceness, @~[ty_param_bound], fn_decl),
+    ty_fn(@TyFn),
     ty_tup(~[@Ty]),
     ty_path(@path, node_id),
-    ty_fixed_length(@Ty, Option<uint>),
     ty_mac(mac),
     // ty_infer means the type should be inferred instead of it having been
     // specified. This should only appear at the "top level" of a type and not
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 7c8909668f5..3879e70cb28 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -524,15 +524,19 @@ 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, onceness, bounds, decl) =>
-        ty_fn(proto,
-              purity,
-              onceness,
-              @vec::map(*bounds, |x| fold_ty_param_bound(*x, fld)),
-              fold_fn_decl(decl, fld)),
+      ty_fn(f) =>
+        ty_fn(@TyFn {
+            proto: f.proto,
+            purity: f.purity,
+            region: f.region,
+            onceness: f.onceness,
+            bounds: @vec::map(*f.bounds, |x| fold_ty_param_bound(*x, fld)),
+            decl: fold_fn_decl(f.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)),
-      ty_fixed_length(t, vs) => ty_fixed_length(fld.fold_ty(t), vs),
+      ty_fixed_length_vec(mt, vs) =>
+        ty_fixed_length_vec(fold_mt(mt, fld), vs),
       ty_mac(mac) => ty_mac(fold_mac(mac))
     }
 }
diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs
index 1344c9b11ea..e5f6cf8ee25 100644
--- a/src/libsyntax/parse/obsolete.rs
+++ b/src/libsyntax/parse/obsolete.rs
@@ -24,7 +24,6 @@ pub enum ObsoleteSyntax {
     ObsoletePrivSection,
     ObsoleteModeInFnType,
     ObsoleteByMutRefMode,
-    ObsoleteFixedLengthVec,
     ObsoleteMoveInit,
     ObsoleteBinaryMove
 }
@@ -102,11 +101,6 @@ impl Parser : ObsoleteReporter {
                 "by-mutable-reference mode",
                 "Declare an argument of type &mut T instead"
             ),
-            ObsoleteFixedLengthVec => (
-                "fixed-length vector",
-                "Fixed-length types are now written `[T * N]`, and instances \
-                 are type-inferred"
-            ),
             ObsoleteMoveInit => (
                 "initializer-by-move",
                 "Write `let foo = move bar` instead"
@@ -200,65 +194,5 @@ impl Parser : ObsoleteReporter {
         }
     }
 
-    fn try_parse_obsolete_fixed_vstore() -> Option<Option<uint>> {
-        if self.token == token::BINOP(token::SLASH) {
-            self.bump();
-            match copy self.token {
-                token::UNDERSCORE => {
-                    self.obsolete(copy self.last_span,
-                                  ObsoleteFixedLengthVec);
-                    self.bump(); Some(None)
-                }
-                token::LIT_INT_UNSUFFIXED(i) if i >= 0i64 => {
-                    self.obsolete(copy self.last_span,
-                                  ObsoleteFixedLengthVec);
-                    self.bump(); Some(Some(i as uint))
-                }
-                _ => None
-            }
-        } else {
-            None
-        }
-    }
-
-    fn try_convert_ty_to_obsolete_fixed_length_vstore(sp: span, t: ast::ty_)
-        -> ast::ty_ {
-        match self.try_parse_obsolete_fixed_vstore() {
-            // Consider a fixed length vstore suffix (/N or /_)
-            None => t,
-            Some(v) => {
-                ast::ty_fixed_length(
-                    @{id: self.get_id(), node: t, span: sp}, v)
-            }
-        }
-    }
-
-    fn try_convert_expr_to_obsolete_fixed_length_vstore(
-        lo: uint, hi: uint, ex: ast::expr_
-    ) -> (uint, ast::expr_) {
-
-        let mut hi = hi;
-        let mut ex = ex;
-
-        // Vstore is legal following expr_lit(lit_str(...)) and expr_vec(...)
-        // only.
-        match ex {
-            ast::expr_lit(@{node: ast::lit_str(_), span: _}) |
-            ast::expr_vec(_, _)  => {
-                match self.try_parse_obsolete_fixed_vstore() {
-                    None => (),
-                    Some(v) => {
-                        hi = self.span.hi;
-                        ex = ast::expr_vstore(self.mk_expr(lo, hi, ex),
-                                              ast::expr_vstore_fixed(v));
-                    }
-                }
-            }
-            _ => ()
-        }
-
-        return (hi, ex);
-    }
-
 }
 
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 63545a69608..db3f6abbf7b 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -50,8 +50,8 @@ use ast::{_mod, add, arg, arm, attribute,
              match_tok, method, mode, module_ns, mt, mul, mutability,
              named_field, neg, noreturn, not, pat, pat_box, pat_enum,
              pat_ident, pat_lit, pat_range, pat_rec, pat_region, pat_struct,
-             pat_tup, pat_uniq, pat_wild, path, private, proto, proto_bare,
-             proto_block, proto_box, proto_uniq, provided, public, pure_fn,
+             pat_tup, pat_uniq, pat_wild, path, private, Proto, ProtoBare,
+             ProtoBorrowed, ProtoBox, ProtoUniq, provided, public, pure_fn,
              purity, re_static, re_self, re_anon, re_named, region,
              rem, required, ret_style,
              return_val, self_ty, shl, shr, stmt, stmt_decl, stmt_expr,
@@ -61,14 +61,14 @@ use ast::{_mod, add, arg, arm, attribute,
              tt_tok, tt_nonterminal, tuple_variant_kind, Ty, ty_, ty_bot,
              ty_box, ty_field, ty_fn, ty_infer, ty_mac, ty_method, ty_nil,
              ty_param, ty_param_bound, ty_path, ty_ptr, ty_rec, ty_rptr,
-             ty_tup, ty_u32, ty_uniq, ty_vec, ty_fixed_length, type_value_ns,
-             uniq, unnamed_field, unsafe_blk, unsafe_fn,
+             ty_tup, ty_u32, ty_uniq, ty_vec, ty_fixed_length_vec,
+             type_value_ns, uniq, unnamed_field, unsafe_blk, unsafe_fn,
              variant, view_item, view_item_, view_item_export,
              view_item_import, view_item_use, view_path, view_path_glob,
              view_path_list, view_path_simple, visibility, vstore, vstore_box,
              vstore_fixed, vstore_slice, vstore_uniq,
              expr_vstore_fixed, expr_vstore_slice, expr_vstore_box,
-             expr_vstore_uniq};
+             expr_vstore_uniq, TyFn, Onceness, Once, Many};
 
 export file_type;
 export Parser;
@@ -287,30 +287,90 @@ impl Parser {
 
     pure fn id_to_str(id: ident) -> @~str { self.sess.interner.get(id) }
 
-    fn parse_ty_fn(purity: ast::purity, onceness: ast::Onceness) -> ty_ {
-        let proto, bounds;
+    fn token_is_fn_keyword(+tok: token::Token) -> bool {
+        self.token_is_keyword(~"pure", tok) ||
+            self.token_is_keyword(~"unsafe", tok) ||
+            self.token_is_keyword(~"once", tok) ||
+            self.token_is_keyword(~"fn", tok) ||
+            self.token_is_keyword(~"extern", tok)
+    }
+
+    fn parse_ty_fn(pre_proto: Option<ast::Proto>,
+                       pre_region_name: Option<ident>) -> ty_
+    {
+        /*
+
+        (&|~|@) [r/] [pure|unsafe] [once] fn [:K] (S) -> T
+        ^~~~~~^ ^~~^ ^~~~~~~~~~~~^ ^~~~~^    ^~~^ ^~^    ^
+           |     |     |             |        |    |     |
+           |     |     |             |        |    |   Return type
+           |     |     |             |        |  Argument types
+           |     |     |             |    Environment bounds
+           |     |     |          Once-ness (a.k.a., affine)
+           |     |   Purity
+           | Lifetime bound
+        Allocation type
+
+        */
+
+        // At this point, the allocation type and lifetime bound have been
+        // parsed.
+
+        let purity = parse_purity(&self);
+        let onceness = parse_onceness(&self);
+
+        let bounds, post_proto;
         if self.eat_keyword(~"extern") {
             self.expect_keyword(~"fn");
-            proto = ast::proto_bare;
+            post_proto = Some(ast::ProtoBare);
             bounds = @~[];
         } else {
             self.expect_keyword(~"fn");
-            proto = self.parse_fn_ty_proto();
+            post_proto = self.parse_fn_ty_proto();
             bounds = self.parse_optional_ty_param_bounds();
         };
-        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)
-    }
+        let proto = match (pre_proto, post_proto) {
+            (None, None) => ast::ProtoBorrowed,
+            (Some(p), None) | (None, Some(p)) => p,
+            (Some(_), Some(_)) => {
+                self.fatal(~"cannot combine prefix and postfix \
+                             syntax for closure kind; note that \
+                             postfix syntax is obsolete");
+            }
+        };
+
+        let region = if pre_region_name.is_some() {
+            Some(self.region_from_name(pre_region_name))
+        } else {
+            None
+        };
+
+        return ty_fn(@TyFn {
+            proto: proto,
+            region: region,
+            purity: purity,
+            onceness: onceness,
+            bounds: bounds,
+            decl: self.parse_ty_fn_decl()
+        });
+
+        fn parse_purity(self: &Parser) -> purity {
+            if self.eat_keyword(~"pure") {
+                return pure_fn;
+            } else if self.eat_keyword(~"unsafe") {
+                return unsafe_fn;
+            } else {
+                return impure_fn;
+            }
+        }
 
-    fn parse_ty_fn_with_purity_and_onceness() -> ty_ {
-        let purity = self.parse_optional_purity();
-        self.parse_ty_fn_with_onceness(purity)
+        fn parse_onceness(self: &Parser) -> Onceness {
+            if self.eat_keyword(~"once") {Once} else {Many}
+        }
     }
 
+
     fn parse_ty_fn_decl() -> fn_decl {
         let inputs = do self.parse_unspanned_seq(
             token::LPAREN, token::RPAREN,
@@ -449,23 +509,6 @@ impl Parser {
         }
     }
 
-    // Parses something like "&x/" (note the trailing slash)
-    fn parse_region_with_sep() -> @region {
-        let name =
-            match copy self.token {
-              token::IDENT(sid, _) => {
-                if self.look_ahead(1u) == token::BINOP(token::SLASH) {
-                    self.bump(); self.bump();
-                    Some(sid)
-                } else {
-                    None
-                }
-              }
-              _ => { None }
-            };
-        self.region_from_name(name)
-    }
-
     fn parse_ty(colons_before_params: bool) -> @Ty {
         maybe_whole!(self, nt_ty);
 
@@ -498,10 +541,10 @@ impl Parser {
             }
         } else if self.token == token::AT {
             self.bump();
-            ty_box(self.parse_mt())
+            self.parse_box_or_uniq_pointee(ast::ProtoBox, ty_box)
         } else if self.token == token::TILDE {
             self.bump();
-            ty_uniq(self.parse_mt())
+            self.parse_box_or_uniq_pointee(ast::ProtoUniq, ty_uniq)
         } else if self.token == token::BINOP(token::STAR) {
             self.bump();
             ty_ptr(self.parse_mt())
@@ -516,51 +559,77 @@ impl Parser {
             ty_rec(elems)
         } else if self.token == token::LBRACKET {
             self.expect(token::LBRACKET);
-            let mut t = ty_vec(self.parse_mt());
+            let mt = self.parse_mt();
 
             // Parse the `* 3` in `[ int * 3 ]`
-            match self.maybe_parse_fixed_vstore_with_star() {
-                None => {}
-                Some(suffix) => {
-                    t = ty_fixed_length(@{
-                        id: self.get_id(),
-                        node: t,
-                        span: mk_sp(lo, self.last_span.hi)
-                    }, suffix)
-                }
-            }
+            let t = match self.maybe_parse_fixed_vstore_with_star() {
+                None => ty_vec(mt),
+                Some(suffix) => ty_fixed_length_vec(mt, suffix)
+            };
             self.expect(token::RBRACKET);
             t
         } else if self.token == token::BINOP(token::AND) {
             self.bump();
-            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_with_onceness(ast::pure_fn)
-        } else if self.eat_keyword(~"unsafe") {
-            self.parse_ty_fn_with_onceness(ast::unsafe_fn)
-        } else if self.is_keyword(~"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, ast::Many, @~[],
-                  self.parse_ty_fn_decl())
+            self.parse_borrowed_pointee()
+        } else if self.token_is_fn_keyword(self.token) {
+            self.parse_ty_fn(None, None)
         } 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())
         } else { self.fatal(~"expected type"); };
 
         let sp = mk_sp(lo, self.last_span.hi);
-        return {
-            let node =
-                self.try_convert_ty_to_obsolete_fixed_length_vstore(sp, t);
-            @{id: self.get_id(),
-              node: node,
-              span: sp}
+        return @{id: self.get_id(), node: t, span: sp};
+    }
+
+    fn parse_box_or_uniq_pointee(
+        proto: ast::Proto,
+        ctor: &fn(+v: mt) -> ty_) -> ty_
+    {
+        // @foo/fn() or @fn() are parsed directly as fn types:
+        match copy self.token {
+            token::IDENT(rname, _) => {
+                if self.look_ahead(1u) == token::BINOP(token::SLASH) &&
+                    self.token_is_fn_keyword(self.look_ahead(2u))
+                {
+                    self.bump(); self.bump();
+                    return self.parse_ty_fn(Some(proto), Some(rname));
+                } else if self.token_is_fn_keyword(self.token) {
+                    return self.parse_ty_fn(Some(proto), None);
+                }
+            }
+            _ => {}
+        }
+
+        // other things are parsed as @ + a type.  Note that constructs like
+        // @[] and @str will be resolved during typeck to slices and so forth,
+        // rather than boxed ptrs.  But the special casing of str/vec is not
+        // reflected in the AST type.
+        let mt = self.parse_mt();
+        ctor(mt)
+    }
+
+    fn parse_borrowed_pointee() -> ty_ {
+        // look for `&foo/` and interpret `foo` as the region name:
+        let rname = match copy self.token {
+            token::IDENT(sid, _) => {
+                if self.look_ahead(1u) == token::BINOP(token::SLASH) {
+                    self.bump(); self.bump();
+                    Some(sid)
+                } else {
+                    None
+                }
+            }
+            _ => { None }
         };
+
+        if self.token_is_fn_keyword(self.token) {
+            return self.parse_ty_fn(Some(ProtoBorrowed), rname);
+        }
+
+        let r = self.region_from_name(rname);
+        let mt = self.parse_mt();
+        return ty_rptr(r, mt);
     }
 
     fn parse_arg_mode() -> mode {
@@ -691,16 +760,19 @@ impl Parser {
         }
     }
 
-    fn maybe_parse_fixed_vstore_with_star() -> Option<Option<uint>> {
+    fn maybe_parse_fixed_vstore_with_star() -> Option<uint> {
         if self.eat(token::BINOP(token::STAR)) {
             match copy self.token {
-              token::UNDERSCORE => {
-                self.bump(); Some(None)
-              }
-              token::LIT_INT_UNSUFFIXED(i) if i >= 0i64 => {
-                self.bump(); Some(Some(i as uint))
-              }
-              _ => None
+                token::LIT_INT_UNSUFFIXED(i) if i >= 0i64 => {
+                    self.bump();
+                    Some(i as uint)
+                }
+                _ => {
+                    self.fatal(
+                        fmt!("expected integral vector length \
+                              but found `%s`",
+                             token_to_str(self.reader, self.token)));
+                }
             }
         } else {
             None
@@ -909,11 +981,13 @@ impl Parser {
         } else if self.eat_keyword(~"match") {
             return self.parse_alt_expr();
         } else if self.eat_keyword(~"fn") {
-            let proto = self.parse_fn_ty_proto();
-            match proto {
-              proto_bare => self.fatal(~"fn expr are deprecated, use fn@"),
-              _ => { /* fallthrough */ }
-            }
+            let opt_proto = self.parse_fn_ty_proto();
+            let proto = match opt_proto {
+                None | Some(ast::ProtoBare) => {
+                    self.fatal(~"fn expr are deprecated, use fn@")
+                }
+                Some(p) => { p }
+            };
             return self.parse_fn_expr(proto);
         } else if self.eat_keyword(~"unsafe") {
             return self.parse_block_expr(lo, unsafe_blk);
@@ -1055,9 +1129,6 @@ impl Parser {
             ex = expr_lit(@lit);
         }
 
-        let (hi, ex) =
-            self.try_convert_expr_to_obsolete_fixed_length_vstore(lo, hi, ex);
-
         return self.mk_expr(lo, hi, ex);
     }
 
@@ -1495,7 +1566,7 @@ impl Parser {
         return self.mk_expr(q.lo, q.hi, expr_if(q.cond, q.then, q.els));
     }
 
-    fn parse_fn_expr(proto: proto) -> @expr {
+    fn parse_fn_expr(proto: Proto) -> @expr {
         let lo = self.last_span.lo;
 
         // if we want to allow fn expression argument types to be inferred in
@@ -3188,23 +3259,23 @@ impl Parser {
         (id, item_enum(enum_definition, ty_params), None)
     }
 
-    fn parse_fn_ty_proto() -> proto {
+    fn parse_fn_ty_proto() -> Option<Proto> {
         match self.token {
-          token::AT => {
-            self.bump();
-            proto_box
-          }
-          token::TILDE => {
-            self.bump();
-            proto_uniq
-          }
-          token::BINOP(token::AND) => {
-            self.bump();
-            proto_block
-          }
-          _ => {
-            proto_block
-          }
+            token::AT => {
+                self.bump();
+                Some(ProtoBox)
+            }
+            token::TILDE => {
+                self.bump();
+                Some(ProtoUniq)
+            }
+            token::BINOP(token::AND) => {
+                self.bump();
+                Some(ProtoBorrowed)
+            }
+            _ => {
+                None
+            }
         }
     }
 
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index e0b9958bcb7..ed64d02cea3 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -7,6 +7,7 @@ use ast_util::{operator_prec};
 use dvec::DVec;
 use parse::classify::*;
 use parse::token::ident_interner;
+use str::{push_str, push_char};
 
 // The ps is stored here to prevent recursive type.
 enum ann_node {
@@ -211,7 +212,9 @@ fn head(s: ps, w: ~str) {
     // head-box is inconsistent
     ibox(s, str::len(w) + 1);
     // keyword that starts the head
-    word_nbsp(s, w);
+    if !w.is_empty() {
+        word_nbsp(s, w);
+    }
 }
 
 fn bopen(s: ps) {
@@ -328,20 +331,19 @@ fn print_foreign_mod(s: ps, nmod: ast::foreign_mod,
     for nmod.items.each |item| { print_foreign_item(s, *item); }
 }
 
-fn print_region(s: ps, region: @ast::region, sep: ~str) {
+fn print_region(s: ps, prefix: ~str, region: @ast::region, sep: ~str) {
+    word(s.s, prefix);
     match region.node {
         ast::re_anon => {
-            word_space(s, ~"&");
             return;
         }
         ast::re_static => {
-            word_space(s, ~"&static")
+            word_space(s, ~"static")
         }
         ast::re_self => {
-            word_space(s, ~"&self")
+            word_space(s, ~"self")
         }
         ast::re_named(name) => {
-            word(s.s, ~"&");
             print_ident(s, name);
         }
     }
@@ -372,7 +374,7 @@ fn print_type_ex(s: ps, &&ty: @ast::Ty, print_colons: bool) {
       }
       ast::ty_ptr(mt) => { word(s.s, ~"*"); print_mt(s, mt); }
       ast::ty_rptr(region, mt) => {
-          print_region(s, region, ~"/");
+          print_region(s, ~"&", region, ~"/");
           print_mt(s, mt);
       }
       ast::ty_rec(fields) => {
@@ -394,26 +396,21 @@ 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, onceness, bounds, d) => {
-        print_ty_fn(s, Some(proto), purity, onceness, bounds, d, None, None,
-                    None);
+      ast::ty_fn(f) => {
+        print_ty_fn(s, Some(f.proto), f.region, f.purity,
+                    f.onceness, f.bounds, f.decl, None, None, None);
       }
       ast::ty_path(path, _) => print_path(s, path, print_colons),
-      ast::ty_fixed_length(t, v) => {
+      ast::ty_fixed_length_vec(mt, v) => {
         word(s.s, ~"[");
-        match t.node {
-          ast::ty_vec(mt) => {
-            match mt.mutbl {
-              ast::m_mutbl => word_space(s, ~"mut"),
-              ast::m_const => word_space(s, ~"const"),
-              ast::m_imm => ()
-            }
-            print_type(s, mt.ty);
-          }
-          _ => fail ~"ty_fixed_length can only contain ty_vec as type"
+        match mt.mutbl {
+            ast::m_mutbl => word_space(s, ~"mut"),
+            ast::m_const => word_space(s, ~"const"),
+            ast::m_imm => ()
         }
+        print_type(s, mt.ty);
         word(s.s, ~" * ");
-        print_vstore(s, ast::vstore_fixed(v));
+        word(s.s, fmt!("%u", v));
         word(s.s, ~"]");
       }
       ast::ty_mac(_) => {
@@ -805,7 +802,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, ast::Many,
+    print_ty_fn(s, None, None, m.purity, ast::Many,
                 @~[], m.decl, Some(m.ident), Some(m.tps),
                 Some(m.self_ty.node));
     word(s.s, ~";");
@@ -1023,7 +1020,7 @@ fn print_vstore(s: ps, t: ast::vstore) {
         ast::vstore_fixed(None) => word(s.s, ~"_"),
         ast::vstore_uniq => word(s.s, ~"~"),
         ast::vstore_box => word(s.s, ~"@"),
-        ast::vstore_slice(r) => print_region(s, r, ~"/")
+        ast::vstore_slice(r) => print_region(s, ~"&", r, ~"/")
     }
 }
 
@@ -1274,8 +1271,8 @@ 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), ast::Many,
-                                        ast::inherited));
+        print_fn_header_info(s, None, None, ast::Many,
+                             Some(proto), ast::inherited);
         print_fn_args_and_ret(s, decl, *cap_clause, None);
         space(s.s);
         print_block(s, body);
@@ -1481,7 +1478,7 @@ fn print_path(s: ps, &&path: @ast::path, colons_before_params: bool) {
           None => { /* ok */ }
           Some(r) => {
             word(s.s, ~"/");
-            print_region(s, r, ~"");
+            print_region(s, ~"&", r, ~"");
           }
         }
 
@@ -1614,8 +1611,9 @@ fn print_fn(s: ps,
             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, ast::Many,
-                                  vis));
+    head(s, ~"");
+    print_fn_header_info(s, opt_self_ty, purity, ast::Many, None, vis);
+    nbsp(s);
     print_ident(s, name);
     print_type_params(s, typarams);
     print_fn_args_and_ret(s, decl, ~[], opt_self_ty);
@@ -1836,7 +1834,8 @@ fn print_arg(s: ps, input: ast::arg) {
 }
 
 fn print_ty_fn(s: ps,
-               opt_proto: Option<ast::proto>,
+               opt_proto: Option<ast::Proto>,
+               opt_region: Option<@ast::region>,
                purity: ast::purity,
                onceness: ast::Onceness,
                bounds: @~[ast::ty_param_bound],
@@ -1844,8 +1843,15 @@ fn print_ty_fn(s: ps,
                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,
-                                    onceness, ast::inherited));
+
+    // Duplicates the logic in `print_fn_header_info()`.  This is because that
+    // function prints the proto in the wrong place.  That should be fixed.
+    print_self_ty_if_static(s, opt_self_ty);
+    print_opt_proto(s, opt_proto);
+    for opt_region.each |r| { print_region(s, ~"", *r, ~"/"); }
+    print_purity(s, purity);
+    print_onceness(s, onceness);
+    word(s.s, ~"fn");
     print_bounds(s, bounds);
     match id { Some(id) => { word(s.s, ~" "); print_ident(s, id); } _ => () }
     match tps { Some(tps) => print_type_params(s, tps), _ => () }
@@ -2066,39 +2072,50 @@ 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, ~"");
-
-    match opt_sty {
-        Some(ast::sty_static) => str::push_str(&mut s, ~"static "),
-        _ => ()
-    };
+fn print_self_ty_if_static(s: ps,
+                           opt_self_ty: Option<ast::self_ty_>) {
+    match opt_self_ty {
+        Some(ast::sty_static) => { word(s.s, ~"static "); }
+        _ => {}
+    }
+}
 
+fn print_opt_purity(s: ps, opt_purity: Option<ast::purity>) {
     match opt_purity {
-      Some(ast::impure_fn) => { }
-      Some(purity) => {
-        str::push_str(&mut s, purity_to_str(purity));
-        str::push_char(&mut s, ' ');
-      }
-      None => {}
+        Some(ast::impure_fn) => { }
+        Some(purity) => {
+            word_nbsp(s, purity_to_str(purity));
+        }
+        None => {}
     }
+}
 
-    str::push_str(&mut s, opt_proto_to_str(opt_p));
-
-    match onceness {
-        ast::Once => str::push_str(&mut s, ~"once "),
-        ast::Many => {}
-    }
+fn print_opt_proto(s: ps, opt_proto: Option<ast::Proto>) {
+    match opt_proto {
+        Some(ast::ProtoBare) => { word(s.s, ~"extern "); }
+        Some(ast::ProtoBorrowed) => { word(s.s, ~"&"); }
+        Some(ast::ProtoUniq) => { word(s.s, ~"~"); }
+        Some(ast::ProtoBox) => { word(s.s, ~"@"); }
+        None => {}
+    };
+}
 
-    return s;
+fn print_fn_header_info(s: ps,
+                        opt_sty: Option<ast::self_ty_>,
+                        opt_purity: Option<ast::purity>,
+                        onceness: ast::Onceness,
+                        opt_proto: Option<ast::Proto>,
+                        vis: ast::visibility)
+{
+    word(s.s, visibility_qualified(vis, ~""));
+    print_self_ty_if_static(s, opt_sty);
+    print_opt_purity(s, opt_purity);
+    print_onceness(s, onceness);
+    word(s.s, ~"fn");
+    print_opt_proto(s, opt_proto);
 }
 
-fn opt_proto_to_str(opt_p: Option<ast::proto>) -> ~str {
+fn opt_proto_to_str(opt_p: Option<ast::Proto>) -> ~str {
     match opt_p {
       None => ~"fn",
       Some(p) => proto_to_str(p)
@@ -2128,12 +2145,19 @@ fn print_purity(s: ps, p: ast::purity) {
     }
 }
 
-fn proto_to_str(p: ast::proto) -> ~str {
+fn print_onceness(s: ps, o: ast::Onceness) {
+    match o {
+        ast::Once => { word_nbsp(s, ~"once"); }
+        ast::Many => {}
+    }
+}
+
+fn proto_to_str(p: ast::Proto) -> ~str {
     return match p {
-      ast::proto_bare => ~"extern fn",
-      ast::proto_block => ~"fn&",
-      ast::proto_uniq => ~"fn~",
-      ast::proto_box => ~"fn@"
+      ast::ProtoBare => ~"extern fn",
+      ast::ProtoBorrowed => ~"fn&",
+      ast::ProtoUniq => ~"fn~",
+      ast::ProtoBox => ~"fn@"
     };
 }
 
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index be6fb4cefa8..97cc52bd35a 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -15,7 +15,7 @@ enum vt<E> { mk_vt(visitor<E>), }
 enum fn_kind {
     fk_item_fn(ident, ~[ty_param], purity), //< an item declared with fn()
     fk_method(ident, ~[ty_param], @method),
-    fk_anon(proto, capture_clause),  //< an anonymous function like fn@(...)
+    fk_anon(Proto, capture_clause),  //< an anonymous function like fn@(...)
     fk_fn_block(capture_clause),     //< a block {||...}
     fk_dtor(~[ty_param], ~[attribute], node_id /* self id */,
             def_id /* parent class id */) // class destructor
@@ -203,13 +203,13 @@ 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) => {
-        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);
+      ty_fn(f) => {
+        for f.decl.inputs.each |a| { v.visit_ty(a.ty, e, v); }
+        visit_ty_param_bounds(f.bounds, e, v);
+        v.visit_ty(f.decl.output, e, v);
       }
       ty_path(p, _) => visit_path(p, e, v),
-      ty_fixed_length(t, _) => v.visit_ty(t, e, v),
+      ty_fixed_length_vec(mt, _) => v.visit_ty(mt.ty, e, v),
       ty_nil |
       ty_bot |
       ty_mac(_) |