Skip to content

Commit edb747c

Browse files
committed
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).
1 parent c9102ee commit edb747c

File tree

15 files changed

+187
-40
lines changed

15 files changed

+187
-40
lines changed

src/rustc/metadata/common.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ const tag_path_len: uint = 0x41u;
7878
const tag_path_elt_mod: uint = 0x42u;
7979
const tag_path_elt_name: uint = 0x43u;
8080
const tag_item_field: uint = 0x44u;
81+
const tag_class_mut: uint = 0x45u;
8182

8283
// used to encode crate_ctxt side tables
8384
enum astencode_tag { // Reserves 0x50 -- 0x6f

src/rustc/metadata/decoder.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,18 @@ fn class_member_id(d: ebml::doc, cdata: cmd) -> ast::def_id {
118118
ret translate_def_id(cdata, parse_def_id(ebml::doc_data(tagdoc)));
119119
}
120120

121+
fn field_mutability(d: ebml::doc) -> ast::class_mutability {
122+
// Use maybe_get_doc in case it's a method
123+
option::maybe(ebml::maybe_get_doc(d, tag_class_mut),
124+
ast::class_immutable,
125+
{|d|
126+
alt ebml::doc_as_u8(d) as char {
127+
'm' { ast::class_mutable }
128+
_ { ast::class_immutable }
129+
}
130+
})
131+
}
132+
121133
fn variant_disr_val(d: ebml::doc) -> option<int> {
122134
option::chain(ebml::maybe_get_doc(d, tag_disr_val)) {|val_doc|
123135
int::parse_buf(ebml::doc_data(val_doc), 10u)
@@ -435,9 +447,9 @@ fn get_class_members(cdata: cmd, id: ast::node_id,
435447
if p(f) {
436448
let name = item_name(an_item);
437449
let did = class_member_id(an_item, cdata);
450+
let mt = field_mutability(an_item);
438451
result += [{ident: name, id: did, privacy:
439-
// This won't work for methods, argh
440-
family_to_privacy(f)}];
452+
family_to_privacy(f), mutability: mt}];
441453
}
442454
}
443455
result

src/rustc/metadata/encoder.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ fn encode_named_def_id(ebml_w: ebml::writer, name: str, id: def_id) {
4747
}
4848
}
4949

50+
fn encode_mutability(ebml_w: ebml::writer, mt: class_mutability) {
51+
ebml_w.wr_tag(tag_class_mut) {||
52+
ebml_w.writer.write([alt mt { class_immutable { 'i' }
53+
class_mutable { 'm' } } as u8]);
54+
}
55+
}
56+
5057
type entry<T> = {val: T, pos: uint};
5158

5259
fn encode_enum_variant_paths(ebml_w: ebml::writer, variants: [variant],
@@ -370,15 +377,15 @@ fn encode_info_for_class(ecx: @encode_ctxt, ebml_w: ebml::writer,
370377
/* We encode both private and public fields -- need to include
371378
private fields to get the offsets right */
372379
alt ci.node.decl {
373-
instance_var(nm, _, _, id) {
380+
instance_var(nm, _, mt, id) {
374381
*index += [{val: id, pos: ebml_w.writer.tell()}];
375382
ebml_w.start_tag(tag_items_data_item);
376383
#debug("encode_info_for_class: doing %s %d", nm, id);
377384
encode_privacy(ebml_w, ci.node.privacy);
378385
encode_name(ebml_w, nm);
379386
encode_path(ebml_w, path, ast_map::path_name(nm));
380387
encode_type(ecx, ebml_w, node_id_to_type(tcx, id));
381-
/* TODO: mutability */
388+
encode_mutability(ebml_w, mt);
382389
encode_def_id(ebml_w, local_def(id));
383390
ebml_w.end_tag();
384391
}

src/rustc/middle/mutbl.rs

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ fn expr_root(tcx: ty::ctxt, ex: @expr, autoderef: bool) ->
5757
}
5858
}
5959
}
60+
ty::ty_class(did, _) {
61+
util::common::log_expr(*ex);
62+
for fld: ty::field_ty in ty::lookup_class_fields(tcx, did) {
63+
#debug("%s %?", fld.ident, fld.mutability);
64+
if str::eq(ident, fld.ident) {
65+
is_mutbl = fld.mutability == class_mutable;
66+
}
67+
break;
68+
}
69+
}
6070
_ {}
6171
}
6272
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) ->
114124
// Actual mutbl-checking pass
115125

