1
Fork 0

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).
This commit is contained in:
Tim Chevalier 2012-03-28 14:17:41 -07:00
parent c141e7a068
commit ca6636d6b6
7 changed files with 82 additions and 33 deletions

View file

@ -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; }

View file

@ -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 }

View file

@ -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); }
}
}

View file

@ -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 */ }
}

View file

@ -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};

View file

@ -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); }

View file

@ -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() {