about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTim Chevalier <chevalier@alum.wellesley.edu>2012-02-20 15:42:21 -0800
committerTim Chevalier <chevalier@alum.wellesley.edu>2012-02-20 17:16:52 -0800
commit2299d204e40e565396daabc7b8d5141cdef52c8b (patch)
treed44318a7c1a3ca7d7d6e93a020626538c0a4b0e0
parent5837e1e809af6d783984d94ca27fe488823ecfe6 (diff)
downloadrust-2299d204e40e565396daabc7b8d5141cdef52c8b.tar.gz
rust-2299d204e40e565396daabc7b8d5141cdef52c8b.zip
Further work on resolving and typechecking classes
Class tests aren't working yet, but they fail a little later :-)

Also, make the parser correctly set a constructor's result type to
its enclosing class type.
-rw-r--r--src/comp/middle/resolve.rs69
-rw-r--r--src/comp/middle/ty.rs19
-rw-r--r--src/comp/middle/typeck.rs120
-rw-r--r--src/comp/syntax/parse/parser.rs11
-rw-r--r--src/comp/util/ppaux.rs2
-rw-r--r--src/test/run-pass/classes-simple.rs7
6 files changed, 200 insertions, 28 deletions
diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs
index 0b547bc0a04..951fe54e093 100644
--- a/src/comp/middle/resolve.rs
+++ b/src/comp/middle/resolve.rs
@@ -151,7 +151,7 @@ enum dir { inside, outside, }
 // when looking up a variable name that's not yet in scope to check
 // if it's already bound to a enum.
 enum namespace { ns_val(ns_value_type), ns_type, ns_module, }
-enum ns_value_type { ns_a_enum, ns_any_value, }
+enum ns_value_type { ns_an_enum, ns_any_value, }
 
 fn resolve_crate(sess: session, amap: ast_map::map, crate: @ast::crate) ->
    {def_map: def_map, exp_map: exp_map, impl_map: impl_map} {
@@ -469,7 +469,7 @@ fn resolve_names(e: @env, c: @ast::crate) {
            variable a refers to a nullary enum. */
           ast::pat_ident(p, none) {
               alt lookup_in_scope(*e, sc, p.span, path_to_ident(p),
-                                    ns_val(ns_a_enum)) {
+                                    ns_val(ns_an_enum)) {
                 some(fnd@ast::def_variant(_,_)) {
                     e.def_map.insert(pat.id, fnd);
                 }
@@ -519,11 +519,16 @@ fn visit_item_with_scope(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) {
       }
       ast::item_class(tps, members, ctor_id, ctor_decl, ctor_block) {
         visit::visit_ty_params(tps, sc, v);
-        let ctor_scope = cons(scope_fn_expr(ctor_decl, ctor_id, tps), @sc);
+        let class_scope = cons(scope_item(i), @sc);
+        /* visit the constructor... */
+        visit_fn_with_scope(e, visit::fk_item_fn(i.ident, tps), ctor_decl,
+                            ctor_block, ctor_block.span, ctor_id,
+                            class_scope, v);
+        /* visit the items */
         for cm in members {
             alt cm.node.decl {
-              class_method(i) { visit_item_with_scope(e, i, ctor_scope, v); }
-              _ { } // instance var -- nothing to do
+              class_method(i) { visit_item_with_scope(e, i, class_scope, v); }
+              instance_var(_,t,_,_) { v.visit_ty(t, class_scope, v); }
             }
         }
       }
@@ -622,10 +627,10 @@ fn visit_local_with_scope(e: @env, loc: @local, sc:scopes, v:vt<scopes>) {
     // to enum foo, or is it binding a new name foo?)
     alt loc.node.pat.node {
       pat_ident(an_ident,_) {
-          // Be sure to pass ns_a_enum to lookup_in_scope so that
+          // Be sure to pass ns_an_enum to lookup_in_scope so that
           // if this is a name that's being shadowed, we don't die
           alt lookup_in_scope(*e, sc, loc.span,
-                 path_to_ident(an_ident), ns_val(ns_a_enum)) {
+                 path_to_ident(an_ident), ns_val(ns_an_enum)) {
               some(ast::def_variant(enum_id,variant_id)) {
                   // Declaration shadows a enum that's in scope.
                   // That's an error.
@@ -804,7 +809,7 @@ fn ns_name(ns: namespace) -> str {
       ns_val(v) {
           alt (v) {
               ns_any_value { "name" }
-              ns_a_enum    { "enum" }
+              ns_an_enum    { "enum" }
           }
       }
       ns_module { "modulename" }
@@ -1000,6 +1005,21 @@ 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_class(tps, members, ctor_id, _, _) {
+                  if ns == ns_type {
+                    ret lookup_in_ty_params(e, name, tps);
+                  }
+                  if ns == ns_val(ns_any_value) && name == it.ident {
+                      ret some(ast::def_fn(local_def(ctor_id),
+                                           ast::impure_fn));
+                  }
+                  if ns == ns_val(ns_any_value) {
+                          ret lookup_in_class(local_def(it.id),
+                                              members, name);
+                  }
+                  // FIXME: AST allows other items to appear in a class,
+                  // but that might not be wise
+              }
               _ { }
             }
           }
@@ -1071,7 +1091,7 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace)
                             /* If we were looking for a enum, at this point
                                we know it's bound to a non-enum value, and
                                we can return none instead of failing */
-                            ns_a_enum { ret none; }
+                            ns_an_enum { ret none; }
                             _ { "attempted dynamic environment-capture" }
                           }
                       }
@@ -1146,6 +1166,29 @@ fn lookup_in_fn(e: env, name: ident, decl: ast::fn_decl,
     }
 }
 
+/* 
+   FIXME: not sure about this code. maybe this should be handled
+   using the mod_index stuff
+ */
+fn lookup_in_class(parent_id: def_id,
+                   members: [@class_item], name: ident)
+   -> option<def> {
+    for m in members {
+      alt m.node.decl {
+        instance_var(v_name,_,_,id) {
+            if v_name == name {
+              ret some(def_class_field(parent_id, local_def(id)));
+            }
+        }
+        class_method(i) {
+            if i.ident == name {
+              ret some(def_class_method(parent_id, local_def(i.id)));
+            }
+        }
+      }
+    }
+    ret none;
+}
 
 fn lookup_in_block(e: env, name: ident, sp: span, b: ast::blk_, pos: uint,
                    loc_pos: uint, ns: namespace) -> option<def> {
@@ -1430,7 +1473,7 @@ fn lookup_in_globs(e: env, globs: [glob_imp_def], sp: span, id: ident,
     if vec::len(matches) == 0u {
         ret none;
         }
-    else if vec::len(matches) == 1u || ns == ns_val(ns_a_enum) {
+    else if vec::len(matches) == 1u || ns == ns_val(ns_an_enum) {
         ret some(matches[0].def);
     } else {
         for match: glob_imp_def in matches {
@@ -1449,7 +1492,7 @@ fn lookup_glob_in_mod(e: env, info: @indexed_mod, sp: span, id: ident,
     if !info.glob_imported_names.contains_key(id) {
         info.glob_imported_names.insert(id, glob_resolving(sp));
         // kludge
-        let val_ns = if wanted_ns == ns_val(ns_a_enum) { ns_val(ns_a_enum) }
+        let val_ns = if wanted_ns == ns_val(ns_an_enum) { ns_val(ns_an_enum) }
                      else { ns_val(ns_any_value) };
         let globs = info.glob_imports;
         let val = lookup_in_globs(e, globs, sp, id, val_ns, dr);
@@ -1615,7 +1658,7 @@ fn index_nmod(md: ast::native_mod) -> mod_index {
 // External lookups
 fn ns_for_def(d: def) -> namespace {
     alt d {
-      ast::def_variant(_, _) { ns_val(ns_a_enum) }
+      ast::def_variant(_, _) { ns_val(ns_an_enum) }
       ast::def_fn(_, _) | ast::def_self(_) |
       ast::def_const(_) | ast::def_arg(_, _) | ast::def_local(_) |
       ast::def_upvar(_, _, _) |  ast::def_self(_) |
@@ -1632,7 +1675,7 @@ fn ns_for_def(d: def) -> namespace {
 // a enum
 fn ns_ok(wanted:namespace, actual:namespace) -> bool {
     alt actual {
-      ns_val(ns_a_enum) {
+      ns_val(ns_an_enum) {
         alt wanted {
           ns_val(_) { true }
           _ { false }
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index 06823f4aa30..3e9822ab4fd 100644
--- a/src/comp/middle/ty.rs
+++ b/src/comp/middle/ty.rs
@@ -2183,6 +2183,21 @@ mod unify {
               }
             }
           }
+          ty_class(expected_class, expected_tys) {
+              alt get(actual).struct {
+                ty_class(actual_class, actual_tys) {
+                    if expected_class != actual_class {
+                        ret ures_err(terr_mismatch);
+                    }
+                    ret unify_tps(cx, expected_tys, actual_tys, variance,
+                           {|tps|
+                            ures_ok(mk_class(cx.tcx, expected_class, tps))});
+                }
+                _ {
+                    ret ures_err(terr_mismatch);
+                }
+              }
+          }
           _ { cx.tcx.sess.bug("unify: unexpected type"); }
         }
     }
@@ -2478,13 +2493,11 @@ fn enum_variant_with_id(cx: ctxt, enum_id: ast::def_id,
 
 // If the given item is in an external crate, looks up its type and adds it to
 // the type cache. Returns the type parameters and type.
+// a precondition (did.crate != ast::local_crate) would be nice
 fn lookup_item_type(cx: ctxt, did: ast::def_id) -> ty_param_bounds_and_ty {
     alt cx.tcache.find(did) {
       some(tpt) { ret tpt; }
       none {
-          /* where do things get added to the cache?
-             Have to add class members */
-
         // The item is in this crate. The caller should have added it to the
         // type cache already
         assert did.crate != ast::local_crate;
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index a54a376be2d..69db4b8bca6 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -48,8 +48,17 @@ type crate_ctxt = {mutable self_infos: [self_info],
                    impl_map: resolve::impl_map,
                    method_map: method_map,
                    dict_map: dict_map,
+                   // Not at all sure it's right to put these here
+                   /* node_id for the class this fn is in --
+                      none if it's not in a class */
+                   enclosing_class_id: option<ast::node_id>,
+                   /* map from node_ids for enclosing-class
+                      vars and methods to types */
+                   enclosing_class: class_map,
                    tcx: ty::ctxt};
 
+type class_map = hashmap<ast::node_id, ty::t>;
+
 type fn_ctxt =
     // var_bindings, locals and next_var_id are shared
     // with any nested functions that capture the environment
@@ -121,7 +130,6 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
       }
       ast::def_fn(id, _) | ast::def_const(id) |
       ast::def_variant(_, id) | ast::def_class(id)
-          | ast::def_class_method(_, id) | ast::def_class_field(_, id)
          { ret ty::lookup_item_type(fcx.ccx.tcx, id); }
       ast::def_binding(id) {
         assert (fcx.locals.contains_key(id.node));
@@ -134,6 +142,20 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
       ast::def_upvar(_, inner, _) {
         ret ty_param_bounds_and_ty_for_def(fcx, sp, *inner);
       }
+      ast::def_class_method(_, id) | ast::def_class_field(_, id) {
+          if id.crate != ast::local_crate {
+                  fcx.ccx.tcx.sess.span_fatal(sp,
+                                 "class method or field referred to \
+                                  out of scope");
+          }
+          alt fcx.ccx.enclosing_class.find(id.node) {
+             some(a_ty) { ret {bounds: @[], ty: a_ty}; }
+             _ { fcx.ccx.tcx.sess.span_fatal(sp,
+                                 "class method or field referred to \
+                                  out of scope"); }
+          }
+      }
+
       _ {
         // FIXME: handle other names.
         fcx.ccx.tcx.sess.unimpl("definition variant");
@@ -316,7 +338,11 @@ fn ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t {
         ty::mk_fn(tcx, ty_of_fn_decl(tcx, mode, proto, decl))
       }
       ast::ty_path(path, id) {
-        alt tcx.def_map.get(id) {
+        let a_def = alt tcx.def_map.find(id) {
+          none { tcx.sess.span_fatal(ast_ty.span, #fmt("unbound path %s",
+                                                   path_to_str(path))); }
+          some(d) { d }};
+        alt a_def {
           ast::def_ty(id) {
             instantiate(tcx, ast_ty.span, mode, id, path.node.types)
           }
@@ -349,6 +375,23 @@ fn ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t {
               }
             }
           }
+          ast::def_class(class_id) {
+              alt tcx.items.find(class_id.node) {
+                 some(ast_map::node_item(
+                  @{node: ast::item_class(tps, _, _, _, _), _}, _)) {
+                     if vec::len(tps) != vec::len(path.node.types) {
+                        tcx.sess.span_err(ast_ty.span, "incorrect number of \
+                           type parameters to object type");
+                     }
+                     ty::mk_class(tcx, class_id, vec::map(path.node.types,
+                        {|ast_ty| ast_ty_to_ty(tcx, mode, ast_ty)}))
+                 }
+                 _ {
+                     tcx.sess.span_bug(ast_ty.span, "class id is unbound \
+                       in items");
+                 }
+              }
+          }
           _ {
             tcx.sess.span_fatal(ast_ty.span,
                                 "found type name used as a variable");
@@ -787,9 +830,20 @@ mod collect {
           }
         }
     }
-    fn convert_class_item(_cx: @ctxt, _parent_ty: ty::t,
-                          _ci: ast::class_member) {
-        /* TODO */
+    fn convert_class_item(cx: @ctxt, ci: ast::class_member) {
+        /* we want to do something here, b/c within the
+         scope of the class, it's ok to refer to fields &
+        methods unqualified */
+
+        /* they have these types *within the scope* of the
+         class. outside the class, it's done with expr_field */
+        alt ci {
+         ast::instance_var(_,t,_,id) {
+             let tt = ast_ty_to_ty(cx.tcx, m_collect, t);
+             write_ty(cx.tcx, id, tt);
+         }
+         ast::class_method(it) { convert(cx, it); }
+        }
     }
     fn convert(cx: @ctxt, it: @ast::item) {
         alt it.node {
@@ -890,16 +944,23 @@ mod collect {
             ensure_iface_methods(cx.tcx, it.id);
           }
           ast::item_class(tps, members, ctor_id, ctor_decl, ctor_block) {
-              let parent_ty = ty::lookup_item_type(cx.tcx, local_def(it.id));
+              // Write the class type
+              let {bounds,params} = mk_ty_params(cx.tcx, tps);
+              let class_ty = ty::mk_class(cx.tcx, local_def(it.id), params);
+              let tpt = {bounds: bounds, ty: class_ty};
+              cx.tcx.tcache.insert(local_def(it.id), tpt);
+              write_ty(cx.tcx, it.id, class_ty);
               // Write the ctor type
               let t_ctor = ty::mk_fn(cx.tcx,
                                      ty_of_fn_decl(cx.tcx, m_collect,
                                              ast::proto_any, ctor_decl));
               write_ty(cx.tcx, ctor_id, t_ctor);
+              cx.tcx.tcache.insert(local_def(ctor_id),
+                                   {bounds: bounds, ty: t_ctor});
               /* FIXME: check for proper public/privateness */
               // Write the type of each of the members
               for m in members {
-                 convert_class_item(cx, parent_ty.ty, m.node.decl);
+                 convert_class_item(cx, m.node.decl);
               }
           }
           _ {
@@ -2252,7 +2313,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
       }
       ast::expr_fn(proto, decl, body, captures) {
         check_expr_fn_with_unifier(fcx, expr, proto, decl, body,
-                                   unify, expected);
+                          unify, expected);
         capture::check_capture_clause(tcx, expr.id, proto, *captures);
       }
       ast::expr_fn_block(decl, body) {
@@ -2446,6 +2507,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
               _ {}
             }
           }
+          ty::ty_class(_id, _params) {
+              // TODO (classes)
+              tcx.sess.span_bug(expr.span,
+                  #fmt("can't check class field accesses yet: %s",
+                    ty_to_str(fcx.ccx.tcx, base_t)));
+          }
           _ {}
         }
         if !handled {
@@ -2874,6 +2941,30 @@ fn check_method(ccx: @crate_ctxt, method: @ast::method) {
     check_fn(ccx, ast::proto_bare, method.decl, method.body, method.id, none);
 }
 
+fn class_types(ccx: @crate_ctxt, members: [@ast::class_item]) -> class_map {
+    let rslt = new_int_hash::<ty::t>();
+    for m in members {
+      alt m.node.decl {
+         ast::instance_var(_,t,_,id) {
+           rslt.insert(id, ast_ty_to_ty(ccx.tcx, m_collect, t));
+         }
+         ast::class_method(it) {
+             rslt.insert(it.id, ty_of_item(ccx.tcx, m_collect, it).ty);
+         }
+      }
+    }
+    rslt
+}
+
+fn check_class_member(ccx: @crate_ctxt, cm: ast::class_member) {
+    alt cm {
+      ast::instance_var(_,t,_,_) { // ??? Not sure
+      }
+      // not right yet -- need a scope
+      ast::class_method(i) { check_item(ccx, i); }
+    }
+}
+
 fn check_item(ccx: @crate_ctxt, it: @ast::item) {
     alt it.node {
       ast::item_const(_, e) { check_const(ccx, it.span, e, it.id); }
@@ -2889,6 +2980,17 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
         for m in ms { check_method(ccx, m); }
         vec::pop(ccx.self_infos);
       }
+      ast::item_class(tps, members, ctor_id, ctor_decl, ctor_body) {
+          let cid = some(it.id);
+          let members_info = class_types(ccx, members);
+          let class_ccx = @{enclosing_class_id:cid,
+                            enclosing_class:members_info with *ccx};
+          // typecheck the ctor
+          check_fn(class_ccx, ast::proto_bare, ctor_decl, ctor_body, ctor_id,
+                   none);
+          // typecheck the members
+          for m in members { check_class_member(class_ccx, m.node.decl); }
+      }
       _ {/* nothing to do */ }
     }
 }
@@ -3149,6 +3251,8 @@ fn check_crate(tcx: ty::ctxt, impl_map: resolve::impl_map,
                 impl_map: impl_map,
                 method_map: std::map::new_int_hash(),
                 dict_map: std::map::new_int_hash(),
+                enclosing_class_id: none,
+                enclosing_class: std::map::new_int_hash(),
                 tcx: tcx};
     let visit = visit::mk_simple_visitor(@{
         visit_item: bind check_item(ccx, _)
diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs
index eb34fb4b89d..35d5119c4e6 100644
--- a/src/comp/syntax/parse/parser.rs
+++ b/src/comp/syntax/parse/parser.rs
@@ -1972,13 +1972,14 @@ fn parse_item_res(p: parser, attrs: [ast::attribute]) -> @ast::item {
 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 class_path = ident_to_path(p.last_span, class_name);
     let ty_params = parse_ty_params(p);
     expect(p, token::LBRACE);
     let items: [@ast::class_item] = [];
     let ctor_id = p.get_id();
     let the_ctor : option<(ast::fn_decl, ast::blk)> = none;
     while p.token != token::RBRACE {
-       alt parse_class_item(p) {
+        alt parse_class_item(p, class_path) {
             ctor_decl(a_fn_decl, blk) {
                 the_ctor = some((a_fn_decl, blk));
             }
@@ -2015,10 +2016,14 @@ enum class_contents { ctor_decl(ast::fn_decl, ast::blk),
                       // none of these are a ctor decl
                       priv_decls([ast::class_member])}
 
-fn parse_class_item(p:parser) -> class_contents {
+    fn parse_class_item(p:parser, class_name:@ast::path) -> class_contents {
     if eat_word(p, "new") {
         // Can ctors have attrs?
-        let decl = parse_fn_decl(p, ast::impure_fn);
+            // result type is always the type of the class
+        let decl_ = parse_fn_decl(p, ast::impure_fn);
+        let decl = {output: @{node: ast::ty_path(class_name, p.get_id()),
+                                  span: decl_.output.span}
+                    with decl_};
         let body = parse_block(p);
         ret ctor_decl(decl, body);
     }
diff --git a/src/comp/util/ppaux.rs b/src/comp/util/ppaux.rs
index 2d253a1c3a9..547c2d5ee82 100644
--- a/src/comp/util/ppaux.rs
+++ b/src/comp/util/ppaux.rs
@@ -117,7 +117,7 @@ fn ty_to_str(cx: ctxt, typ: t) -> str {
       ty_param(id, _) {
         "'" + str::from_bytes([('a' as u8) + (id as u8)])
       }
-      ty_enum(did, tps) | ty_res(did, _, tps) {
+      ty_enum(did, tps) | ty_res(did, _, tps) | ty_class(did, tps) {
         // Not sure why, but under some circumstances enum or resource types
         // do not have an associated id.  I didn't investigate enough to know
         // if there is a good reason for this. - Niko, 2012-02-10
diff --git a/src/test/run-pass/classes-simple.rs b/src/test/run-pass/classes-simple.rs
index 006c6392a6b..938afa6b2d3 100644
--- a/src/test/run-pass/classes-simple.rs
+++ b/src/test/run-pass/classes-simple.rs
@@ -7,4 +7,11 @@ class cat {
   let how_hungry : int;
 
   new(in_x : uint, in_y : int) { meows = in_x; how_hungry = in_y; }
+}
+
+fn main() {
+  let nyan : cat = cat(52u, 99);
+  let kitty = cat(1000u, 2); 
+  log(debug, nyan.how_hungry);
+  log(debug, kitty.how_hungry);
 }
\ No newline at end of file