116126
type mutbl_map = std::map::hashmap<node_id, ()>;
117-
type ctx = {tcx: ty::ctxt, mutbl_map: mutbl_map};
127+
// Keep track of whether we're inside a ctor, so as to
128+
// allow mutating immutable fields in the same class
129+
type ctx = {tcx: ty::ctxt, mutbl_map: mutbl_map, in_ctor: bool};
118130

119131
fn check_crate(tcx: ty::ctxt, crate: @crate) -> mutbl_map {
120-
let cx = @{tcx: tcx, mutbl_map: std::map::int_hash()};
121-
let v = @{visit_expr: bind visit_expr(cx, _, _, _),
122-
visit_decl: bind visit_decl(cx, _, _, _)
132+
let cx = @{tcx: tcx, mutbl_map: std::map::int_hash(), in_ctor: false};
133+
let v = @{visit_expr: visit_expr,
134+
visit_decl: visit_decl,
135+
visit_item: visit_item
123136
with *visit::default_visitor()};
124-
visit::visit_crate(*crate, (), visit::mk_vt(v));
137+
visit::visit_crate(*crate, cx, visit::mk_vt(v));
125138
ret cx.mutbl_map;
126139
}
127140

@@ -135,8 +148,8 @@ fn mk_err(cx: @ctx, span: syntax::codemap::span, msg: msg, name: str) {
135148
});
136149
}
137150

