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:
parent
c141e7a068
commit
ca6636d6b6
7 changed files with 82 additions and 33 deletions
|
@ -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; }
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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); }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */ }
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue