about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTim Chevalier <chevalier@alum.wellesley.edu>2012-03-28 14:17:41 -0700
committerTim Chevalier <chevalier@alum.wellesley.edu>2012-03-28 14:19:00 -0700
commitca6636d6b689fe209a210b0eda51e368f01cdb0f (patch)
tree7c979884781d3179bb918045f414426eb6158cea
parentc141e7a068e3fbb1a5d24dcd000567b7731910bb (diff)
downloadrust-ca6636d6b689fe209a210b0eda51e368f01cdb0f.tar.gz
rust-ca6636d6b689fe209a210b0eda51e368f01cdb0f.zip
Allow references to "self" within classes
Allow writing self.f within a class that has a field f. Currently,
the compiler accepts either self.f or f. In a future commit I'll
require writing self.f and not f.

Not sure whether self.f() works if f is a method (making sure that
works next).
-rw-r--r--src/rustc/middle/alias.rs2
-rw-r--r--src/rustc/middle/mutbl.rs39
-rw-r--r--src/rustc/middle/resolve.rs14
-rw-r--r--src/rustc/middle/typeck.rs53
-rw-r--r--src/rustc/syntax/ast.rs1
-rw-r--r--src/rustc/syntax/parse/parser.rs4
-rw-r--r--src/test/run-pass/classes-simple.rs2
7 files changed, 82 insertions, 33 deletions
diff --git a/src/rustc/middle/alias.rs b/src/rustc/middle/alias.rs
index 3fe17f96bbd..5ad5d42896f 100644
--- a/src/rustc/middle/alias.rs
+++ b/src/rustc/middle/alias.rs
@@ -647,7 +647,7 @@ fn pattern_roots(tcx: ty::ctxt, mutbl: option<unsafe_ty>, pat: @ast::pat)
 // return-by-reference
 fn expr_root(cx: ctx, ex: @ast::expr, autoderef: bool)
     -> {ex: @ast::expr, mutbl: option<unsafe_ty>} {
-    let base_root = mutbl::expr_root(cx.tcx, ex, autoderef);
+    let base_root = mutbl::expr_root_(cx.tcx, none, ex, autoderef);
     let mut unsafe_ty = none;
     for d in *base_root.ds {
         if d.mutbl { unsafe_ty = some(contains(d.outer_t)); break; }
diff --git a/src/rustc/middle/mutbl.rs b/src/rustc/middle/mutbl.rs
index 8bebb3b5e68..be06a16a0dc 100644
--- a/src/rustc/middle/mutbl.rs
+++ b/src/rustc/middle/mutbl.rs
@@ -12,7 +12,13 @@ type deref = @{mutbl: bool, kind: deref_t, outer_t: ty::t};
 // vec of dereferences that were used on this root. Note that, in this vec,
 // the inner derefs come in front, so foo.bar[1] becomes rec(ex=foo,
 // ds=[index,field])
-fn expr_root(tcx: ty::ctxt, ex: @expr, autoderef: bool) ->
+fn expr_root(cx: @ctx, ex: @expr, autoderef: bool)
+    -> {ex: @expr, ds: @[deref]} {
+    expr_root_(cx.tcx, cx.in_ctor, ex, autoderef)
+}
+
+fn expr_root_(tcx: ty::ctxt, ctor_self: option<node_id>,
+             ex: @expr, autoderef: bool) ->
    {ex: @expr, ds: @[deref]} {
     fn maybe_auto_unbox(tcx: ty::ctxt, t: ty::t) -> {t: ty::t, ds: [deref]} {
         let mut ds = [], t = t;
@@ -58,13 +64,23 @@ fn expr_root(tcx: ty::ctxt, ex: @expr, autoderef: bool) ->
                 }
               }
               ty::ty_class(did, _) {
-                  util::common::log_expr(*ex);
+                  util::common::log_expr(*base);
+                  let in_self = alt ctor_self {
+                          some(selfid) {
+                              alt tcx.def_map.find(base.id) {
+                                 some(def_self(slfid)) { slfid == selfid }
+                                 _ { false }
+                              }
+                          }
+                          none { false }
+                  };
                   for fld: ty::field_ty in ty::lookup_class_fields(tcx, did) {
-                    #debug("%s %?", fld.ident, fld.mutability);
                     if str::eq(ident, fld.ident) {
-                        is_mutbl = fld.mutability == class_mutable;
+                        is_mutbl = fld.mutability == class_mutable
+                            || in_self; // all fields can be mutated
+                                        // in the ctor
+                        break;
                     }
-                    break;
                   }
               }
               _ {}
@@ -126,10 +142,11 @@ fn expr_root(tcx: ty::ctxt, ex: @expr, autoderef: bool) ->
 type mutbl_map = std::map::hashmap<node_id, ()>;
 // Keep track of whether we're inside a ctor, so as to
 // allow mutating immutable fields in the same class
-type ctx = {tcx: ty::ctxt, mutbl_map: mutbl_map, in_ctor: bool};
+// if we are in a ctor, we track the self id
+type ctx = {tcx: ty::ctxt, mutbl_map: mutbl_map, in_ctor: option<node_id>};
 
 fn check_crate(tcx: ty::ctxt, crate: @crate) -> mutbl_map {
-    let cx = @{tcx: tcx, mutbl_map: std::map::int_hash(), in_ctor: false};
+    let cx = @{tcx: tcx, mutbl_map: std::map::int_hash(), in_ctor: none};
     let v = @{visit_expr: visit_expr,
               visit_decl: visit_decl,
               visit_item: visit_item
@@ -204,7 +221,7 @@ fn visit_item(item: @item, &&cx: @ctx, v: visit::vt<@ctx>) {
                             i.node.privacy, i.node.decl, cx, v); });
                 v.visit_fn(visit::fk_ctor(item.ident, tps), ctor.node.dec,
                            ctor.node.body, ctor.span, ctor.node.id,
-                           @{in_ctor: true with *cx}, v);
+                           @{in_ctor: some(ctor.node.self_id) with *cx}, v);
             }
             _ { visit::visit_item(item, cx, v); }
     }