138-
fn visit_decl(cx: @ctx, d: @decl, &&e: (), v: visit::vt<()>) {
139-
visit::visit_decl(d, e, v);
151+
fn visit_decl(d: @decl, &&cx: @ctx, v: visit::vt<@ctx>) {
152+
visit::visit_decl(d, cx, v);
140153
alt d.node {
141154
decl_local(locs) {
142155
for loc in locs {
@@ -152,7 +165,7 @@ fn visit_decl(cx: @ctx, d: @decl, &&e: (), v: visit::vt<()>) {
152165
}
153166
}
154167

155-
fn visit_expr(cx: @ctx, ex: @expr, &&e: (), v: visit::vt<()>) {
168+
fn visit_expr(ex: @expr, &&cx: @ctx, v: visit::vt<@ctx>) {
156169
alt ex.node {
157170
expr_call(f, args, _) { check_call(cx, f, args); }
158171
expr_bind(f, args) { check_bind(cx, f, args); }
@@ -179,7 +192,22 @@ fn visit_expr(cx: @ctx, ex: @expr, &&e: (), v: visit::vt<()>) {
179192
}
180193
_ { }
181194
}
182-
visit::visit_expr(ex, e, v);
195+
visit::visit_expr(ex, cx, v);
196+
}
197+
198+
fn visit_item(item: @item, &&cx: @ctx, v: visit::vt<@ctx>) {
199+
alt item.node {
200+
item_class(tps, items, ctor) {
201+
v.visit_ty_params(tps, cx, v);
202+
vec::map::<@class_item, ()>(items,
203+
{|i| v.visit_class_item(i.span,
204+
i.node.privacy, i.node.decl, cx, v); });
205+
v.visit_fn(visit::fk_ctor(item.ident, tps), ctor.node.dec,
206+
ctor.node.body, ctor.span, ctor.node.id,
207+
@{in_ctor: true with *cx}, v);
208+
}
209+
_ { visit::visit_item(item, cx, v); }
210+
}
183211
}
184212

185213
fn check_lval(cx: @ctx, dest: @expr, msg: msg) {
@@ -277,7 +305,7 @@ fn check_bind(cx: @ctx, f: @expr, args: [option<@expr>]) {
277305
fn is_illegal_to_modify_def(cx: @ctx, def: def, msg: msg) -> option<str> {
278306
alt def {
279307
def_fn(_, _) | def_mod(_) | def_native_mod(_) | def_const(_) |
280-
def_use(_) {
308+
def_use(_) | def_class_method(_,_) {
281309
some("static item")
282310
}
283311
def_arg(_, m) {
@@ -310,6 +338,18 @@ fn is_illegal_to_modify_def(cx: @ctx, def: def, msg: msg) -> option<str> {
310338
}
311339

312340
def_binding(_) { some("binding") }
341+
def_class_field(parent,fld) {
342+
if !cx.in_ctor {
343+
/* Enforce mutability *unless* we're inside a ctor */
344+
alt ty::lookup_class_field(cx.tcx, parent, fld).mutability {
345+
class_mutable { none }
346+
class_immutable { some("immutable class field") }
347+
}
348+
}
349+
else {
350+
none
351+
}
352+
}
313353
_ { none }
314354
}
315355
}

src/rustc/middle/trans/base.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,11 +2271,7 @@ fn trans_rec_field_inner(bcx: block, val: ValueRef, ty: ty::t,
22712271
_ { bcx.tcx().sess.span_bug(sp, "trans_rec_field:\
22722272
base expr has non-record type"); }
22732273
};
2274-
let ix = alt ty::field_idx(field, fields) {
2275-
none { bcx.tcx().sess.span_bug(sp, #fmt("trans_rec_field:\
2276-
base expr doesn't appear to have a field named %s", field));}
2277-
some(i) { i }
2278-
};
2274+
let ix = field_idx_strict(bcx.tcx(), sp, field, fields);
22792275
let val = GEPi(bcx, val, [0, ix as int]);
22802276
ret {bcx: bcx, val: val, kind: owned};
22812277
}
@@ -3666,7 +3662,7 @@ fn raw_block(fcx: fn_ctxt, llbb: BasicBlockRef) -> block {
36663662
// trans_block_cleanups: Go through all the cleanups attached to this
36673663
// block and execute them.
36683664
//
3669-
// When translating a block that introdces new variables during its scope, we
3665+
// When translating a block that introduces new variables during its scope, we
36703666
// need to make sure those variables go out of scope when the block ends. We
36713667
// do that by running a 'cleanup' function for each variable.
36723668
// trans_block_cleanups runs all the cleanup functions for the block.
@@ -4344,10 +4340,24 @@ fn trans_item(ccx: @crate_ctxt, item: ast::item) {
43444340
// wouldn't make sense
43454341
// So we initialize it here
43464342
let selfptr = alloc_ty(bcx_top, rslt_ty);
4343+
// initialize fields to zero
4344+
let fields = ty::class_items_as_fields(bcx_top.tcx(),
4345+
local_def(item.id));
4346+
let mut bcx = bcx_top;
4347+
// Initialize fields to zero so init assignments can validly
4348+
// drop their LHS
4349+
for field in fields {
4350+
let ix = field_idx_strict(bcx.tcx(), ctor.span, field.ident,
4351+
fields);
4352+
bcx = zero_alloca(bcx, GEPi(bcx, selfptr, [0, ix]),
4353+
field.mt.ty);
4354+
}
4355+
4356+
// note we don't want to take *or* drop self.
43474357
fcx.llself = some({v: selfptr, t: rslt_ty});
43484358

43494359
// Translate the body of the ctor
4350-
let mut bcx = trans_block(bcx_top, ctor.node.body, ignore);
4360+
bcx = trans_block(bcx_top, ctor.node.body, ignore);
43514361
let lval_res = {bcx: bcx, val: selfptr, kind: owned};
43524362
// Generate the return expression
43534363
bcx = store_temp_expr(bcx, INIT, fcx.llretptr, lval_res,

src/rustc/middle/trans/common.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,16 @@ fn node_id_type_params(bcx: block, id: ast::node_id) -> [ty::t] {
884884
}
885885
}
886886

887+
fn field_idx_strict(cx: ty::ctxt, sp: span, ident: ast::ident,
888+
fields: [ty::field])
889+
-> int {
890+
alt ty::field_idx(ident, fields) {
891+
none { cx.sess.span_bug(sp, #fmt("base expr doesn't appear to \
892+
have a field named %s", ident)); }
893+
some(i) { i as int }
894+
}
895+
}
896+
887897
//
888898
// Local Variables:
889899
// mode: rust

src/rustc/middle/trans/shape.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ const shape_stack_fn: u8 = 26u8;
5757
const shape_bare_fn: u8 = 27u8;
5858
const shape_tydesc: u8 = 28u8;
5959
const shape_send_tydesc: u8 = 29u8;
60-
const shape_class: u8 = 30u8;
6160
const shape_rptr: u8 = 31u8;
6261

6362
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] {
370369
s
371370
}
372371
ty::ty_iface(_, _) { [shape_box_fn] }
373-
ty::ty_class(_, _) { [shape_class] }
372+
ty::ty_class(did, _) {
373+
// same as records
374+
let mut s = [shape_struct], sub = [];
375+
for f:field in ty::class_items_as_fields(ccx.tcx, did) {
376+
sub += shape_of(ccx, f.mt.ty, ty_param_map);
377+
}
378+
add_substr(s, sub);
379+
s
380+
}
374381
ty::ty_rptr(_, tm) {
375382
let mut s = [shape_rptr];
376383
add_substr(s, shape_of(ccx, tm.ty, ty_param_map));

src/rustc/middle/ty.rs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export fm_var, fm_general, fm_rptr;
4141
export get_element_type;
4242
export is_binopable;
4343
export is_pred_ty;
44-
export lookup_class_fields;
44+
export lookup_class_field, lookup_class_fields;
4545
export lookup_class_method_by_name;
4646
export lookup_field_type;
4747
export lookup_item_type;
@@ -164,7 +164,8 @@ type mt = {ty: t, mutbl: ast::mutability};
164164
type field_ty = {
165165
ident: ident,
166166
id: def_id,
167-
privacy: ast::privacy
167+
privacy: ast::privacy,
168+
mutability: ast::class_mutability
168169
};
169170

170171
// 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 {
864865
for f in flds { if type_needs_drop(cx, f.mt.ty) { accum = true; } }
865866
accum
866867
}
868+
ty_class(did,_) {
869+
for f in ty::class_items_as_fields(cx, did)
870+
{ if type_needs_drop(cx, f.mt.ty) { accum = true; } }
871+
accum
872+
}
873+
867874
ty_tup(elts) {
868875
for m in elts { if type_needs_drop(cx, m) { accum = true; } }
869876
accum
@@ -1956,11 +1963,6 @@ fn lookup_item_type(cx: ctxt, did: ast::def_id) -> ty_param_bounds_and_ty {
19561963
// Look up a field ID, whether or not it's local
19571964
fn lookup_field_type(tcx: ctxt, class_id: def_id, id: def_id) -> ty::t {
19581965
if id.crate == ast::local_crate {
1959-
/*
1960-
alt items.find(tcx.items, id.node) {
1961-
some(ast_map::node_item({node: item_class(_,items,
1962-
}
1963-
*/
19641966
node_id_to_type(tcx, id.node)
19651967
}
19661968
else {
@@ -1999,6 +2001,15 @@ fn lookup_class_fields(cx: ctxt, did: ast::def_id) -> [field_ty] {
19992001
}
20002002
}
20012003

2004+
fn lookup_class_field(cx: ctxt, parent: ast::def_id, field_id: ast::def_id)
2005+
-> field_ty {
2006+
alt vec::find(lookup_class_fields(cx, parent))
2007+
{|f| f.id.node == field_id.node} {
2008+
some(t) { t }
2009+
none { cx.sess.bug("class ID not found in parent's fields"); }
2010+
}
2011+
}
2012+
20022013
fn lookup_public_fields(cx: ctxt, did: ast::def_id) -> [field_ty] {
20032014
vec::filter(lookup_class_fields(cx, did), is_public)
20042015
}
@@ -2050,9 +2061,9 @@ fn class_field_tys(items: [@class_item]) -> [field_ty] {
20502061
let mut rslt = [];
20512062
for it in items {
20522063
alt it.node.decl {
2053-
instance_var(nm, _, _, id) {
2064+
instance_var(nm, _, cm, id) {
20542065
rslt += [{ident: nm, id: ast_util::local_def(id),
2055-
privacy: it.node.privacy}];
2066+
privacy: it.node.privacy, mutability: cm}];
20562067
}
20572068
class_method(_) {
20582069
}

src/rustc/syntax/parse/parser.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,8 +1684,8 @@ fn parse_let(p: parser) -> @ast::decl {
16841684
fn parse_instance_var(p:parser) -> (ast::class_member, codemap::span) {
16851685
let mut is_mutbl = ast::class_immutable;
16861686
let lo = p.span.lo;
1687-
if eat_word(p, "mut") {
1688-
is_mutbl = ast::class_mutable;
1687+
if eat_word(p, "mut") || eat_word(p, "mutable") {
1688+
is_mutbl = ast::class_mutable;
16891689
}
16901690
if !is_plain_ident(p) {
16911691
p.fatal("expecting ident");

src/test/auxiliary/cci_class_4.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ class cat {
1212
}
1313
}
1414

15-
let how_hungry : int;
15+
let mutable how_hungry : int;
16+
let name : str;
1617

17-
new(in_x : uint, in_y : int) { meows = in_x; how_hungry = in_y; }
18+
new(in_x : uint, in_y : int, in_name: str)
19+
{ meows = in_x; how_hungry = in_y; name = in_name; }
1820

1921
fn speak() { meow(); }
2022

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// error-pattern:assigning to immutable class field
2+
class cat {
3+
priv {
4+
let mutable meows : uint;
5+
}
6+
7+
let how_hungry : int;
8+
9+
fn eat() {
10+
how_hungry -= 5;
11+
}
12+
13+
new(in_x : uint, in_y : int) { meows = in_x; how_hungry = in_y; }
14+
}
15+
16+
fn main() {
17+
let nyan : cat = cat(52u, 99);
18+
nyan.eat();
19+
}

0 commit comments

Comments
 (0)