about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMarijn Haverbeke <marijnh@gmail.com>2011-12-20 16:33:55 +0100
committerMarijn Haverbeke <marijnh@gmail.com>2011-12-23 18:09:25 +0100
commit057617c6654bfd5a677112cd26141d0b9c137145 (patch)
tree316c1b2cbcb11aa3cd02f05107e76fa5cd624fef /src
parent92927449595782926c4db5a579ba7e4c6952287d (diff)
Parse `iface` items and interface references in `impl` items.
The (temporary) syntax is

    iface seq<T> {
        fn len() -> uint;
        fn iter(f: block(T));
    }

    // The 'blah<T>' can be left of to default the name of the
    // impl to seq<T>. The 'of seq<T>' can be left off when
    // not implementing a named interface.
    impl blah<T> of seq<T> for [T] {
        fn len() -> uint { vec::len(self) }
        fn iter(f: block(T)) { for x in self { f(x); } }
    }
Diffstat (limited to 'src')
-rw-r--r--src/comp/metadata/encoder.rs15
-rw-r--r--src/comp/middle/ast_map.rs2
-rw-r--r--src/comp/middle/resolve.rs28
-rw-r--r--src/comp/middle/trans.rs4
-rw-r--r--src/comp/middle/tstate/pre_post_conditions.rs5
-rw-r--r--src/comp/middle/ty.rs91
-rw-r--r--src/comp/middle/typeck.rs18
-rw-r--r--src/comp/syntax/ast.rs4
-rw-r--r--src/comp/syntax/fold.rs15
-rw-r--r--src/comp/syntax/parse/parser.rs117
-rw-r--r--src/comp/syntax/print/pprust.rs38
-rw-r--r--src/comp/syntax/visit.rs11
12 files changed, 209 insertions, 139 deletions
diff --git a/src/comp/metadata/encoder.rs b/src/comp/metadata/encoder.rs
index ba87bcf9dab..6bbca1335a1 100644
--- a/src/comp/metadata/encoder.rs
+++ b/src/comp/metadata/encoder.rs
@@ -64,6 +64,7 @@ fn encode_native_module_item_paths(ebml_w: ebml::writer, nmod: native_mod,
 
 fn encode_module_item_paths(ebml_w: ebml::writer, module: _mod, path: [str],
                             &index: [entry<str>]) {
+    // FIXME factor out add_to_index/start/encode_name/encode_def_id/end ops
     for it: @item in module.items {
         if !ast_util::is_exported(it.ident, module) { cont; }
         alt it.node {
@@ -137,7 +138,14 @@ fn encode_module_item_paths(ebml_w: ebml::writer, module: _mod, path: [str],
             encode_def_id(ebml_w, local_def(it.id));
             ebml::end_tag(ebml_w);
           }
-          item_impl(_, _, _) {}
+          item_iface(_, _) {
+            add_to_index(ebml_w, path, index, it.ident);
+            ebml::start_tag(ebml_w, tag_paths_data_item);
+            encode_name(ebml_w, it.ident);
+            encode_def_id(ebml_w, local_def(it.id));
+            ebml::end_tag(ebml_w);
+          }
+          item_impl(_, _, _, _) {}
         }
     }
 }
@@ -252,7 +260,7 @@ fn encode_info_for_mod(ebml_w: ebml::writer, md: _mod,
     encode_name(ebml_w, name);
     for i in md.items {
         alt i.node {
-          item_impl(_, _, _) {
+          item_impl(_, _, _, _) {
             if ast_util::is_exported(i.ident, md) {
                 ebml::start_tag(ebml_w, tag_mod_impl);
                 ebml_w.writer.write(str::bytes(def_to_str(local_def(i.id))));
@@ -363,7 +371,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
         encode_symbol(ecx, ebml_w, ctor_id);
         ebml::end_tag(ebml_w);
       }
-      item_impl(tps, _, methods) {
+      item_impl(tps, _, _, methods) {
         ebml::start_tag(ebml_w, tag_items_data_item);
         encode_def_id(ebml_w, local_def(item.id));
         encode_family(ebml_w, 'i' as u8);
@@ -390,6 +398,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
             ebml::end_tag(ebml_w);
         }
       }
+      item_iface(_, _) { /* FIXME[impl] */ }
     }
 }
 
diff --git a/src/comp/middle/ast_map.rs b/src/comp/middle/ast_map.rs
index f1ecfb29e2a..0b0d0337d2d 100644
--- a/src/comp/middle/ast_map.rs
+++ b/src/comp/middle/ast_map.rs
@@ -71,7 +71,7 @@ fn map_item(cx: ctx, i: @item) {
             cx.map.insert(m.id, node_obj_method(m));
         }
       }
-      item_impl(_, _, ms) {
+      item_impl(_, _, _, ms) {
         for m in ms { cx.map.insert(m.id, node_method(m)); }
       }
       item_res(_, _, _, dtor_id, ctor_id) {
diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs
index 393c7000bce..68d4d5aac30 100644
--- a/src/comp/middle/resolve.rs
+++ b/src/comp/middle/resolve.rs
@@ -401,8 +401,9 @@ fn resolve_names(e: @env, c: @ast::crate) {
 fn visit_item_with_scope(i: @ast::item, sc: scopes, v: vt<scopes>) {
     let sc = cons(scope_item(i), @sc);
     alt i.node {
-      ast::item_impl(tps, sty, methods) {
-        visit::visit_ty(sty, sc, v);
+      ast::item_impl(tps, ifce, sty, methods) {
+        alt ifce { some(ty) { v.visit_ty(ty, sc, v); } _ {} }
+        v.visit_ty(sty, sc, v);
         for m in methods {
             v.visit_fn_proto(m.decl, tps + m.tps, m.body, m.span,
                              some(m.ident), m.id, sc, v);
@@ -802,14 +803,15 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace)
               ast::item_obj(ob, ty_params, _) {
                 ret lookup_in_obj(name, ob, ty_params, ns, it.id);
               }
-              ast::item_impl(ty_params, _, _) {
+              ast::item_impl(ty_params, _, _, _) {
                 if (name == "self" && ns == ns_value) {
                     ret some(ast::def_self(local_def(it.id)));
                 }
                 if ns == ns_type { ret lookup_in_ty_params(name, ty_params); }
               }
-              ast::item_tag(_, ty_params) {
-                if ns == ns_type { ret lookup_in_ty_params(name, ty_params); }
+              ast::item_iface(tps, _) | ast::item_tag(_, tps) |
+              ast::item_ty(_, tps) {
+                if ns == ns_type { ret lookup_in_ty_params(name, tps); }
               }
               ast::item_mod(_) {
                 ret lookup_in_local_mod(e, it.id, sp, name, ns, inside);
@@ -817,9 +819,6 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace)
               ast::item_native_mod(m) {
                 ret lookup_in_local_native_mod(e, it.id, sp, name, ns);
               }
-              ast::item_ty(_, ty_params) {
-                if ns == ns_type { ret lookup_in_ty_params(name, ty_params); }
-              }
               _ { }
             }
           }
@@ -1064,7 +1063,7 @@ fn found_def_item(i: @ast::item, ns: namespace) -> option::t<def> {
       ast::item_native_mod(_) {
         if ns == ns_module { ret some(ast::def_native_mod(local_def(i.id))); }
       }
-      ast::item_ty(_, _) {
+      ast::item_ty(_, _) | item_iface(_, _) | item_tag(_, _) {
         if ns == ns_type { ret some(ast::def_ty(local_def(i.id))); }
       }
       ast::item_res(_, _, _, _, ctor_id) {
@@ -1076,9 +1075,6 @@ fn found_def_item(i: @ast::item, ns: namespace) -> option::t<def> {
           _ { }
         }
       }
-      ast::item_tag(_, _) {
-        if ns == ns_type { ret some(ast::def_ty(local_def(i.id))); }
-      }
       ast::item_obj(_, _, ctor_id) {
         alt ns {
           ns_value. {
@@ -1322,7 +1318,7 @@ fn index_mod(md: ast::_mod) -> mod_index {
           ast::item_const(_, _) | ast::item_fn(_, _, _) | ast::item_mod(_) |
           ast::item_native_mod(_) | ast::item_ty(_, _) |
           ast::item_res(_, _, _, _, _) | ast::item_obj(_, _, _) |
-          ast::item_impl(_, _, _) {
+          ast::item_impl(_, _, _, _) | ast::item_iface(_, _) {
             add_to_index(index, it.ident, mie_item(it));
           }
           ast::item_tag(variants, _) {
@@ -1570,7 +1566,9 @@ fn check_block(e: @env, b: ast::blk, &&x: (), v: vt<()>) {
                   ast::item_const(_, _) | ast::item_fn(_, _, _) {
                     add_name(values, it.span, it.ident);
                   }
-                  ast::item_ty(_, _) { add_name(types, it.span, it.ident); }
+                  ast::item_ty(_, _) | ast::item_iface(_, _) {
+                    add_name(types, it.span, it.ident);
+                  }
                   ast::item_res(_, _, _, _, _) | ast::item_obj(_, _, _) {
                     add_name(types, it.span, it.ident);
                     add_name(values, it.span, it.ident);
@@ -1768,7 +1766,7 @@ fn find_impls_in_item(i: @ast::item, &impls: [@_impl],
                       name: option::t<ident>,
                       ck_exports: option::t<ast::_mod>) {
     alt i.node {
-      ast::item_impl(_, _, mthds) {
+      ast::item_impl(_, _, _, mthds) {
         if alt name { some(n) { n == i.ident } _ { true } } &&
            alt ck_exports { some(m) { is_exported(i.ident, m) } _ { true } } {
             impls += [@{did: local_def(i.id),
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 366e515b05c..72c38582760 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -5013,7 +5013,7 @@ fn trans_item(cx: @local_ctxt, item: ast::item) {
                  with *extend_path(cx, item.ident)};
         trans_obj(sub_cx, item.span, ob, ctor_id, tps);
       }
-      ast::item_impl(tps, _, ms) {
+      ast::item_impl(tps, _, _, ms) {
         trans_impl(cx, item.ident, ms, item.id, tps);
       }
       ast::item_res(decl, tps, body, dtor_id, ctor_id) {
@@ -5340,7 +5340,7 @@ fn collect_item_2(ccx: @crate_ctxt, i: @ast::item, &&pt: [str],
             ccx.obj_methods.insert(m.id, ());
         }
       }
-      ast::item_impl(tps, _, methods) {
+      ast::item_impl(tps, _, _, methods) {
         let name = ccx.names.next(i.ident);
         for m in methods {
             register_fn(ccx, i.span, pt + [name, m.ident],
diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs
index 83acaaa5902..8f960eea6c0 100644
--- a/src/comp/middle/tstate/pre_post_conditions.rs
+++ b/src/comp/middle/tstate/pre_post_conditions.rs
@@ -64,8 +64,7 @@ fn find_pre_post_item(ccx: crate_ctxt, i: item) {
       }
       item_mod(m) { find_pre_post_mod(m); }
       item_native_mod(nm) { find_pre_post_native_mod(nm); }
-      item_ty(_, _) { ret; }
-      item_tag(_, _) { ret; }
+      item_ty(_, _) | item_tag(_, _) | item_iface(_, _) { ret; }
       item_res(_, _, body, dtor_id, _) {
         let fcx =
             {enclosing: ccx.fm.get(dtor_id),
@@ -75,7 +74,7 @@ fn find_pre_post_item(ccx: crate_ctxt, i: item) {
         find_pre_post_fn(fcx, body);
       }
       item_obj(o, _, _) {for m in o.methods { find_pre_post_method(ccx, m); }}
-      item_impl(_, _, ms) { for m in ms { find_pre_post_method(ccx, m); } }
+      item_impl(_, _, _, ms) { for m in ms { find_pre_post_method(ccx, m); } }
     }
 }
 
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index 2b18b1de08c..f668184acb6 100644
--- a/src/comp/middle/ty.rs
+++ b/src/comp/middle/ty.rs
@@ -73,6 +73,7 @@ export mk_native;
 export mk_native_fn;
 export mk_nil;
 export mk_obj;
+export mk_iface;
 export mk_res;
 export mk_param;
 export mk_ptr;
@@ -123,6 +124,7 @@ export ty_vec;
 export ty_native;
 export ty_nil;
 export ty_obj;
+export ty_iface;
 export ty_res;
 export ty_param;
 export ty_ptr;
@@ -256,6 +258,7 @@ tag sty {
     ty_fn(fn_ty);
     ty_native_fn([arg], t);
     ty_obj([method]);
+    ty_iface(def_id, [t]);
     ty_res(def_id, t, [t]);
     ty_tup([t]);
     ty_var(int); // type variable
@@ -444,7 +447,7 @@ fn mk_raw_ty(cx: ctxt, st: sty) -> @raw_t {
       }
       ty_param(_, _) { has_params = true; }
       ty_var(_) { has_vars = true; }
-      ty_tag(_, tys) {
+      ty_tag(_, tys) | ty_iface(_, tys) {
         for tt: t in tys { derive_flags_t(cx, has_params, has_vars, tt); }
       }
       ty_box(m) { derive_flags_mt(cx, has_params, has_vars, m); }
@@ -584,6 +587,10 @@ fn mk_native_fn(cx: ctxt, args: [arg], ty: t) -> t {
 
 fn mk_obj(cx: ctxt, meths: [method]) -> t { ret gen_ty(cx, ty_obj(meths)); }
 
+fn mk_iface(cx: ctxt, did: ast::def_id, tys: [t]) -> t {
+    ret gen_ty(cx, ty_iface(did, tys));
+}
+
 fn mk_res(cx: ctxt, did: ast::def_id, inner: t, tps: [t]) -> t {
     ret gen_ty(cx, ty_res(did, inner, tps));
 }
@@ -634,7 +641,7 @@ fn walk_ty(cx: ctxt, walker: ty_walk, ty: t) {
         /* no-op */
       }
       ty_box(tm) | ty_vec(tm) | ty_ptr(tm) { walk_ty(cx, walker, tm.ty); }
-      ty_tag(tid, subtys) {
+      ty_tag(_, subtys) | ty_iface(_, subtys) {
         for subty: t in subtys { walk_ty(cx, walker, subty); }
       }
       ty_rec(fields) {
@@ -703,9 +710,10 @@ fn fold_ty(cx: ctxt, fld: fold_mode, ty_0: t) -> t {
         ty = mk_vec(cx, {ty: fold_ty(cx, fld, tm.ty), mut: tm.mut});
       }
       ty_tag(tid, subtys) {
-        let new_subtys: [t] = [];
-        for subty: t in subtys { new_subtys += [fold_ty(cx, fld, subty)]; }
-        ty = mk_tag(cx, tid, new_subtys);
+        ty = mk_tag(cx, tid, vec::map(subtys, {|t| fold_ty(cx, fld, t) }));
+      }
+      ty_iface(did, subtys) {
+        ty = mk_iface(cx, did, vec::map(subtys, {|t| fold_ty(cx, fld, t) }));
       }
       ty_rec(fields) {
         let new_fields: [field] = [];
@@ -785,14 +793,10 @@ fn type_is_bool(cx: ctxt, ty: t) -> bool {
 
 fn type_is_structural(cx: ctxt, ty: t) -> bool {
     alt struct(cx, ty) {
-      ty_rec(_) { ret true; }
-      ty_tup(_) { ret true; }
-      ty_tag(_, _) { ret true; }
-      ty_fn(_) { ret true; }
-      ty_native_fn(_, _) { ret true; }
-      ty_obj(_) { ret true; }
-      ty_res(_, _, _) { ret true; }
-      _ { ret false; }
+      ty_rec(_) | ty_tup(_) | ty_tag(_, _) | ty_fn(_) |
+      ty_native_fn(_, _) | ty_obj(_) | ty_res(_, _, _) |
+      ty_iface(_, _) { true }
+      _ { false }
     }
 }
 
@@ -973,7 +977,7 @@ fn type_kind(cx: ctxt, ty: t) -> ast::kind {
       ty_opaque_closure. { kind_noncopyable }
       // Those with refcounts-to-inner raise pinned to shared,
       // lower unique to shared. Therefore just set result to shared.
-      ty_box(mt) { ast::kind_copyable }
+      ty_box(_) | ty_iface(_, _) { ast::kind_copyable }
       // Boxes and unique pointers raise pinned to shared.
       ty_vec(tm) | ty_uniq(tm) { type_kind(cx, tm.ty) }
       // Records lower to the lowest of their members.
@@ -1142,9 +1146,7 @@ fn type_is_pod(cx: ctxt, ty: t) -> bool {
       ty_send_type. | ty_type. | ty_native(_) | ty_ptr(_) { result = true; }
       // Boxed types
       ty_str. | ty_box(_) | ty_uniq(_) | ty_vec(_) | ty_fn(_) |
-      ty_native_fn(_, _) | ty_obj(_) {
-        result = false;
-      }
+      ty_native_fn(_, _) | ty_obj(_) | ty_iface(_, _) { result = false; }
       // Structural types
       ty_tag(did, tps) {
         let variants = tag_variants(cx, did);
@@ -1332,6 +1334,11 @@ fn hash_type_structure(st: sty) -> uint {
       ty_send_type. { ret 38u; }
       ty_opaque_closure. { ret 39u; }
       ty_named(t, name) { (str::hash(*name) << 5u) + hash_subty(40u, t) }
+      ty_iface(did, tys) {
+        let h = hash_def(41u, did);
+        for typ: t in tys { h += (h << 5u) + typ; }
+        ret h;
+      }
     }
 }
 
@@ -2024,6 +2031,20 @@ mod unify {
         }
     }
 
+    fn unify_tps(cx: @ctxt, expected_tps: [t], actual_tps: [t],
+                 variance: variance, finish: block([t]) -> result) -> result {
+        let result_tps = [], i = 0u;
+        for exp in expected_tps {
+            let act = actual_tps[i];
+            i += 1u;
+            let result = unify_step(cx, exp, act, variance);
+            alt result {
+              ures_ok(rty) { result_tps += [rty]; }
+              _ { ret result; }
+            }
+        }
+        finish(result_tps)
+    }
     fn unify_step(cx: @ctxt, expected: t, actual: t,
                   variance: variance) -> result {
         // FIXME: rewrite this using tuple pattern matching when available, to
@@ -2109,31 +2130,31 @@ mod unify {
           ty::ty_tag(expected_id, expected_tps) {
             alt struct(cx.tcx, actual) {
               ty::ty_tag(actual_id, actual_tps) {
-                if expected_id.crate != actual_id.crate ||
-                       expected_id.node != actual_id.node {
+                if expected_id != actual_id {
                     ret ures_err(terr_mismatch);
                 }
-                // TODO: factor this cruft out
-                let result_tps: [t] = [];
-                let i = 0u;
-                let expected_len = vec::len::<t>(expected_tps);
-                while i < expected_len {
-                    let expected_tp = expected_tps[i];
-                    let actual_tp = actual_tps[i];
-                    let result = unify_step(
-                        cx, expected_tp, actual_tp, variance);
-                    alt result {
-                      ures_ok(rty) { result_tps += [rty]; }
-                      _ { ret result; }
-                    }
-                    i += 1u;
-                }
-                ret ures_ok(mk_tag(cx.tcx, expected_id, result_tps));
+                ret unify_tps(cx, expected_tps, actual_tps, variance, {|tps|
+                    ures_ok(mk_tag(cx.tcx, expected_id, tps))
+                });
               }
               _ {/* fall through */ }
             }
             ret ures_err(terr_mismatch);
           }
+          ty_iface(expected_id, expected_tps) {
+            alt struct(cx.tcx, actual) {
+              ty::ty_iface(actual_id, actual_tps) {
+                if expected_id != actual_id {
+                    ret ures_err(terr_mismatch);
+                }
+                ret unify_tps(cx, expected_tps, actual_tps, variance, {|tps|
+                    ures_ok(mk_iface(cx.tcx, expected_id, tps))
+                });
+              }
+              _ {}
+            }
+            ret ures_err(terr_mismatch);
+          }
           ty::ty_box(expected_mt) {
             alt struct(cx.tcx, actual) {
               ty::ty_box(actual_mt) {
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index 362f01b7573..21adb2af201 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -422,7 +422,15 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item)
         tcx.tcache.insert(local_def(it.id), tpt);
         ret tpt;
       }
-      ast::item_impl(_, _, _) | ast::item_mod(_) |
+      ast::item_iface(tps, methods) {
+        let t = ty::mk_named(tcx, ty::mk_iface(tcx, local_def(it.id),
+                                               mk_ty_params(tcx, tps)),
+                             @it.ident);
+        let tpt = {kinds: ty_param_kinds(tps), ty: t};
+        tcx.tcache.insert(local_def(it.id), tpt);
+        ret tpt;
+      }
+      ast::item_impl(_, _, _, _) | ast::item_mod(_) |
       ast::item_native_mod(_) { fail; }
     }
 }
@@ -647,7 +655,7 @@ mod collect {
             write::ty_only(cx.tcx, it.id, tpt.ty);
             get_tag_variant_types(cx, tpt.ty, variants, ty_params);
           }
-          ast::item_impl(_, selfty, ms) {
+          ast::item_impl(_, _, selfty, ms) {
             for m in ms {
                 let ty = ty::mk_fn(cx.tcx, ty_of_fn_decl(cx.tcx, m_collect,
                                                          m.decl));
@@ -1429,9 +1437,9 @@ fn lookup_method(fcx: @fn_ctxt, isc: resolve::iscopes,
               some(m) {
                 let (n_tps, self_ty) = if did.crate == ast::local_crate {
                     alt fcx.ccx.tcx.items.get(did.node) {
-                      ast_map::node_item(@{node: ast::item_impl(tps, st, _),
+                      ast_map::node_item(@{node: ast::item_impl(ts, _, st, _),
                                            _}) {
-                        (vec::len(tps), ast_ty_to_ty_crate(fcx.ccx, st))
+                        (vec::len(ts), ast_ty_to_ty_crate(fcx.ccx, st))
                       }
                     }
                 } else {
@@ -2637,7 +2645,7 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
         // Now remove the info from the stack.
         vec::pop(ccx.self_infos);
       }
-      ast::item_impl(_, ty, ms) {
+      ast::item_impl(_, _, ty, ms) {
         ccx.self_infos += [self_impl(ast_ty_to_ty(ccx.tcx, m_check, ty))];
         for m in ms { check_method(ccx, m); }
         vec::pop(ccx.self_infos);
diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs
index a94e860b764..9952dd55546 100644
--- a/src/comp/syntax/ast.rs
+++ b/src/comp/syntax/ast.rs
@@ -490,7 +490,9 @@ tag item_ {
     item_obj(_obj, [ty_param], /* constructor id */node_id);
     item_res(fn_decl /* dtor */, [ty_param], blk,
              node_id /* dtor id */, node_id /* ctor id */);
-    item_impl([ty_param], @ty /* self */, [@method]);
+    item_iface([ty_param], [ty_method]);
+    item_impl([ty_param], option::t<@ty> /* iface */,
+              @ty /* self */, [@method]);
 }
 
 type native_item =
diff --git a/src/comp/syntax/fold.rs b/src/comp/syntax/fold.rs
index 0c136819ce9..d06c98b0a31 100644
--- a/src/comp/syntax/fold.rs
+++ b/src/comp/syntax/fold.rs
@@ -65,7 +65,7 @@ type a_f =
      fold_native_mod: fn@(native_mod) -> native_mod,
      fold_variant: fn@(variant) -> variant,
      fold_ident: fn@(&&ident) -> ident,
-     fold_path: fn@(@path) -> @path,
+     fold_path: fn@(&&@path) -> @path,
      fold_local: fn@(&&@local) -> @local,
      map_exprs: fn@(fn@(&&@expr) -> @expr, [@expr]) -> [@expr],
      new_id: fn@(node_id) -> node_id,
@@ -94,7 +94,7 @@ fn nf_mod_dummy(_m: _mod) -> _mod { fail; }
 fn nf_native_mod_dummy(_n: native_mod) -> native_mod { fail; }
 fn nf_variant_dummy(_v: variant) -> variant { fail; }
 fn nf_ident_dummy(&&_i: ident) -> ident { fail; }
-fn nf_path_dummy(_p: @path) -> @path { fail; }
+fn nf_path_dummy(&&_p: @path) -> @path { fail; }
 fn nf_obj_field_dummy(_o: obj_field) -> obj_field { fail; }
 fn nf_local_dummy(&&_o: @local) -> @local { fail; }
 
@@ -243,10 +243,13 @@ fn noop_fold_item_underscore(i: item_, fld: ast_fold) -> item_ {
                       methods: vec::map(o.methods, fld.fold_method)},
                      typms, d)
           }
-          item_impl(tps, ty, methods) {
-            item_impl(tps, fld.fold_ty(ty),
+          item_impl(tps, ifce, ty, methods) {
+            item_impl(tps, option::map(ifce, fld.fold_ty), fld.fold_ty(ty),
                       vec::map(methods, fld.fold_method))
           }
+          item_iface(tps, methods) {
+            item_iface(tps, methods)
+          }
           item_res(decl, typms, body, did, cid) {
             item_res(fold_fn_decl(decl, fld), typms, fld.fold_block(body),
                      did, cid)
@@ -471,7 +474,7 @@ fn noop_fold_variant(v: variant_, fld: ast_fold) -> variant_ {
 
 fn noop_fold_ident(&&i: ident, _fld: ast_fold) -> ident { ret i; }
 
-fn noop_fold_path(p: path_, fld: ast_fold) -> path_ {
+fn noop_fold_path(&&p: path_, fld: ast_fold) -> path_ {
     ret {global: p.global,
          idents: vec::map(p.idents, fld.fold_ident),
          types: vec::map(p.types, fld.fold_ty)};
@@ -630,7 +633,7 @@ fn make_fold(afp: ast_fold_precursor) -> ast_fold {
     fn f_ident(afp: ast_fold_precursor, f: ast_fold, &&x: ident) -> ident {
         ret afp.fold_ident(x, f);
     }
-    fn f_path(afp: ast_fold_precursor, f: ast_fold, x: @path) -> @path {
+    fn f_path(afp: ast_fold_precursor, f: ast_fold, &&x: @path) -> @path {
         ret @{node: afp.fold_path(x.node, f), span: afp.new_span(x.span)};
     }
     fn f_local(afp: ast_fold_precursor, f: ast_fold, &&x: @local) -> @local {
diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs
index 484659f4bc5..8299f23ae20 100644
--- a/src/comp/syntax/parse/parser.rs
+++ b/src/comp/syntax/parse/parser.rs
@@ -165,7 +165,7 @@ fn bad_expr_word_table() -> hashmap<str, ()> {
                  "cont", "ret", "be", "fail", "type", "resource", "check",
                  "assert", "claim", "native", "fn", "lambda", "pure",
                  "unsafe", "block", "import", "export", "let", "const",
-                 "log", "tag", "obj", "copy", "sendfn", "impl"] {
+                 "log", "tag", "obj", "copy", "sendfn", "impl", "iface"] {
         words.insert(word, ());
     }
     words
@@ -285,7 +285,7 @@ fn parse_ty_fn(proto: ast::proto, p: parser) -> ast::ty_ {
                     constraints: constrs});
 }
 
-fn parse_ty_obj(p: parser) -> ast::ty_ {
+fn parse_ty_methods(p: parser) -> [ast::ty_method] {
     fn parse_method_sig(p: parser) -> ast::ty_method {
         let flo = p.get_lo_pos();
         let proto: ast::proto = parse_method_proto(p);
@@ -298,10 +298,8 @@ fn parse_ty_obj(p: parser) -> ast::ty_ {
           }
         }
     }
-    let meths =
-        parse_seq(token::LBRACE, token::RBRACE, seq_sep_none(),
-                  parse_method_sig, p);
-    ret ast::ty_obj(meths.node);
+    parse_seq(token::LBRACE, token::RBRACE, seq_sep_none(),
+              parse_method_sig, p).node
 }
 
 fn parse_mt(p: parser) -> ast::mt {
@@ -519,7 +517,7 @@ fn parse_ty(p: parser, colons_before_params: bool) -> @ast::ty {
     } else if eat_word(p, "sendfn") {
         t = parse_ty_fn(ast::proto_send, p);
     } else if eat_word(p, "obj") {
-        t = parse_ty_obj(p);
+        t = ast::ty_obj(parse_ty_methods(p));
     } else if p.peek() == token::MOD_SEP || is_ident(p.peek()) {
         let path = parse_path(p);
         t = ast::ty_path(path, p.get_id());
@@ -676,47 +674,22 @@ fn is_plain_ident(p: parser) -> bool {
 
 fn parse_path(p: parser) -> @ast::path {
     let lo = p.get_lo_pos();
-    let hi = lo;
-
-    let global;
-    if p.peek() == token::MOD_SEP {
-        global = true;
-        p.bump();
-    } else { global = false; }
-
-    let ids: [ast::ident] = [];
-    while true {
-        alt p.peek() {
-          token::IDENT(i, _) {
-            hi = p.get_hi_pos();
-            ids += [p.get_str(i)];
-            hi = p.get_hi_pos();
-            p.bump();
-            if p.peek() == token::MOD_SEP && p.look_ahead(1u) != token::LT {
-                p.bump();
-            } else { break; }
-          }
-          _ { break; }
-        }
+    let global = eat(p, token::MOD_SEP), ids = [parse_ident(p)];
+    while p.look_ahead(1u) != token::LT && eat(p, token::MOD_SEP) {
+        ids += [parse_ident(p)];
     }
-    ret @spanned(lo, hi, {global: global, idents: ids, types: []});
+    ret @spanned(lo, p.get_last_hi_pos(),
+                 {global: global, idents: ids, types: []});
 }
 
-fn parse_path_and_ty_param_substs(p: parser) -> @ast::path {
+fn parse_path_and_ty_param_substs(p: parser, colons: bool) -> @ast::path {
     let lo = p.get_lo_pos();
     let path = parse_path(p);
-    if p.peek() == token::MOD_SEP {
-        p.bump();
-
-        let seq =
-            parse_seq_lt_gt(some(token::COMMA), {|p| parse_ty(p, false)}, p);
-        let hi = seq.span.hi;
-        path = @spanned(lo, hi,
-                        {global: path.node.global,
-                         idents: path.node.idents,
-                         types: seq.node});
-    }
-    ret path;
+    if colons ? eat(p, token::MOD_SEP) : p.peek() == token::LT {
+        let seq = parse_seq_lt_gt(some(token::COMMA),
+                                  {|p| parse_ty(p, false)}, p);
+        @spanned(lo, seq.span.hi, {types: seq.node with path.node})
+    } else { path }
 }
 
 fn parse_mutability(p: parser) -> ast::mutability {
@@ -958,7 +931,7 @@ fn parse_bottom_expr(p: parser) -> @ast::expr {
                   is_ident(p.peek()) && !is_word(p, "true") &&
                       !is_word(p, "false") {
         check_bad_word(p);
-        let pth = parse_path_and_ty_param_substs(p);
+        let pth = parse_path_and_ty_param_substs(p, true);
         hi = pth.span.hi;
         ex = ast::expr_path(pth);
     } else {
@@ -984,10 +957,11 @@ fn parse_syntax_ext(p: parser) -> @ast::expr {
 }
 
 fn parse_syntax_ext_naked(p: parser, lo: uint) -> @ast::expr {
-    let pth = parse_path(p);
-    if vec::len(pth.node.idents) == 0u {
-        p.fatal("expected a syntax expander name");
+    alt p.peek() {
+      token::IDENT(_, _) {}
+      _ { p.fatal("expected a syntax expander name"); }
     }
+    let pth = parse_path(p);
     //temporary for a backwards-compatible cycle:
     let sep = seq_sep(token::COMMA);
     let es =
@@ -1518,7 +1492,7 @@ fn parse_pat(p: parser) -> @ast::pat {
             let sub = eat(p, token::AT) ? some(parse_pat(p)) : none;
             pat = ast::pat_bind(name, sub);
         } else {
-            let tag_path = parse_path_and_ty_param_substs(p);
+            let tag_path = parse_path_and_ty_param_substs(p, true);
             hi = tag_path.span.hi;
             let args: [@ast::pat];
             alt p.peek() {
@@ -1751,12 +1725,9 @@ fn parse_ty_param(p: parser) -> ast::ty_param {
 }
 
 fn parse_ty_params(p: parser) -> [ast::ty_param] {
-    let ty_params: [ast::ty_param] = [];
-    if p.peek() == token::LT {
-        p.bump();
-        ty_params = parse_seq_to_gt(some(token::COMMA), parse_ty_param, p);
-    }
-    ret ty_params;
+    if eat(p, token::LT) {
+        parse_seq_to_gt(some(token::COMMA), parse_ty_param, p)
+    } else { [] }
 }
 
 fn parse_fn_decl(p: parser, proto: ast::proto, purity: ast::purity)
@@ -1866,15 +1837,47 @@ fn parse_item_obj(p: parser, attrs: [ast::attribute]) -> @ast::item {
                 attrs);
 }
 
-fn parse_item_impl(p: parser, attrs: [ast::attribute]) -> @ast::item {
+fn parse_item_iface(p: parser, attrs: [ast::attribute]) -> @ast::item {
     let lo = p.get_last_lo_pos(), ident = parse_ident(p),
+        tps = parse_ty_params(p), meths = parse_ty_methods(p);
+    ret mk_item(p, lo, p.get_last_hi_pos(), ident,
+                ast::item_iface(tps, meths), attrs);
+}
+
+fn parse_item_impl(p: parser, attrs: [ast::attribute]) -> @ast::item {
+    let lo = p.get_last_lo_pos(), ident, tps, ifce;
+    fn wrap_path(p: parser, pt: @ast::path) -> @ast::ty {
+        @{node: ast::ty_path(pt, p.get_id()), span: pt.span}
+    }
+    if eat_word(p, "of") {
+        let path = parse_path_and_ty_param_substs(p, false);
+        tps = vec::map(path.node.types, {|tp|
+            alt tp.node {
+              ast::ty_path(pt, _) {
+                if vec::len(pt.node.idents) == 1u &&
+                   vec::len(pt.node.types) == 0u {
+                     ret {ident: pt.node.idents[0], kind: ast::kind_sendable};
+                }
+              }
+              _ {}
+            }
+            p.fatal("only single-word, parameter-less types allowed here");
+        });
+        ident = path.node.idents[vec::len(path.node.idents)-1u];
+        ifce = some(wrap_path(p, path));
+    } else {
+        ident = parse_ident(p);
         tps = parse_ty_params(p);
+        ifce = if eat_word(p, "of") {
+            some(wrap_path(p, parse_path_and_ty_param_substs(p, false)))
+        } else { none };
+    };
     expect_word(p, "for");
     let ty = parse_ty(p, false), meths = [];
     expect(p, token::LBRACE);
     while !eat(p, token::RBRACE) { meths += [parse_method(p, true)]; }
     ret mk_item(p, lo, p.get_last_hi_pos(), ident,
-                ast::item_impl(tps, ty, meths), attrs);
+                ast::item_impl(tps, ifce, ty, meths), attrs);
 }
 
 fn parse_item_res(p: parser, attrs: [ast::attribute]) -> @ast::item {
@@ -2145,6 +2148,8 @@ fn parse_item(p: parser, attrs: [ast::attribute]) -> option::t<@ast::item> {
     } else if is_word(p, "obj") && p.look_ahead(1u) != token::LPAREN {
         p.bump();
         ret some(parse_item_obj(p, attrs));
+    } else if eat_word(p, "iface") {
+        ret some(parse_item_iface(p, attrs));
     } else if eat_word(p, "impl") {
         ret some(parse_item_impl(p, attrs));
     } else if eat_word(p, "resource") {
diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs
index e2856c60f8d..99793d1b816 100644
--- a/src/comp/syntax/print/pprust.rs
+++ b/src/comp/syntax/print/pprust.rs
@@ -311,14 +311,7 @@ fn print_type(s: ps, &&ty: @ast::ty) {
       ast::ty_obj(methods) {
         head(s, "obj");
         bopen(s);
-        for m: ast::ty_method in methods {
-            hardbreak_if_not_bol(s);
-            cbox(s, indent_unit);
-            maybe_print_comment(s, m.span.lo);
-            print_ty_fn(s, m.decl, some(m.ident));
-            word(s.s, ";");
-            end(s);
-        }
+        for m in methods { print_ty_method(s, m); }
         bclose(s, ty.span);
       }
       ast::ty_path(path, _) { print_path(s, path, false); }
@@ -473,11 +466,19 @@ fn print_item(s: ps, &&item: @ast::item) {
         }
         bclose(s, item.span);
       }
-      ast::item_impl(tps, ty, methods) {
+      ast::item_impl(tps, ifce, ty, methods) {
         head(s, "impl");
         word(s.s, item.ident);
         print_type_params(s, tps);
-        nbsp(s);
+        space(s.s);
+        alt ifce {
+          some(ty) {
+            word_nbsp(s, "of");
+            print_type(s, ty);
+            space(s.s);
+          }
+          _ {}
+        }
         word_nbsp(s, "for");
         print_type(s, ty);
         space(s.s);
@@ -491,6 +492,14 @@ fn print_item(s: ps, &&item: @ast::item) {
         }
         bclose(s, item.span);
       }
+      ast::item_iface(tps, methods) {
+        head(s, "iface");
+        word(s.s, item.ident);
+        print_type_params(s, tps);
+        bopen(s);
+        for meth in methods { print_ty_method(s, meth); }
+        bclose(s, item.span);
+      }
       ast::item_res(decl, tps, body, dt_id, ct_id) {
         head(s, "resource");
         word(s.s, item.ident);
@@ -506,6 +515,15 @@ fn print_item(s: ps, &&item: @ast::item) {
     s.ann.post(ann_node);
 }
 
+fn print_ty_method(s: ps, m: ast::ty_method) {
+    hardbreak_if_not_bol(s);
+    cbox(s, indent_unit);
+    maybe_print_comment(s, m.span.lo);
+    print_ty_fn(s, m.decl, some(m.ident));
+    word(s.s, ";");
+    end(s);
+}
+
 fn print_outer_attributes(s: ps, attrs: [ast::attribute]) {
     let count = 0;
     for attr: ast::attribute in attrs {
diff --git a/src/comp/syntax/visit.rs b/src/comp/syntax/visit.rs
index dc91064dba6..ecdf8c9a05d 100644
--- a/src/comp/syntax/visit.rs
+++ b/src/comp/syntax/visit.rs
@@ -120,13 +120,20 @@ fn visit_item<E>(i: @item, e: E, v: vt<E>) {
                              some(m.ident), m.id, e, v);
         }
       }
-      item_impl(_, ty, methods) {
-        visit_ty(ty, e, v);
+      item_impl(_, ifce, ty, methods) {
+        alt ifce { some(ty) { v.visit_ty(ty, e, v); } _ {} }
+        v.visit_ty(ty, e, v);
         for m in methods {
             v.visit_fn_proto(m.decl, m.tps, m.body, m.span,
                              some(m.ident), m.id, e, v);
         }
       }
+      item_iface(_, methods) {
+        for m in methods {
+            for a in m.decl.inputs { v.visit_ty(a.ty, e, v); }
+            v.visit_ty(m.decl.output, e, v);
+        }
+      }
     }
 }