Enforce mutability declarations in classes; correct shapes for classes
1. Enforce mutability declarations on class fields. Don't allow any mutation of class fields not declared as mutable (except inside the constructor). 2. Handle classes correctly in shape (treat classes like records).
This commit is contained in:
parent
c9102ee7a1
commit
edb747ceed
15 changed files with 187 additions and 40 deletions
|
@ -78,6 +78,7 @@ const tag_path_len: uint = 0x41u;
|
||||||
const tag_path_elt_mod: uint = 0x42u;
|
const tag_path_elt_mod: uint = 0x42u;
|
||||||
const tag_path_elt_name: uint = 0x43u;
|
const tag_path_elt_name: uint = 0x43u;
|
||||||
const tag_item_field: uint = 0x44u;
|
const tag_item_field: uint = 0x44u;
|
||||||
|
const tag_class_mut: uint = 0x45u;
|
||||||
|
|
||||||
// used to encode crate_ctxt side tables
|
// used to encode crate_ctxt side tables
|
||||||
enum astencode_tag { // Reserves 0x50 -- 0x6f
|
enum astencode_tag { // Reserves 0x50 -- 0x6f
|
||||||
|
|
|
@ -118,6 +118,18 @@ fn class_member_id(d: ebml::doc, cdata: cmd) -> ast::def_id {
|
||||||
ret translate_def_id(cdata, parse_def_id(ebml::doc_data(tagdoc)));
|
ret translate_def_id(cdata, parse_def_id(ebml::doc_data(tagdoc)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn field_mutability(d: ebml::doc) -> ast::class_mutability {
|
||||||
|
// Use maybe_get_doc in case it's a method
|
||||||
|
option::maybe(ebml::maybe_get_doc(d, tag_class_mut),
|
||||||
|
ast::class_immutable,
|
||||||
|
{|d|
|
||||||
|
alt ebml::doc_as_u8(d) as char {
|
||||||
|
'm' { ast::class_mutable }
|
||||||
|
_ { ast::class_immutable }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn variant_disr_val(d: ebml::doc) -> option<int> {
|
fn variant_disr_val(d: ebml::doc) -> option<int> {
|
||||||
option::chain(ebml::maybe_get_doc(d, tag_disr_val)) {|val_doc|
|
option::chain(ebml::maybe_get_doc(d, tag_disr_val)) {|val_doc|
|
||||||
int::parse_buf(ebml::doc_data(val_doc), 10u)
|
int::parse_buf(ebml::doc_data(val_doc), 10u)
|
||||||
|
@ -435,9 +447,9 @@ fn get_class_members(cdata: cmd, id: ast::node_id,
|
||||||
if p(f) {
|
if p(f) {
|
||||||
let name = item_name(an_item);
|
let name = item_name(an_item);
|
||||||
let did = class_member_id(an_item, cdata);
|
let did = class_member_id(an_item, cdata);
|
||||||
|
let mt = field_mutability(an_item);
|
||||||
result += [{ident: name, id: did, privacy:
|
result += [{ident: name, id: did, privacy:
|
||||||
// This won't work for methods, argh
|
family_to_privacy(f), mutability: mt}];
|
||||||
family_to_privacy(f)}];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
|
|
|
@ -47,6 +47,13 @@ fn encode_named_def_id(ebml_w: ebml::writer, name: str, id: def_id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn encode_mutability(ebml_w: ebml::writer, mt: class_mutability) {
|
||||||
|
ebml_w.wr_tag(tag_class_mut) {||
|
||||||
|
ebml_w.writer.write([alt mt { class_immutable { 'i' }
|
||||||
|
class_mutable { 'm' } } as u8]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type entry<T> = {val: T, pos: uint};
|
type entry<T> = {val: T, pos: uint};
|
||||||
|
|
||||||
fn encode_enum_variant_paths(ebml_w: ebml::writer, variants: [variant],
|
fn encode_enum_variant_paths(ebml_w: ebml::writer, variants: [variant],
|
||||||
|
@ -370,7 +377,7 @@ fn encode_info_for_class(ecx: @encode_ctxt, ebml_w: ebml::writer,
|
||||||
/* We encode both private and public fields -- need to include
|
/* We encode both private and public fields -- need to include
|
||||||
private fields to get the offsets right */
|
private fields to get the offsets right */
|
||||||
alt ci.node.decl {
|
alt ci.node.decl {
|
||||||
instance_var(nm, _, _, id) {
|
instance_var(nm, _, mt, id) {
|
||||||
*index += [{val: id, pos: ebml_w.writer.tell()}];
|
*index += [{val: id, pos: ebml_w.writer.tell()}];
|
||||||
ebml_w.start_tag(tag_items_data_item);
|
ebml_w.start_tag(tag_items_data_item);
|
||||||
#debug("encode_info_for_class: doing %s %d", nm, id);
|
#debug("encode_info_for_class: doing %s %d", nm, id);
|
||||||
|
@ -378,7 +385,7 @@ fn encode_info_for_class(ecx: @encode_ctxt, ebml_w: ebml::writer,
|
||||||
encode_name(ebml_w, nm);
|
encode_name(ebml_w, nm);
|
||||||
encode_path(ebml_w, path, ast_map::path_name(nm));
|
encode_path(ebml_w, path, ast_map::path_name(nm));
|
||||||
encode_type(ecx, ebml_w, node_id_to_type(tcx, id));
|
encode_type(ecx, ebml_w, node_id_to_type(tcx, id));
|
||||||
/* TODO: mutability */
|
encode_mutability(ebml_w, mt);
|
||||||
encode_def_id(ebml_w, local_def(id));
|
encode_def_id(ebml_w, local_def(id));
|
||||||
ebml_w.end_tag();
|
ebml_w.end_tag();
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,16 @@ fn expr_root(tcx: ty::ctxt, ex: @expr, autoderef: bool) ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ty::ty_class(did, _) {
|
||||||
|
util::common::log_expr(*ex);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ {}
|
_ {}
|
||||||
}
|
}
|
||||||
ds += [@{mutbl: is_mutbl, kind: field, outer_t: auto_unbox.t}];
|
ds += [@{mutbl: is_mutbl, kind: field, outer_t: auto_unbox.t}];
|
||||||
|
@ -114,14 +124,17 @@ fn expr_root(tcx: ty::ctxt, ex: @expr, autoderef: bool) ->
|
||||||
// Actual mutbl-checking pass
|
// Actual mutbl-checking pass
|
||||||
|
|
||||||
type mutbl_map = std::map::hashmap<node_id, ()>;
|
type mutbl_map = std::map::hashmap<node_id, ()>;
|
||||||
type ctx = {tcx: ty::ctxt, mutbl_map: mutbl_map};
|
// 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};
|
||||||
|
|
||||||
fn check_crate(tcx: ty::ctxt, crate: @crate) -> mutbl_map {
|
fn check_crate(tcx: ty::ctxt, crate: @crate) -> mutbl_map {
|
||||||
let cx = @{tcx: tcx, mutbl_map: std::map::int_hash()};
|
let cx = @{tcx: tcx, mutbl_map: std::map::int_hash(), in_ctor: false};
|
||||||
let v = @{visit_expr: bind visit_expr(cx, _, _, _),
|
let v = @{visit_expr: visit_expr,
|
||||||
visit_decl: bind visit_decl(cx, _, _, _)
|
visit_decl: visit_decl,
|
||||||
|
visit_item: visit_item
|
||||||
with *visit::default_visitor()};
|
with *visit::default_visitor()};
|
||||||
visit::visit_crate(*crate, (), visit::mk_vt(v));
|
visit::visit_crate(*crate, cx, visit::mk_vt(v));
|
||||||
ret cx.mutbl_map;
|
ret cx.mutbl_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,8 +148,8 @@ fn mk_err(cx: @ctx, span: syntax::codemap::span, msg: msg, name: str) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_decl(cx: @ctx, d: @decl, &&e: (), v: visit::vt<()>) {
|
fn visit_decl(d: @decl, &&cx: @ctx, v: visit::vt<@ctx>) {
|
||||||
visit::visit_decl(d, e, v);
|
visit::visit_decl(d, cx, v);
|
||||||
alt d.node {
|
alt d.node {
|
||||||
decl_local(locs) {
|
decl_local(locs) {
|
||||||
for loc in locs {
|
for loc in locs {
|
||||||
|
@ -152,7 +165,7 @@ fn visit_decl(cx: @ctx, d: @decl, &&e: (), v: visit::vt<()>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expr(cx: @ctx, ex: @expr, &&e: (), v: visit::vt<()>) {
|
fn visit_expr(ex: @expr, &&cx: @ctx, v: visit::vt<@ctx>) {
|
||||||
alt ex.node {
|
alt ex.node {
|
||||||
expr_call(f, args, _) { check_call(cx, f, args); }
|
expr_call(f, args, _) { check_call(cx, f, args); }
|
||||||
expr_bind(f, args) { check_bind(cx, f, args); }
|
expr_bind(f, args) { check_bind(cx, f, args); }
|
||||||
|
@ -179,7 +192,22 @@ fn visit_expr(cx: @ctx, ex: @expr, &&e: (), v: visit::vt<()>) {
|
||||||
}
|
}
|
||||||
_ { }
|
_ { }
|
||||||
}
|
}
|
||||||
visit::visit_expr(ex, e, v);
|
visit::visit_expr(ex, cx, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_item(item: @item, &&cx: @ctx, v: visit::vt<@ctx>) {
|
||||||
|
alt item.node {
|
||||||
|
item_class(tps, items, ctor) {
|
||||||
|
v.visit_ty_params(tps, cx, v);
|
||||||
|
vec::map::<@class_item, ()>(items,
|
||||||
|
{|i| v.visit_class_item(i.span,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
_ { visit::visit_item(item, cx, v); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_lval(cx: @ctx, dest: @expr, msg: msg) {
|
fn check_lval(cx: @ctx, dest: @expr, msg: msg) {
|
||||||
|
@ -277,7 +305,7 @@ fn check_bind(cx: @ctx, f: @expr, args: [option<@expr>]) {
|
||||||
fn is_illegal_to_modify_def(cx: @ctx, def: def, msg: msg) -> option<str> {
|
fn is_illegal_to_modify_def(cx: @ctx, def: def, msg: msg) -> option<str> {
|
||||||
alt def {
|
alt def {
|
||||||
def_fn(_, _) | def_mod(_) | def_native_mod(_) | def_const(_) |
|
def_fn(_, _) | def_mod(_) | def_native_mod(_) | def_const(_) |
|
||||||
def_use(_) {
|
def_use(_) | def_class_method(_,_) {
|
||||||
some("static item")
|
some("static item")
|
||||||
}
|
}
|
||||||
def_arg(_, m) {
|
def_arg(_, m) {
|
||||||
|
@ -310,6 +338,18 @@ fn is_illegal_to_modify_def(cx: @ctx, def: def, msg: msg) -> option<str> {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_binding(_) { some("binding") }
|
def_binding(_) { some("binding") }
|
||||||
|
def_class_field(parent,fld) {
|
||||||
|
if !cx.in_ctor {
|
||||||
|
/* Enforce mutability *unless* we're inside a ctor */
|
||||||
|
alt ty::lookup_class_field(cx.tcx, parent, fld).mutability {
|
||||||
|
class_mutable { none }
|
||||||
|
class_immutable { some("immutable class field") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
none
|
||||||
|
}
|
||||||
|
}
|
||||||
_ { none }
|
_ { none }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2271,11 +2271,7 @@ fn trans_rec_field_inner(bcx: block, val: ValueRef, ty: ty::t,
|
||||||
_ { bcx.tcx().sess.span_bug(sp, "trans_rec_field:\
|
_ { bcx.tcx().sess.span_bug(sp, "trans_rec_field:\
|
||||||
base expr has non-record type"); }
|
base expr has non-record type"); }
|
||||||
};
|
};
|
||||||
let ix = alt ty::field_idx(field, fields) {
|
let ix = field_idx_strict(bcx.tcx(), sp, field, fields);
|
||||||
none { bcx.tcx().sess.span_bug(sp, #fmt("trans_rec_field:\
|
|
||||||
base expr doesn't appear to have a field named %s", field));}
|
|
||||||
some(i) { i }
|
|
||||||
};
|
|
||||||
let val = GEPi(bcx, val, [0, ix as int]);
|
let val = GEPi(bcx, val, [0, ix as int]);
|
||||||
ret {bcx: bcx, val: val, kind: owned};
|
ret {bcx: bcx, val: val, kind: owned};
|
||||||
}
|
}
|
||||||
|
@ -3666,7 +3662,7 @@ fn raw_block(fcx: fn_ctxt, llbb: BasicBlockRef) -> block {
|
||||||
// trans_block_cleanups: Go through all the cleanups attached to this
|
// trans_block_cleanups: Go through all the cleanups attached to this
|
||||||
// block and execute them.
|
// block and execute them.
|
||||||
//
|
//
|
||||||
// When translating a block that introdces new variables during its scope, we
|
// When translating a block that introduces new variables during its scope, we
|
||||||
// need to make sure those variables go out of scope when the block ends. We
|
// need to make sure those variables go out of scope when the block ends. We
|
||||||
// do that by running a 'cleanup' function for each variable.
|
// do that by running a 'cleanup' function for each variable.
|
||||||
// trans_block_cleanups runs all the cleanup functions for the block.
|
// trans_block_cleanups runs all the cleanup functions for the block.
|
||||||
|
@ -4344,10 +4340,24 @@ fn trans_item(ccx: @crate_ctxt, item: ast::item) {
|
||||||
// wouldn't make sense
|
// wouldn't make sense
|
||||||
// So we initialize it here
|
// So we initialize it here
|
||||||
let selfptr = alloc_ty(bcx_top, rslt_ty);
|
let selfptr = alloc_ty(bcx_top, rslt_ty);
|
||||||
|
// initialize fields to zero
|
||||||
|
let fields = ty::class_items_as_fields(bcx_top.tcx(),
|
||||||
|
local_def(item.id));
|
||||||
|
let mut bcx = bcx_top;
|
||||||
|
// Initialize fields to zero so init assignments can validly
|
||||||
|
// drop their LHS
|
||||||
|
for field in fields {
|
||||||
|
let ix = field_idx_strict(bcx.tcx(), ctor.span, field.ident,
|
||||||
|
fields);
|
||||||
|
bcx = zero_alloca(bcx, GEPi(bcx, selfptr, [0, ix]),
|
||||||
|
field.mt.ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
// note we don't want to take *or* drop self.
|
||||||
fcx.llself = some({v: selfptr, t: rslt_ty});
|
fcx.llself = some({v: selfptr, t: rslt_ty});
|
||||||
|
|
||||||
// Translate the body of the ctor
|
// Translate the body of the ctor
|
||||||
let mut bcx = trans_block(bcx_top, ctor.node.body, ignore);
|
bcx = trans_block(bcx_top, ctor.node.body, ignore);
|
||||||
let lval_res = {bcx: bcx, val: selfptr, kind: owned};
|
let lval_res = {bcx: bcx, val: selfptr, kind: owned};
|
||||||
// Generate the return expression
|
// Generate the return expression
|
||||||
bcx = store_temp_expr(bcx, INIT, fcx.llretptr, lval_res,
|
bcx = store_temp_expr(bcx, INIT, fcx.llretptr, lval_res,
|
||||||
|
|
|
@ -884,6 +884,16 @@ fn node_id_type_params(bcx: block, id: ast::node_id) -> [ty::t] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn field_idx_strict(cx: ty::ctxt, sp: span, ident: ast::ident,
|
||||||
|
fields: [ty::field])
|
||||||
|
-> int {
|
||||||
|
alt ty::field_idx(ident, fields) {
|
||||||
|
none { cx.sess.span_bug(sp, #fmt("base expr doesn't appear to \
|
||||||
|
have a field named %s", ident)); }
|
||||||
|
some(i) { i as int }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Local Variables:
|
// Local Variables:
|
||||||
// mode: rust
|
// mode: rust
|
||||||
|
|
|
@ -57,7 +57,6 @@ const shape_stack_fn: u8 = 26u8;
|
||||||
const shape_bare_fn: u8 = 27u8;
|
const shape_bare_fn: u8 = 27u8;
|
||||||
const shape_tydesc: u8 = 28u8;
|
const shape_tydesc: u8 = 28u8;
|
||||||
const shape_send_tydesc: u8 = 29u8;
|
const shape_send_tydesc: u8 = 29u8;
|
||||||
const shape_class: u8 = 30u8;
|
|
||||||
const shape_rptr: u8 = 31u8;
|
const shape_rptr: u8 = 31u8;
|
||||||
|
|
||||||
fn hash_res_info(ri: res_info) -> uint {
|
fn hash_res_info(ri: res_info) -> uint {
|
||||||
|
@ -370,7 +369,15 @@ fn shape_of(ccx: @crate_ctxt, t: ty::t, ty_param_map: [uint]) -> [u8] {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
ty::ty_iface(_, _) { [shape_box_fn] }
|
ty::ty_iface(_, _) { [shape_box_fn] }
|
||||||
ty::ty_class(_, _) { [shape_class] }
|
ty::ty_class(did, _) {
|
||||||
|
// same as records
|
||||||
|
let mut s = [shape_struct], sub = [];
|
||||||
|
for f:field in ty::class_items_as_fields(ccx.tcx, did) {
|
||||||
|
sub += shape_of(ccx, f.mt.ty, ty_param_map);
|
||||||
|
}
|
||||||
|
add_substr(s, sub);
|
||||||
|
s
|
||||||
|
}
|
||||||
ty::ty_rptr(_, tm) {
|
ty::ty_rptr(_, tm) {
|
||||||
let mut s = [shape_rptr];
|
let mut s = [shape_rptr];
|
||||||
add_substr(s, shape_of(ccx, tm.ty, ty_param_map));
|
add_substr(s, shape_of(ccx, tm.ty, ty_param_map));
|
||||||
|
|
|
@ -41,7 +41,7 @@ export fm_var, fm_general, fm_rptr;
|
||||||
export get_element_type;
|
export get_element_type;
|
||||||
export is_binopable;
|
export is_binopable;
|
||||||
export is_pred_ty;
|
export is_pred_ty;
|
||||||
export lookup_class_fields;
|
export lookup_class_field, lookup_class_fields;
|
||||||
export lookup_class_method_by_name;
|
export lookup_class_method_by_name;
|
||||||
export lookup_field_type;
|
export lookup_field_type;
|
||||||
export lookup_item_type;
|
export lookup_item_type;
|
||||||
|
@ -164,7 +164,8 @@ type mt = {ty: t, mutbl: ast::mutability};
|
||||||
type field_ty = {
|
type field_ty = {
|
||||||
ident: ident,
|
ident: ident,
|
||||||
id: def_id,
|
id: def_id,
|
||||||
privacy: ast::privacy
|
privacy: ast::privacy,
|
||||||
|
mutability: ast::class_mutability
|
||||||
};
|
};
|
||||||
|
|
||||||
// Contains information needed to resolve types and (in the future) look up
|
// Contains information needed to resolve types and (in the future) look up
|
||||||
|
@ -864,6 +865,12 @@ fn type_needs_drop(cx: ctxt, ty: t) -> bool {
|
||||||
for f in flds { if type_needs_drop(cx, f.mt.ty) { accum = true; } }
|
for f in flds { if type_needs_drop(cx, f.mt.ty) { accum = true; } }
|
||||||
accum
|
accum
|
||||||
}
|
}
|
||||||
|
ty_class(did,_) {
|
||||||
|
for f in ty::class_items_as_fields(cx, did)
|
||||||
|
{ if type_needs_drop(cx, f.mt.ty) { accum = true; } }
|
||||||
|
accum
|
||||||
|
}
|
||||||
|
|
||||||
ty_tup(elts) {
|
ty_tup(elts) {
|
||||||
for m in elts { if type_needs_drop(cx, m) { accum = true; } }
|
for m in elts { if type_needs_drop(cx, m) { accum = true; } }
|
||||||
accum
|
accum
|
||||||
|
@ -1956,11 +1963,6 @@ fn lookup_item_type(cx: ctxt, did: ast::def_id) -> ty_param_bounds_and_ty {
|
||||||
// Look up a field ID, whether or not it's local
|
// Look up a field ID, whether or not it's local
|
||||||
fn lookup_field_type(tcx: ctxt, class_id: def_id, id: def_id) -> ty::t {
|
fn lookup_field_type(tcx: ctxt, class_id: def_id, id: def_id) -> ty::t {
|
||||||
if id.crate == ast::local_crate {
|
if id.crate == ast::local_crate {
|
||||||
/*
|
|
||||||
alt items.find(tcx.items, id.node) {
|
|
||||||
some(ast_map::node_item({node: item_class(_,items,
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
node_id_to_type(tcx, id.node)
|
node_id_to_type(tcx, id.node)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1999,6 +2001,15 @@ fn lookup_class_fields(cx: ctxt, did: ast::def_id) -> [field_ty] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lookup_class_field(cx: ctxt, parent: ast::def_id, field_id: ast::def_id)
|
||||||
|
-> field_ty {
|
||||||
|
alt vec::find(lookup_class_fields(cx, parent))
|
||||||
|
{|f| f.id.node == field_id.node} {
|
||||||
|
some(t) { t }
|
||||||
|
none { cx.sess.bug("class ID not found in parent's fields"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn lookup_public_fields(cx: ctxt, did: ast::def_id) -> [field_ty] {
|
fn lookup_public_fields(cx: ctxt, did: ast::def_id) -> [field_ty] {
|
||||||
vec::filter(lookup_class_fields(cx, did), is_public)
|
vec::filter(lookup_class_fields(cx, did), is_public)
|
||||||
}
|
}
|
||||||
|
@ -2050,9 +2061,9 @@ fn class_field_tys(items: [@class_item]) -> [field_ty] {
|
||||||
let mut rslt = [];
|
let mut rslt = [];
|
||||||
for it in items {
|
for it in items {
|
||||||
alt it.node.decl {
|
alt it.node.decl {
|
||||||
instance_var(nm, _, _, id) {
|
instance_var(nm, _, cm, id) {
|
||||||
rslt += [{ident: nm, id: ast_util::local_def(id),
|
rslt += [{ident: nm, id: ast_util::local_def(id),
|
||||||
privacy: it.node.privacy}];
|
privacy: it.node.privacy, mutability: cm}];
|
||||||
}
|
}
|
||||||
class_method(_) {
|
class_method(_) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1684,8 +1684,8 @@ fn parse_let(p: parser) -> @ast::decl {
|
||||||
fn parse_instance_var(p:parser) -> (ast::class_member, codemap::span) {
|
fn parse_instance_var(p:parser) -> (ast::class_member, codemap::span) {
|
||||||
let mut is_mutbl = ast::class_immutable;
|
let mut is_mutbl = ast::class_immutable;
|
||||||
let lo = p.span.lo;
|
let lo = p.span.lo;
|
||||||
if eat_word(p, "mut") {
|
if eat_word(p, "mut") || eat_word(p, "mutable") {
|
||||||
is_mutbl = ast::class_mutable;
|
is_mutbl = ast::class_mutable;
|
||||||
}
|
}
|
||||||
if !is_plain_ident(p) {
|
if !is_plain_ident(p) {
|
||||||
p.fatal("expecting ident");
|
p.fatal("expecting ident");
|
||||||
|
|
|
@ -12,9 +12,11 @@ class cat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let how_hungry : int;
|
let mutable how_hungry : int;
|
||||||
|
let name : str;
|
||||||
|
|
||||||
new(in_x : uint, in_y : int) { meows = in_x; how_hungry = in_y; }
|
new(in_x : uint, in_y : int, in_name: str)
|
||||||
|
{ meows = in_x; how_hungry = in_y; name = in_name; }
|
||||||
|
|
||||||
fn speak() { meow(); }
|
fn speak() { meow(); }
|
||||||
|
|
||||||
|
|
19
src/test/compile-fail/mutable-class-fields-2.rs
Normal file
19
src/test/compile-fail/mutable-class-fields-2.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// error-pattern:assigning to immutable class field
|
||||||
|
class cat {
|
||||||
|
priv {
|
||||||
|
let mutable meows : uint;
|
||||||
|
}
|
||||||
|
|
||||||
|
let how_hungry : int;
|
||||||
|
|
||||||
|
fn eat() {
|
||||||
|
how_hungry -= 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
new(in_x : uint, in_y : int) { meows = in_x; how_hungry = in_y; }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let nyan : cat = cat(52u, 99);
|
||||||
|
nyan.eat();
|
||||||
|
}
|
15
src/test/compile-fail/mutable-class-fields.rs
Normal file
15
src/test/compile-fail/mutable-class-fields.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// error-pattern:assigning to immutable field
|
||||||
|
class cat {
|
||||||
|
priv {
|
||||||
|
let mutable meows : uint;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
nyan.how_hungry = 0;
|
||||||
|
}
|
11
src/test/run-pass/class-str-field.rs
Normal file
11
src/test/run-pass/class-str-field.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class cat {
|
||||||
|
|
||||||
|
let name : str;
|
||||||
|
|
||||||
|
new(in_name: str)
|
||||||
|
{ name = in_name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let nyan = cat("nyan");
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ use cci_class_4;
|
||||||
import cci_class_4::kitties::*;
|
import cci_class_4::kitties::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let nyan = cat(0u, 2);
|
let nyan = cat(0u, 2, "nyan");
|
||||||
nyan.eat();
|
nyan.eat();
|
||||||
assert(!nyan.eat());
|
assert(!nyan.eat());
|
||||||
uint::range(1u, 10u, {|_i| nyan.speak(); });
|
uint::range(1u, 10u, {|_i| nyan.speak(); });
|
||||||
|
|
|
@ -10,9 +10,11 @@ class cat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let how_hungry : int;
|
let mutable how_hungry : int;
|
||||||
|
let name : str;
|
||||||
|
|
||||||
new(in_x : uint, in_y : int) { meows = in_x; how_hungry = in_y; }
|
new(in_x : uint, in_y : int, in_name: str)
|
||||||
|
{ meows = in_x; how_hungry = in_y; name = in_name; }
|
||||||
|
|
||||||
fn speak() { meow(); }
|
fn speak() { meow(); }
|
||||||
|
|
||||||
|
@ -30,7 +32,7 @@ class cat {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let nyan = cat(0u, 2);
|
let nyan = cat(0u, 2, "nyan");
|
||||||
nyan.eat();
|
nyan.eat();
|
||||||
assert(!nyan.eat());
|
assert(!nyan.eat());
|
||||||
uint::range(1u, 10u, {|_i| nyan.speak(); });
|
uint::range(1u, 10u, {|_i| nyan.speak(); });
|
||||||
|
|
Loading…
Add table
Reference in a new issue