@@ -221,7 +238,7 @@ fn check_lval(cx: @ctx, dest: @expr, msg: msg) {
         cx.mutbl_map.insert(ast_util::def_id_of_def(def).node, ());
       }
       _ {
-        let root = expr_root(cx.tcx, dest, false);
+        let root = expr_root(cx, dest, false);
         if vec::len(*root.ds) == 0u {
             if msg != msg_move_out {
                 mk_err(cx, dest.span, msg, "non-lvalue");
@@ -251,7 +268,7 @@ fn check_move_rhs(cx: @ctx, src: @expr) {
         check_lval(cx, src, msg_move_out);
       }
       _ {
-        let root = expr_root(cx.tcx, src, false);
+        let root = expr_root(cx, src, false);
 
         // Not a path and no-derefs means this is a temporary.
         if vec::len(*root.ds) != 0u &&
@@ -339,7 +356,7 @@ fn is_illegal_to_modify_def(cx: @ctx, def: def, msg: msg) -> option<str> {
 
       def_binding(_) { some("binding") }
       def_class_field(parent,fld) {
-          if !cx.in_ctor {
+          if option::is_none(cx.in_ctor) {
              /* Enforce mutability *unless* we're inside a ctor */
              alt ty::lookup_class_field(cx.tcx, parent, fld).mutability {
                class_mutable { none }
diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs
index 712ccbf02af..91aac1c7d6a 100644
--- a/src/rustc/middle/resolve.rs
+++ b/src/rustc/middle/resolve.rs
@@ -523,17 +523,23 @@ fn visit_item_with_scope(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) {
       }
       ast::item_class(tps, 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);
         /* visit the constructor... */
+        let ctor_scope = cons(scope_method(ctor.node.self_id, tps),
+                              @class_scope);
         visit_fn_with_scope(e, visit::fk_item_fn(i.ident, tps), ctor.node.dec,
                             ctor.node.body, ctor.span, ctor.node.id,
-                            class_scope, v);
+                            ctor_scope, v);
         /* visit the items */
         for cm in members {
             alt cm.node.decl {
-              class_method(m) { visit_fn_with_scope(e,
-                 visit::fk_item_fn(m.ident, tps), m.decl, m.body,
-                 m.span, m.id, class_scope, v); }
+              class_method(m) {
+                  let msc = cons(scope_method(m.self_id, tps + m.tps),
+                                 @class_scope);
+                  visit_fn_with_scope(e,
+                     visit::fk_item_fn(m.ident, tps), m.decl, m.body,
+                                 m.span, m.id, msc, v); }
               instance_var(_,t,_,_) { v.visit_ty(t, class_scope, v); }
             }
         }
diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs
index 721b846e052..63a30f02434 100644
--- a/src/rustc/middle/typeck.rs
+++ b/src/rustc/middle/typeck.rs
@@ -49,7 +49,9 @@ type vtable_map = hashmap<ast::node_id, vtable_res>;
 type ty_table = hashmap<ast::def_id, ty::t>;
 
 // Used for typechecking the methods of an impl
-enum self_info { self_impl(ty::t) }
+// first field is the self type, second is the ID for the "self" object
+// that's currently in scope
+enum self_info { self_impl(ty::t, ast::node_id) }
 
 type crate_ctxt = {mut self_infos: [self_info],
                    impl_map: resolve::impl_map,
@@ -117,7 +119,7 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
       }
       ast::def_self(_) {
         alt get_self_info(fcx.ccx) {
-          some(self_impl(impl_t)) {
+          some(self_impl(impl_t,_)) {
             ret {bounds: @[], ty: impl_t};
           }
           none {
@@ -3128,20 +3130,28 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
               // field
               #debug("class named %s", ty_to_str(tcx, base_t));
               /*
-                This is an external reference, so only consider public
-                fields
+                check whether this is a self-reference or not, which
+                determines whether we look at all fields or only public
+                ones
                */
-              let cls_items = lookup_public_fields(tcx, base_id);
-              #debug("cls_items: %?", cls_items);
-              alt lookup_field_ty(tcx, base_id, cls_items, field) {
+              let node_def = lookup_def(fcx, base.span, base.id);
+              let cls_items = alt get_self_info(fcx.ccx) {
+                      some(self_impl(_, n_id)) if alt node_def {
+                          ast::def_self(base_id) { base_id == n_id }
+                          _ { false }} {
+                        // base expr is "self" -- consider all fields
+                        ty::lookup_class_fields(tcx, base_id)
+                      }
+                      _ { lookup_public_fields(tcx, base_id) }
+              };
+               alt lookup_field_ty(tcx, base_id, cls_items, field) {
                  some(field_ty) {
-                     #debug("a");
                     // (2) look up what field's type is, and return it
                     // FIXME: actually instantiate any type params
                      write_ty(tcx, id, field_ty);
                      handled = true;
                  }
-                 none { #debug("b"); }
+                 none {}
               }
           }
           _ {}
@@ -3659,11 +3669,16 @@ fn class_types(ccx: @crate_ctxt, members: [@ast::class_item]) -> class_map {
     rslt
 }
 
-fn check_class_member(ccx: @crate_ctxt, cm: ast::class_member) {
+fn check_class_member(ccx: @crate_ctxt, class_t: ty::t,
+                      cm: ast::class_member) {
     alt cm {
       ast::instance_var(_,t,_,_) {
       }
-      ast::class_method(m) { check_method(ccx, m); }
+      ast::class_method(m) {
+          ccx.self_infos += [self_impl(class_t, m.self_id)];
+          check_method(ccx, m);
+          vec::pop(ccx.self_infos);
+      }
     }
 }
 
@@ -3681,20 +3696,28 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
         let mut self_ty = ast_ty_to_ty(ccx.tcx, m_check, ty);
         let self_region = ty::re_self;
         self_ty = instantiate_self_regions(ccx.tcx, self_region, self_ty);
-        ccx.self_infos += [self_impl(self_ty)];
-        for m in ms { check_method(ccx, m); }
-        vec::pop(ccx.self_infos);
+        for m in ms {
+             ccx.self_infos += [self_impl(self_ty, m.id)];
+             check_method(ccx, m);
+             vec::pop(ccx.self_infos);
+        }
       }
       ast::item_class(tps, members, ctor) {
           let cid = some(it.id);
+          let class_t = node_id_to_type(ccx.tcx, it.id);
           let members_info = class_types(ccx, members);
+          // can also ditch the enclosing_class stuff once we move to self
+          // FIXME
           let class_ccx = @{enclosing_class_id:cid,
                             enclosing_class:members_info with *ccx};
+          class_ccx.self_infos += [self_impl(class_t, ctor.node.self_id)];
           // typecheck the ctor
           check_fn(class_ccx, ast::proto_bare, ctor.node.dec,
                    ctor.node.body, ctor.node.id, false, none);
+          vec::pop(class_ccx.self_infos);
           // typecheck the members
-          for m in members { check_class_member(class_ccx, m.node.decl); }
+          for m in members { check_class_member(class_ccx, class_t,
+                                                m.node.decl); }
       }
       _ {/* nothing to do */ }
     }
diff --git a/src/rustc/syntax/ast.rs b/src/rustc/syntax/ast.rs
index 95c5b607cd4..447d754895e 100644
--- a/src/rustc/syntax/ast.rs
+++ b/src/rustc/syntax/ast.rs
@@ -684,6 +684,7 @@ type class_ctor = spanned<class_ctor_>;
 
 #[auto_serialize]
 type class_ctor_ = {id: node_id,
+                    self_id: node_id,
                     dec: fn_decl,
                     body: blk};
 
diff --git a/src/rustc/syntax/parse/parser.rs b/src/rustc/syntax/parse/parser.rs
index 3ca2e6a588b..61cc675c492 100644
--- a/src/rustc/syntax/parse/parser.rs
+++ b/src/rustc/syntax/parse/parser.rs
@@ -2092,10 +2092,12 @@ 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,
+      some((ct_d, ct_b, ct_s)) {
+          ret mk_item(p, lo, p.last_span.hi,
                                              class_name,
          ast::item_class(ty_params, items,
                          {node: {id: ctor_id,
+                                 self_id: p.get_id(),
                                  dec: ct_d,
                                  body: ct_b},
                           span: ct_s}), attrs); }
diff --git a/src/test/run-pass/classes-simple.rs b/src/test/run-pass/classes-simple.rs
index bb0384f902e..ae37fb9a997 100644
--- a/src/test/run-pass/classes-simple.rs
+++ b/src/test/run-pass/classes-simple.rs
@@ -5,7 +5,7 @@ class cat {
 
   let how_hungry : int;
 
-  new(in_x : uint, in_y : int) { meows = in_x; how_hungry = in_y; }
+  new(in_x : uint, in_y : int) { self.meows = in_x; self.how_hungry = in_y; }
 }
 
 fn main() {