]> git.lizzy.rs Git - rust.git/commitdiff
Allow classes to implement ifaces
authorTim Chevalier <chevalier@alum.wellesley.edu>
Wed, 11 Apr 2012 23:18:00 +0000 (16:18 -0700)
committerTim Chevalier <chevalier@alum.wellesley.edu>
Wed, 11 Apr 2012 23:20:01 +0000 (16:20 -0700)
Introduce syntax like:

iface animal { ... }
class cat implements animal { ... }

to allow classes to implement ifaces. Casting classes to ifaces
is *not* yet supported. ifaces that a class implements are not
yet included in metadata.

The syntax is subject to change, and may go away completely if we
decide to use duck typing to relate classes with ifaces (see
http://smallcultfollowing.com/babysteps/blog/2012/04/10/declared-vs-duckish-typing/ )

19 files changed:
src/librustsyntax/ast.rs
src/librustsyntax/fold.rs
src/librustsyntax/parse/parser.rs
src/librustsyntax/print/pprust.rs
src/librustsyntax/visit.rs
src/rustc/metadata/encoder.rs
src/rustc/middle/ast_map.rs
src/rustc/middle/mutbl.rs
src/rustc/middle/region.rs
src/rustc/middle/resolve.rs
src/rustc/middle/trans/base.rs
src/rustc/middle/trans/reachable.rs
src/rustc/middle/tstate/pre_post_conditions.rs
src/rustc/middle/ty.rs
src/rustc/middle/typeck.rs
src/test/compile-fail/class-implements-bad-iface.rs [new file with mode: 0644]
src/test/compile-fail/class-implements-int.rs [new file with mode: 0644]
src/test/compile-fail/class-method-missing.rs [new file with mode: 0644]
src/test/run-pass/class-implement-ifaces.rs [new file with mode: 0644]

index 885e0943b9b45af504e65e267afe01c6d1f2b31b..fa190a120e9b4c2716faee0588e52e97b77001ed 100644 (file)
@@ -641,6 +641,9 @@ enum attr_style { attr_outer, attr_inner, }
 #[auto_serialize]
 type attribute_ = {style: attr_style, value: meta_item};
 
+#[auto_serialize]
+type iface_ref = {path: @path, id: node_id};
+
 #[auto_serialize]
 type item = {ident: ident, attrs: [attribute],
              id: node_id, node: item_, span: span};
@@ -656,6 +659,7 @@ enum item_ {
     item_res(fn_decl /* dtor */, [ty_param], blk /* dtor body */,
              node_id /* dtor id */, node_id /* ctor id */),
     item_class([ty_param], /* ty params for class */
+               [iface_ref],   /* ifaces this class implements */
                [@class_member], /* methods, etc. */
                                /* (not including ctor) */
                class_ctor
index afde21d2965b3d9a5861239b226c32922f53a0fa..f40c351b4ef017b18afd148e664f55310aade700 100644 (file)
@@ -269,11 +269,13 @@ fn noop_fold_item_underscore(i: item_, fld: ast_fold) -> item_ {
             item_enum(vec::map(variants, fld.fold_variant),
                       fold_ty_params(typms, fld))
           }
-          item_class(typms, items, ctor) {
+          item_class(typms, ifaces, items, ctor) {
               let ctor_body = fld.fold_block(ctor.node.body);
               let ctor_decl = fold_fn_decl(ctor.node.dec, fld);
               let ctor_id   = fld.new_id(ctor.node.id);
-              item_class(typms,
+              item_class(typms, vec::map(ifaces, {|p|
+                              {path: fld.fold_path(p.path),
+                               id: fld.new_id(p.id)}}),
                          vec::map(items, fld.fold_class_item),
                          {node: {body: ctor_body,
                                  dec: ctor_decl,
index 01224845338db878ff099cbe701451c5974ca72e..17a2c4e217fbac4a1d04802bfebfded42921a3c5 100644 (file)
@@ -2143,11 +2143,19 @@ fn ident_to_path_tys(p: parser, i: ast::ident,
     @spanned(s.lo, s.hi, p_)
 }
 
+fn parse_iface_ref_list(p:parser) -> [ast::iface_ref] {
+    parse_seq_to_before_end(token::LBRACE, seq_sep(token::COMMA),
+                   {|p| {path: parse_path(p), id: p.get_id()}}, p)
+}
+
 fn parse_item_class(p: parser, attrs: [ast::attribute]) -> @ast::item {
     let lo = p.last_span.lo;
     let class_name = parse_value_ident(p);
     let ty_params = parse_ty_params(p);
     let class_path = ident_to_path_tys(p, class_name, ty_params);
+    let ifaces : [ast::iface_ref] = if eat_word(p, "implements")
+                                       { parse_iface_ref_list(p) }
+                                    else { [] };
     expect(p, token::LBRACE);
     let mut ms: [@ast::class_member] = [];
     let ctor_id = p.get_id();
@@ -2163,9 +2171,8 @@ fn parse_item_class(p: parser, attrs: [ast::attribute]) -> @ast::item {
     p.bump();
     alt the_ctor {
       some((ct_d, ct_b, ct_s)) {
-          ret mk_item(p, lo, p.last_span.hi,
-                                             class_name,
-         ast::item_class(ty_params, ms,
+          ret mk_item(p, lo, p.last_span.hi, class_name,
+                      ast::item_class(ty_params, ifaces, ms,
                          {node: {id: ctor_id,
                                  self_id: p.get_id(),
                                  dec: ct_d,
index 1d2094762f19b634f7af42819f9056bd8d0a4d58..d45ae9e0056ca5767af2978ce21b21bd9c71ced6 100644 (file)
@@ -493,10 +493,13 @@ fn print_item(s: ps, &&item: @ast::item) {
             bclose(s, item.span);
         }
       }
-      ast::item_class(tps,items,ctor) {
+      ast::item_class(tps,ifaces,items,ctor) {
           head(s, "class");
           word_nbsp(s, item.ident);
           print_type_params(s, tps);
+          word_space(s, "implements");
+          commasep(s, inconsistent, ifaces, {|s, p|
+                      print_path(s, p.path, false)});
           bopen(s);
           hardbreak_if_not_bol(s);
           maybe_print_comment(s, ctor.span.lo);
index 33af7fe366119ee1de571eb40c2b5b58b6eb4fc4..20d85e08989a5c58d1332d01fad21ec6817ca89d 100644 (file)
@@ -137,11 +137,12 @@ fn visit_item<E>(i: @item, e: E, v: vt<E>) {
             visit_method_helper(m, e, v)
         }
       }
-      item_class(tps, members, ctor) {
+      item_class(tps, ifaces, members, ctor) {
           v.visit_ty_params(tps, e, v);
           for members.each {|m|
              v.visit_class_item(m, e, v);
           }
+          for ifaces.each {|p| visit_path(p.path, e, v); }
           visit_class_ctor_helper(ctor, i.ident, tps,
                                   ast_util::local_def(i.id), e, v);
       }
index 4587c1c740df29ef2907ac978a7f1fd939838945..b46bc091aaa32db61cbbb23396cb74d86b01f4ff 100644 (file)
@@ -151,7 +151,7 @@ fn encode_module_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt,
             encode_def_id(ebml_w, local_def(it.id));
             ebml_w.end_tag();
           }
-          item_class(tps,items,ctor) {
+          item_class(_, _, items, ctor) {
             add_to_index(ebml_w, path, index, it.ident);
             ebml_w.start_tag(tag_paths_data_item);
             encode_name(ebml_w, it.ident);
@@ -556,7 +556,7 @@ fn add_to_index_(item: @item, ebml_w: ebml::writer,
         encode_enum_variant_info(ecx, ebml_w, item.id, variants,
                                  path, index, tps);
       }
-      item_class(tps,items,ctor) {
+      item_class(tps, _ifaces, items,ctor) {
         /* First, encode the fields and methods
            These come first because we need to write them to make
            the index, and the index needs to be in the item for the
@@ -573,7 +573,7 @@ fn add_to_index_(item: @item, ebml_w: ebml::writer,
         encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id));
         encode_name(ebml_w, item.ident);
         encode_path(ebml_w, path, ast_map::path_name(item.ident));
-
+        /* FIXME: encode ifaces */
         /* Encode def_ids for each field and method
          for methods, write all the stuff get_iface_method
         needs to know*/
@@ -729,7 +729,7 @@ fn encode_info_for_items(ecx: @encode_ctxt, ebml_w: ebml::writer,
                 encode_info_for_item(ecx, ebml_w, i, index, *pt);
                 /* encode ctor, then encode items */
                 alt i.node {
-                  item_class(tps,_,ctor) {
+                  item_class(tps,_,_,ctor) {
                    /* this is assuming that ctors aren't inlined...
                       probably shouldn't assume that */
                    #debug("encoding info for ctor %s %d", i.ident,
index b4a6e194b38c89a4dd8ba106955e575b6ec33f11..5c217fb6e96fff83c3bb723a404b81500910d100 100644 (file)
@@ -203,7 +203,7 @@ fn map_item(i: @item, cx: ctx, v: vt) {
             cx.map.insert(nitem.id, node_native_item(nitem, abi, @cx.path));
         }
       }
-      item_class(_, items, ctor) {
+      item_class(_, _, items, ctor) {
           let d_id = ast_util::local_def(i.id);
           let p = extend(cx, i.ident);
           for items.each {|ci|
index fa2e8ddb0d7c364bf690244f9ddd337f337ac8ef..45d0beeab81fbf685bafa0bd8d4f5dc0a7c9aaa9 100644 (file)
@@ -216,7 +216,7 @@ fn visit_expr(ex: @expr, &&cx: @ctx, v: visit::vt<@ctx>) {
 
 fn visit_item(item: @item, &&cx: @ctx, v: visit::vt<@ctx>) {
     alt item.node {
-      item_class(tps, items, ctor) {
+      item_class(tps, _, items, ctor) {
          v.visit_ty_params(tps, cx, v);
          vec::map::<@class_member, ()>(items,
              {|i| v.visit_class_item(i, cx, v); });
index bc662738ba42bc5e5be42194ee0202dd4aaf1679..cea516c3cf40b4ddc8c4e6f7bcf1962b22fe7adc 100644 (file)
@@ -530,7 +530,7 @@ fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
             {parent: pa_fn_item(item.id),
              scope: cx.scope.binding_subscope(item.id)}
           }
-          ast::item_impl(_, _, _, _) | ast::item_class(_, _, _) {
+          ast::item_impl(_, _, _, _) | ast::item_class(_, _, _, _) {
             {parent: pa_item(item.id),
              scope: cx.scope.self_subscope(item.id)}
           }
index 611a3e36d051ce7a01a1c9d5970af7d1711ccff6..897a562a4d3686a9049a1420dca0f96d018029d4 100644 (file)
@@ -391,7 +391,7 @@ fn resolve_names(e: @env, c: @ast::crate) {
     e.used_imports.track = true;
     let v =
         @{visit_native_item: visit_native_item_with_scope,
-          visit_item: bind visit_item_with_scope(e, _, _, _),
+          visit_item: bind walk_item(e, _, _, _),
           visit_block: visit_block_with_scope,
           visit_decl: visit_decl_with_scope,
           visit_arm: visit_arm_with_scope,
@@ -407,6 +407,24 @@ fn resolve_names(e: @env, c: @ast::crate) {
     e.used_imports.track = false;
     e.sess.abort_if_errors();
 
+    fn walk_item(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) {
+        visit_item_with_scope(e, i, sc, v);
+        /*
+          Resolve the ifaces that a class implements; do nothing for
+          non-class items
+         */
+        alt i.node {
+           ast::item_class(_, ifaces, _, _) {
+             /* visit the iface paths... */
+             for ifaces.each {|p|
+               maybe_insert(e, p.id,
+                 lookup_path_strict(*e, sc, p.path.span, p.path.node,
+                                    ns_type))};
+           }
+           _ {}
+        }
+    }
+
     fn walk_expr(e: @env, exp: @ast::expr, sc: scopes, v: vt<scopes>) {
         visit::visit_expr(exp, sc, v);
         alt exp.node {
@@ -517,7 +535,7 @@ fn visit_item_with_scope(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) {
             v.visit_ty(m.decl.output, msc, v);
         }
       }
-      ast::item_class(tps, members, ctor) {
+      ast::item_class(tps, ifaces, members, ctor) {
         visit::visit_ty_params(tps, sc, v);
         // Can maybe skip this now that we require self on class fields
         let class_scope = cons(scope_item(i), @sc);
@@ -993,7 +1011,7 @@ fn in_scope(e: env, sp: span, name: ident, s: scope, ns: namespace) ->
               ast::item_native_mod(m) {
                 ret lookup_in_local_native_mod(e, it.id, sp, name, ns);
               }
-              ast::item_class(tps, members, ctor) {
+              ast::item_class(tps, _, members, ctor) {
                   if ns == ns_type {
                     ret lookup_in_ty_params(e, name, tps);
                   }
@@ -1278,7 +1296,7 @@ fn found_def_item(i: @ast::item, ns: namespace) -> option<def> {
           _ { }
         }
       }
-      ast::item_class(_, _, _) {
+      ast::item_class(_, _, _, _) {
           if ns == ns_type {
             ret some(ast::def_class(local_def(i.id)));
           }
@@ -1407,7 +1425,11 @@ fn list_search<T: copy, U: copy>(ls: list<T>, f: fn(T) -> option<U>)
 
 fn lookup_in_local_mod(e: env, node_id: node_id, sp: span, id: ident,
                        ns: namespace, dr: dir) -> option<def> {
-    let info = e.mod_map.get(node_id);
+    let info = alt e.mod_map.find(node_id) {
+            some(x) { x }
+            none { e.sess.span_bug(sp, #fmt("lookup_in_local_mod: \
+                     module %d not in mod_map", node_id)); }
+    };
     if dr == outside && !is_exported(e, id, info) {
         // if we're in a native mod, then dr==inside, so info.m is some _mod
         ret none; // name is not visible
@@ -1582,7 +1604,7 @@ fn index_mod(md: ast::_mod) -> mod_index {
                 variant_idx += 1u;
             }
           }
-          ast::item_class(tps, items, ctor) {
+          ast::item_class(tps, _, items, ctor) {
               // add the class name itself
               add_to_index(index, it.ident, mie_item(it));
               // add the constructor decl
index 6c3e81b4999159bc8f5582eadabd57713602da25..92fad70a642f6605e0b63ead2164caf17b0f1254 100644 (file)
@@ -4432,9 +4432,11 @@ fn trans_item(ccx: @crate_ctxt, item: ast::item) {
         };
         native::trans_native_mod(ccx, native_mod, abi);
       }
-      ast::item_class(tps, items, ctor) {
+      ast::item_class(tps, _ifaces, items, ctor) {
         if tps.len() == 0u {
           let psubsts = {tys: ty::ty_params_to_tys(ccx.tcx, tps),
+                         // FIXME: vtables have to get filled in depending
+                         // on ifaces
                          vtables: none,
                          bounds: @[]};
           trans_class_ctor(ccx, *path, ctor.node.dec, ctor.node.body,
index 3a462ff7392717142261647193d30daf9ac8776d..863549f458e39d7096f8cc03cdf4c1f2e9707dca 100644 (file)
@@ -102,7 +102,7 @@ fn traverse_public_item(cx: ctx, item: @item) {
             }
         }
       }
-      item_class(tps, items, ctor) {
+      item_class(tps, _ifaces, items, ctor) {
         cx.rmap.insert(ctor.node.id, ());
         for vec::each(items) {|item|
             alt item.node {
index fc74db67b37976fbddad86b32b2d875751af9ae5..16578d8b1f307f44a40c2035f7ad3a88e70bb537 100644 (file)
@@ -57,8 +57,8 @@ fn find_pre_post_item(ccx: crate_ctxt, i: item) {
              ccx: ccx};
         find_pre_post_fn(fcx, body);
       }
-      item_class(_,_,_) {
-          fail "find_pre_post_item: implement item_class";
+      item_class(_,_,_,_) {
+          fail "find_pre_post_item: shouldn't be called on item_class";
       }
       item_impl(_, _, _, ms) {
         for ms.each {|m| find_pre_post_method(ccx, m); }
index 0852edcbea673649622f72f1130e15992fe2767b..068f23826914b16abef75925cf4a322bc9fbb062 100644 (file)
@@ -2261,7 +2261,7 @@ fn lookup_class_fields(cx: ctxt, did: ast::def_id) -> [field_ty] {
     alt cx.items.find(did.node) {
        some(ast_map::node_item(i,_)) {
          alt i.node {
-           ast::item_class(_, items, _) {
+           ast::item_class(_, _, items, _) {
                class_field_tys(items)
            }
            _ { cx.sess.bug("class ID bound to non-class"); }
@@ -2303,7 +2303,7 @@ fn lookup_public_fields(cx: ctxt, did: ast::def_id) -> [field_ty] {
 fn lookup_class_method_ids(cx: ctxt, did: ast::def_id)
     : is_local(did) -> [{name: ident, id: node_id, privacy: privacy}] {
     alt cx.items.find(did.node) {
-       some(ast_map::node_item(@{node: item_class(_,items,_), _}, _)) {
+       some(ast_map::node_item(@{node: item_class(_,_,items,_), _}, _)) {
          let (_,ms) = split_class_items(items);
          vec::map(ms, {|m| {name: m.ident, id: m.id,
                          privacy: m.privacy}})
index a5affba6becd96fe0fbdbaada3be1491685ec152..b3a07cb211410c056daef64ef417abc56236bc90 100644 (file)
@@ -128,16 +128,23 @@ fn lookup_local(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> ty_vid {
     }
 }
 
-fn lookup_def(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> ast::def {
-    alt fcx.ccx.tcx.def_map.find(id) {
+fn lookup_def_tcx(tcx: ty::ctxt, sp: span, id: ast::node_id) -> ast::def {
+    alt tcx.def_map.find(id) {
       some(x) { x }
       _ {
-        fcx.ccx.tcx.sess.span_fatal(sp,
-                                    "internal error looking up a definition")
+        tcx.sess.span_fatal(sp, "internal error looking up a definition")
       }
     }
 }
 
+fn lookup_def_ccx(ccx: @crate_ctxt, sp: span, id: ast::node_id) -> ast::def {
+    lookup_def_tcx(ccx.tcx, sp, id)
+}
+
+fn lookup_def(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> ast::def {
+    lookup_def_ccx(fcx.ccx, sp, id)
+}
+
 // Returns the type parameter count and the type for the given definition.
 fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
    ty_param_bounds_and_ty {
@@ -567,7 +574,7 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item)
         tcx.tcache.insert(local_def(it.id), tpt);
         ret tpt;
       }
-      ast::item_class(tps,_,_) {
+      ast::item_class(tps,_,_,_) {
           let {bounds,params} = mk_ty_params(tcx, tps);
           let t = ty::mk_class(tcx, local_def(it.id), params);
           let tpt = {bounds: bounds, ty: t};
@@ -1020,6 +1027,7 @@ fn instantiate_bound_regions(tcx: ty::ctxt, region: ty::region, &&ty: ty::t)
     }
 }
 
+
 // Item collection - a pair of bootstrap passes:
 //
 // (1) Collect the IDs of all type items (typedefs) and store them in a table.
@@ -1078,7 +1086,7 @@ fn store_methods<T>(tcx: ty::ctxt, id: ast::node_id,
               store_methods::<ast::ty_method>(tcx, id, ms, {|m|
                           ty_of_ty_method(tcx, m_collect, m)});
           }
-          ast_map::node_item(@{node: ast::item_class(_,its,_), _}, _) {
+          ast_map::node_item(@{node: ast::item_class(_,_,its,_), _}, _) {
               let (_,ms) = split_class_items(its);
               // All methods need to be stored, since lookup_method
               // relies on the same method cache for self-calls
@@ -1087,6 +1095,57 @@ fn store_methods<T>(tcx: ty::ctxt, id: ast::node_id,
           }
         }
     }
+    fn check_methods_against_iface(tcx: ty::ctxt, tps: [ast::ty_param],
+                          selfty: ty::t, t: @ast::ty, ms: [@ast::method]) {
+      let i_bounds = ty_param_bounds(tcx, m_collect, tps);
+      let my_methods = convert_methods(tcx, ms, i_bounds, some(selfty));
+      let iface_ty = ast_ty_to_ty(tcx, m_collect, t);
+      alt ty::get(iface_ty).struct {
+        ty::ty_iface(did, tys) {
+         // Store the iface type in the type node
+         alt check t.node {
+           ast::ty_path(_, t_id) {
+             write_ty_to_tcx(tcx, t_id, iface_ty);
+           }
+         }
+         if did.crate == ast::local_crate {
+             ensure_iface_methods(tcx, did.node);
+         }
+         for vec::each(*ty::iface_methods(tcx, did)) {|if_m|
+            alt vec::find(my_methods,
+                          {|m| if_m.ident == m.mty.ident}) {
+              some({mty: m, id, span}) {
+               if m.purity != if_m.purity {
+                  tcx.sess.span_err(
+                     span, "method `" + m.ident + "`'s purity \
+                       not match the iface method's \
+                       purity");
+               }
+               let mt = compare_impl_method(
+                         tcx, span, m, vec::len(tps), if_m, tys,
+                         selfty);
+               let old = tcx.tcache.get(local_def(id));
+               if old.ty != mt {
+                  tcx.tcache.insert(local_def(id),
+                                    {bounds: old.bounds,
+                                     ty: mt});
+                  write_ty_to_tcx(tcx, id, mt);
+               }
+              }
+              none {
+                   tcx.sess.span_err(t.span, "missing method `" +
+                      if_m.ident + "`");
+              }
+            } // alt
+          } // |if_m|
+        } // for
+        _ {
+            tcx.sess.span_fatal(t.span, "can only implement \
+                                         interface types");
+        }
+     }
+    }
+
     fn convert_class_item(tcx: ty::ctxt, v: ast_util::ivar) {
         /* we want to do something here, b/c within the
          scope of the class, it's ok to refer to fields &
@@ -1141,56 +1200,15 @@ fn convert(tcx: ty::ctxt, it: @ast::item) {
             write_ty_to_tcx(tcx, it.id, selfty);
             tcx.tcache.insert(local_def(it.id), {bounds: i_bounds,
                                                  ty: selfty});
-            let my_methods = convert_methods(tcx, ms, i_bounds, some(selfty));
             alt ifce {
               some(t) {
-                let iface_ty = ast_ty_to_ty(tcx, m_collect, t);
-                alt ty::get(iface_ty).struct {
-                  ty::ty_iface(did, tys) {
-                    // Store the iface type in the type node
-                    alt check t.node {
-                      ast::ty_path(_, t_id) {
-                        write_ty_to_tcx(tcx, t_id, iface_ty);
-                      }
-                    }
-                    if did.crate == ast::local_crate {
-                        ensure_iface_methods(tcx, did.node);
-                    }
-                    for vec::each(*ty::iface_methods(tcx, did)) {|if_m|
-                        alt vec::find(my_methods,
-                                      {|m| if_m.ident == m.mty.ident}) {
-                          some({mty: m, id, span}) {
-                            if m.purity != if_m.purity {
-                                tcx.sess.span_err(
-                                    span, "method `" + m.ident + "`'s purity \
-                                           not match the iface method's \
-                                           purity");
-                            }
-                            let mt = compare_impl_method(
-                                tcx, span, m, vec::len(tps), if_m, tys,
-                                selfty);
-                            let old = tcx.tcache.get(local_def(id));
-                            if old.ty != mt {
-                                tcx.tcache.insert(local_def(id),
-                                                     {bounds: old.bounds,
-                                                     ty: mt});
-                                write_ty_to_tcx(tcx, id, mt);
-                            }
-                          }
-                          none {
-                            tcx.sess.span_err(t.span, "missing method `" +
-                                                 if_m.ident + "`");
-                          }
-                        }
-                    }
-                  }
-                  _ {
-                    tcx.sess.span_fatal(t.span, "can only implement \
-                                                    interface types");
-                  }
-                }
+                  check_methods_against_iface(tcx, tps, selfty,
+                                                    t, ms); }
+              _ {
+                  // Still have to do this to write method types
+                  // into the table
+                convert_methods(tcx, ms, i_bounds, some(selfty));
               }
-              _ {}
             }
           }
           ast::item_res(decl, tps, _, dtor_id, ctor_id) {
@@ -1221,7 +1239,7 @@ fn convert(tcx: ty::ctxt, it: @ast::item) {
             write_ty_to_tcx(tcx, it.id, tpt.ty);
             ensure_iface_methods(tcx, it.id);
           }
-          ast::item_class(tps, members, ctor) {
+          ast::item_class(tps, ifaces, members, ctor) {
               // Write the class type
               let tpt = ty_of_item(tcx, m_collect, it);
               write_ty_to_tcx(tcx, it.id, tpt.ty);
@@ -1245,6 +1263,31 @@ fn convert(tcx: ty::ctxt, it: @ast::item) {
               // Need to convert all methods so we can check internal
               // references to private methods
               convert_methods(tcx, methods, @[], some(selfty));
+              /*
+                Finally, check that the class really implements the ifaces
+                that it claims to implement.
+               */
+              for ifaces.each {|ifce|
+                alt lookup_def_tcx(tcx, it.span, ifce.id) {
+                   ast::def_ty(t_id) {
+                     let t = ty::lookup_item_type(tcx, t_id).ty;
+                     alt ty::get(t).struct {
+                        ty::ty_iface(_,_) {
+                            write_ty_to_tcx(tcx, ifce.id, t);
+                            check_methods_against_iface(tcx, tps, selfty,
+                               @{id: ifce.id,
+                                 node: ast::ty_path(ifce.path, ifce.id),
+                                 span: ifce.path.span},
+                               methods);
+                        }
+                        _ { tcx.sess.span_fatal(ifce.path.span,
+                           "can only implement interface types"); }
+                     }
+                   }
+                   _ { tcx.sess.span_err(ifce.path.span, "not an interface \
+                           type"); }
+                };
+              }
           }
           _ {
             // This call populates the type cache with the converted type
@@ -3978,9 +4021,9 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
         let self_ty = replace_self_region(ccx.tcx, self_region, self_ty);
         for ms.each {|m| check_method(ccx, m, self_ty);}
       }
-      ast::item_class(tps, members, ctor) {
-          let cid = some(it.id);
-          let class_t = ty::node_id_to_type(ccx.tcx, it.id);
+      ast::item_class(tps, ifaces, members, ctor) {
+          let cid = some(it.id), tcx = ccx.tcx;
+          let class_t = ty::node_id_to_type(tcx, it.id);
           let members_info = class_types(ccx, members);
           // can also ditch the enclosing_class stuff once we move to self
           // FIXME
diff --git a/src/test/compile-fail/class-implements-bad-iface.rs b/src/test/compile-fail/class-implements-bad-iface.rs
new file mode 100644 (file)
index 0000000..ff7d9fb
--- /dev/null
@@ -0,0 +1,9 @@
+// error-pattern:unresolved typename: nonexistent
+class cat implements nonexistent {
+  let meows: uint;
+  new(in_x : uint) { self.meows = in_x; }
+}
+
+fn main() {
+  let nyan = cat(0u);
+}
\ No newline at end of file
diff --git a/src/test/compile-fail/class-implements-int.rs b/src/test/compile-fail/class-implements-int.rs
new file mode 100644 (file)
index 0000000..b4111ef
--- /dev/null
@@ -0,0 +1,9 @@
+// error-pattern:not an interface type
+class cat implements int {
+  let meows: uint;
+  new(in_x : uint) { self.meows = in_x; }
+}
+
+fn main() {
+  let nyan = cat(0u);
+}
\ No newline at end of file
diff --git a/src/test/compile-fail/class-method-missing.rs b/src/test/compile-fail/class-method-missing.rs
new file mode 100644 (file)
index 0000000..6e6092f
--- /dev/null
@@ -0,0 +1,13 @@
+// error-pattern:missing method `eat`
+iface animal {
+  fn eat();
+}
+
+class cat implements animal {
+  let meows: uint;
+  new(in_x : uint) { self.meows = in_x; }
+}
+
+fn main() {
+  let nyan = cat(0u);
+}
\ No newline at end of file
diff --git a/src/test/run-pass/class-implement-ifaces.rs b/src/test/run-pass/class-implement-ifaces.rs
new file mode 100644 (file)
index 0000000..6e09d04
--- /dev/null
@@ -0,0 +1,44 @@
+iface noisy {
+  fn speak();
+}
+
+class cat implements noisy {
+  priv {
+    let mut meows : uint;
+    fn meow() {
+      #error("Meow");
+      self.meows += 1u;
+      if self.meows % 5u == 0u {
+          self.how_hungry += 1;
+      }
+    }
+  }
+
+  let mutable how_hungry : int;
+  let name : str;
+
+  new(in_x : uint, in_y : int, in_name: str)
+    { self.meows = in_x; self.how_hungry = in_y; self.name = in_name; }
+
+  fn speak() { self.meow(); }
+
+  fn eat() -> bool {
+    if self.how_hungry > 0 {
+        #error("OM NOM NOM");
+        self.how_hungry -= 2;
+        ret true;
+    }
+    else {
+        #error("Not hungry!");
+        ret false;
+    }
+  }
+}
+
+fn main() {
+  let nyan = cat(0u, 2, "nyan");
+  nyan.eat();
+  assert(!nyan.eat());
+  uint::range(1u, 10u, {|_i| nyan.speak(); });
+  assert(nyan.eat());
+}
\ No newline at end of file