diff options
| author | Tim Chevalier <chevalier@alum.wellesley.edu> | 2012-10-08 09:00:23 -0700 |
|---|---|---|
| committer | Tim Chevalier <chevalier@alum.wellesley.edu> | 2012-10-08 09:00:23 -0700 |
| commit | 7bdab1e4a45bf734e4b25a2d58f4ee21ee5c9fdc (patch) | |
| tree | b24c818211cffeb1f2b8cc7bf7bc31fe3eecfdc9 | |
| parent | 79603f573e504163db7b5c2afa0917c27e3f98ed (diff) | |
| download | rust-7bdab1e4a45bf734e4b25a2d58f4ee21ee5c9fdc.tar.gz rust-7bdab1e4a45bf734e4b25a2d58f4ee21ee5c9fdc.zip | |
Revert "remove ctor from ast"
This reverts commit ed3689d57c988e1dd477930d957c4308c37d1a64.
30 files changed, 651 insertions, 120 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index ced64daa8d6..1c279f81cc3 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -135,7 +135,7 @@ enum def { @def, // closed over def node_id, // expr node that creates the closure node_id), // id for the block/body of the closure expr - def_class(def_id), + def_class(def_id, bool /* has constructor */), def_typaram_binder(node_id), /* class, impl or trait that has ty params */ def_region(node_id), def_label(node_id) @@ -235,9 +235,9 @@ impl def : cmp::Eq { _ => false } } - def_class(e0a) => { + def_class(e0a, e1a) => { match (*other) { - def_class(e0b) => e0a == e0b, + def_class(e0b, e1b) => e0a == e0b && e1a == e1b, _ => false } } @@ -1462,6 +1462,8 @@ type struct_def = { fields: ~[@struct_field], /* fields */ methods: ~[@method], /* methods */ /* (not including ctor or dtor) */ + /* ctor is optional, and will soon go away */ + ctor: Option<class_ctor>, /* dtor is optional */ dtor: Option<class_dtor> }; @@ -1561,6 +1563,7 @@ enum inlined_item { ii_item(@item), ii_method(def_id /* impl id */, @method), ii_foreign(@foreign_item), + ii_ctor(class_ctor, ident, ~[ty_param], def_id /* parent id */), ii_dtor(class_dtor, ident, ~[ty_param], def_id /* parent id */) } diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs index 8555ceed2db..d05c6eadaf6 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map.rs @@ -71,6 +71,9 @@ enum ast_node { // order they are introduced. node_arg(arg, uint), node_local(uint), + // Constructor for a class + // def_id is parent id + node_ctor(ident, ~[ty_param], @class_ctor, def_id, @path), // Destructor for a class node_dtor(~[ty_param], @class_dtor, def_id, @path), node_block(blk), @@ -129,7 +132,7 @@ fn map_decoded_item(diag: span_handler, // don't decode and instantiate the impl, but just the method, we have to // add it to the table now: match ii { - ii_item(*) | ii_dtor(*) => { /* fallthrough */ } + ii_item(*) | ii_ctor(*) | ii_dtor(*) => { /* fallthrough */ } ii_foreign(i) => { cx.map.insert(i.id, node_foreign_item(i, foreign_abi_rust_intrinsic, @path)); @@ -152,6 +155,18 @@ fn map_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, cx.local_id += 1u; } match fk { + visit::fk_ctor(nm, attrs, tps, self_id, parent_id) => { + let ct = @{node: {id: id, + attrs: attrs, + self_id: self_id, + dec: /* FIXME (#2543) */ copy decl, + body: /* FIXME (#2543) */ copy body}, + span: sp}; + cx.map.insert(id, node_ctor(/* FIXME (#2543) */ copy nm, + /* FIXME (#2543) */ copy tps, + ct, parent_id, + @/* FIXME (#2543) */ copy cx.path)); + } visit::fk_dtor(tps, attrs, self_id, parent_id) => { let dt = @{node: {id: id, attrs: attrs, self_id: self_id, body: /* FIXME (#2543) */ copy body}, span: sp}; @@ -367,6 +382,9 @@ fn node_id_to_str(map: map, id: node_id, itr: @ident_interner) -> ~str { Some(node_local(_)) => { // add more info here fmt!("local (id=%?)", id) } + Some(node_ctor(*)) => { // add more info here + fmt!("node_ctor (id=%?)", id) + } Some(node_dtor(*)) => { // add more info here fmt!("node_dtor (id=%?)", id) } diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 9e44c42a702..47cbdb7ac6c 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -57,7 +57,7 @@ pure fn def_id_of_def(d: def) -> def_id { def_fn(id, _) | def_static_method(id, _) | def_mod(id) | def_foreign_mod(id) | def_const(id) | def_variant(_, id) | def_ty(id) | def_ty_param(id, _) | - def_use(id) | def_class(id) => { + def_use(id) | def_class(id, _) => { id } def_arg(id, _) | def_local(id, _) | def_self(id) | @@ -339,6 +339,7 @@ impl inlined_item: inlined_item_utils { ii_item(i) => /* FIXME (#2543) */ copy i.ident, ii_foreign(i) => /* FIXME (#2543) */ copy i.ident, ii_method(_, m) => /* FIXME (#2543) */ copy m.ident, + ii_ctor(_, nm, _, _) => /* FIXME (#2543) */ copy nm, ii_dtor(_, nm, _, _) => /* FIXME (#2543) */ copy nm } } @@ -348,6 +349,7 @@ impl inlined_item: inlined_item_utils { ii_item(i) => i.id, ii_foreign(i) => i.id, ii_method(_, m) => m.id, + ii_ctor(ctor, _, _, _) => ctor.node.id, ii_dtor(dtor, _, _, _) => dtor.node.id } } @@ -357,6 +359,9 @@ impl inlined_item: inlined_item_utils { ii_item(i) => v.visit_item(i, e, v), ii_foreign(i) => v.visit_foreign_item(i, e, v), ii_method(_, m) => visit::visit_method_helper(m, e, v), + ii_ctor(ctor, nm, tps, parent_id) => { + visit::visit_class_ctor_helper(ctor, nm, tps, parent_id, e, v); + } ii_dtor(dtor, _, tps, parent_id) => { visit::visit_class_dtor_helper(dtor, tps, parent_id, e, v); } @@ -490,6 +495,12 @@ fn id_visitor(vfn: fn@(node_id)) -> visit::vt<()> { vfn(id); match fk { + visit::fk_ctor(_, _, tps, self_id, parent_id) => { + for vec::each(tps) |tp| { vfn(tp.id); } + vfn(id); + vfn(self_id); + vfn(parent_id.node); + } visit::fk_dtor(tps, _, self_id, parent_id) => { for vec::each(tps) |tp| { vfn(tp.id); } vfn(id); diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 68d9cd80430..088df01985e 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -271,6 +271,23 @@ fn noop_fold_item_underscore(i: item_, fld: ast_fold) -> item_ { fn fold_struct_def(struct_def: @ast::struct_def, fld: ast_fold) -> @ast::struct_def { + let resulting_optional_constructor; + match struct_def.ctor { + None => { + resulting_optional_constructor = None; + } + Some(constructor) => { + resulting_optional_constructor = Some({ + node: { + body: fld.fold_block(constructor.node.body), + dec: fold_fn_decl(constructor.node.dec, fld), + id: fld.new_id(constructor.node.id), + .. constructor.node + }, + .. constructor + }); + } + } let dtor = do option::map(&struct_def.dtor) |dtor| { let dtor_body = fld.fold_block(dtor.node.body); let dtor_id = fld.new_id(dtor.node.id); @@ -281,6 +298,7 @@ fn fold_struct_def(struct_def: @ast::struct_def, fld: ast_fold) traits: vec::map(struct_def.traits, |p| fold_trait_ref(*p, fld)), fields: vec::map(struct_def.fields, |f| fold_struct_field(*f, fld)), methods: vec::map(struct_def.methods, |m| fld.fold_method(*m)), + ctor: resulting_optional_constructor, dtor: dtor }; } @@ -567,6 +585,7 @@ fn noop_fold_variant(v: variant_, fld: ast_fold) -> variant_ { |f| fld.fold_struct_field(*f)), methods: vec::map(struct_def.methods, |m| fld.fold_method(*m)), + ctor: None, dtor: dtor }) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f2e17c0a7e6..22c25186c91 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -115,7 +115,8 @@ enum class_member { So that we can distinguish a class ctor or dtor from other class members */ -enum class_contents { dtor_decl(blk, ~[attribute], codemap::span), +enum class_contents { ctor_decl(fn_decl, ~[attribute], blk, codemap::span), + dtor_decl(blk, ~[attribute], codemap::span), members(~[@class_member]) } type arg_or_capture_item = Either<arg, capture_item>; @@ -2682,13 +2683,30 @@ impl parser { let mut fields: ~[@struct_field]; let mut methods: ~[@method] = ~[]; + let mut the_ctor: Option<(fn_decl, ~[attribute], blk, codemap::span)> + = None; let mut the_dtor: Option<(blk, ~[attribute], codemap::span)> = None; + let ctor_id = self.get_id(); if self.eat(token::LBRACE) { // It's a record-like struct. fields = ~[]; while self.token != token::RBRACE { match self.parse_class_item() { + ctor_decl(a_fn_decl, attrs, blk, s) => { + match the_ctor { + Some((_, _, _, s_first)) => { + self.span_note(s, #fmt("Duplicate constructor \ + declaration for class %s", + *self.interner.get(class_name))); + self.span_fatal(copy s_first, ~"First constructor \ + declared here"); + } + None => { + the_ctor = Some((a_fn_decl, attrs, blk, s)); + } + } + } dtor_decl(blk, attrs, s) => { match the_dtor { Some((_, _, s_first)) => { @@ -2746,14 +2764,36 @@ impl parser { self_id: self.get_id(), body: d_body}, span: d_s}}; - (class_name, - item_class(@{ - traits: traits, - fields: move fields, - methods: move methods, - dtor: actual_dtor - }, ty_params), - None) + match the_ctor { + Some((ct_d, ct_attrs, ct_b, ct_s)) => { + (class_name, + item_class(@{ + traits: traits, + fields: move fields, + methods: move methods, + ctor: Some({ + node: {id: ctor_id, + attrs: ct_attrs, + self_id: self.get_id(), + dec: ct_d, + body: ct_b}, + span: ct_s}), + dtor: actual_dtor + }, ty_params), + None) + } + None => { + (class_name, + item_class(@{ + traits: traits, + fields: move fields, + methods: move methods, + ctor: None, + dtor: actual_dtor + }, ty_params), + None) + } + } } fn token_is_pound_or_doc_comment(++tok: token::token) -> bool { @@ -3057,6 +3097,12 @@ impl parser { let mut methods: ~[@method] = ~[]; while self.token != token::RBRACE { match self.parse_class_item() { + ctor_decl(*) => { + self.span_fatal(copy self.span, + ~"deprecated explicit \ + constructors are not allowed \ + here"); + } dtor_decl(blk, attrs, s) => { match the_dtor { Some((_, _, s_first)) => { @@ -3097,6 +3143,7 @@ impl parser { traits: ~[], fields: move fields, methods: move methods, + ctor: None, dtor: actual_dtor }; } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index b98014f421b..bff356e5cb7 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -653,6 +653,18 @@ fn print_struct(s: ps, struct_def: @ast::struct_def, tps: ~[ast::ty_param], } bopen(s); hardbreak_if_not_bol(s); + do struct_def.ctor.iter |ctor| { + maybe_print_comment(s, ctor.span.lo); + print_outer_attributes(s, ctor.node.attrs); + // Doesn't call head because there shouldn't be a space after new. + cbox(s, indent_unit); + ibox(s, 4); + word(s.s, ~"new("); + print_fn_args(s, ctor.node.dec, ~[], None); + word(s.s, ~")"); + space(s.s); + print_block(s, ctor.node.body); + } do struct_def.dtor.iter |dtor| { hardbreak_if_not_bol(s); maybe_print_comment(s, dtor.span.lo); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 50fbd21f7b8..e6fd65eb458 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -17,6 +17,8 @@ enum fn_kind { fk_method(ident, ~[ty_param], @method), fk_anon(proto, capture_clause), //< an anonymous function like fn@(...) fk_fn_block(capture_clause), //< a block {||...} + fk_ctor(ident, ~[attribute], ~[ty_param], node_id /* self id */, + def_id /* parent class id */), // class constructor fk_dtor(~[ty_param], ~[attribute], node_id /* self id */, def_id /* parent class id */) // class destructor @@ -24,9 +26,8 @@ enum fn_kind { fn name_of_fn(fk: fn_kind) -> ident { match fk { - fk_item_fn(name, _, _) | fk_method(name, _, _) => { - /* FIXME (#2543) */ copy name - } + fk_item_fn(name, _, _) | fk_method(name, _, _) + | fk_ctor(name, _, _, _, _) => /* FIXME (#2543) */ copy name, fk_anon(*) | fk_fn_block(*) => parse::token::special_idents::anon, fk_dtor(*) => parse::token::special_idents::dtor } @@ -34,11 +35,11 @@ fn name_of_fn(fk: fn_kind) -> ident { fn tps_of_fn(fk: fn_kind) -> ~[ty_param] { match fk { - fk_item_fn(_, tps, _) | fk_method(_, tps, _) | - fk_dtor(tps, _, _, _) => { - /* FIXME (#2543) */ copy tps - } - fk_anon(*) | fk_fn_block(*) => ~[] + fk_item_fn(_, tps, _) | fk_method(_, tps, _) + | fk_ctor(_, _, tps, _, _) | fk_dtor(tps, _, _, _) => { + /* FIXME (#2543) */ copy tps + } + fk_anon(*) | fk_fn_block(*) => ~[] } } @@ -290,6 +291,17 @@ fn visit_method_helper<E>(m: @method, e: E, v: vt<E>) { m.decl, m.body, m.span, m.id, e, v); } +// Similar logic to the comment on visit_method_helper - Tim +fn visit_class_ctor_helper<E>(ctor: class_ctor, nm: ident, tps: ~[ty_param], + parent_id: def_id, e: E, v: vt<E>) { + v.visit_fn(fk_ctor(/* FIXME (#2543) */ copy nm, + ctor.node.attrs, + /* FIXME (#2543) */ copy tps, + ctor.node.self_id, parent_id), + ctor.node.dec, ctor.node.body, ctor.span, ctor.node.id, e, v) + +} + fn visit_class_dtor_helper<E>(dtor: class_dtor, tps: ~[ty_param], parent_id: def_id, e: E, v: vt<E>) { v.visit_fn(fk_dtor(/* FIXME (#2543) */ copy tps, dtor.node.attrs, @@ -318,7 +330,7 @@ fn visit_trait_method<E>(m: trait_method, e: E, v: vt<E>) { } } -fn visit_struct_def<E>(sd: @struct_def, _nm: ast::ident, tps: ~[ty_param], +fn visit_struct_def<E>(sd: @struct_def, nm: ast::ident, tps: ~[ty_param], id: node_id, e: E, v: vt<E>) { for sd.fields.each |f| { v.visit_struct_field(*f, e, v); @@ -329,6 +341,9 @@ fn visit_struct_def<E>(sd: @struct_def, _nm: ast::ident, tps: ~[ty_param], for sd.traits.each |p| { visit_path(p.path, e, v); } + do option::iter(&sd.ctor) |ctor| { + visit_class_ctor_helper(*ctor, nm, tps, ast_util::local_def(id), e, v); + }; do option::iter(&sd.dtor) |dtor| { visit_class_dtor_helper(*dtor, tps, ast_util::local_def(id), e, v) }; diff --git a/src/rustc/metadata/decoder.rs b/src/rustc/metadata/decoder.rs index 70a47522f37..197e567ab2f 100644 --- a/src/rustc/metadata/decoder.rs +++ b/src/rustc/metadata/decoder.rs @@ -123,6 +123,7 @@ enum Family { Variant, // v Impl, // i Trait, // I + Class, // C Struct, // S PublicField, // g PrivateField, // j @@ -155,6 +156,7 @@ fn item_family(item: ebml2::Doc) -> Family { 'v' => Variant, 'i' => Impl, 'I' => Trait, + 'C' => Class, 'S' => Struct, 'g' => PublicField, 'j' => PrivateField, @@ -298,7 +300,8 @@ fn item_to_def_like(item: ebml2::Doc, did: ast::def_id, cnum: ast::crate_num) let fam = item_family(item); match fam { Const => dl_def(ast::def_const(did)), - Struct => dl_def(ast::def_class(did)), + Class => dl_def(ast::def_class(did, true)), + Struct => dl_def(ast::def_class(did, false)), UnsafeFn => dl_def(ast::def_fn(did, ast::unsafe_fn)), Fn => dl_def(ast::def_fn(did, ast::impure_fn)), PureFn => dl_def(ast::def_fn(did, ast::pure_fn)), @@ -819,6 +822,7 @@ fn item_family_to_str(fam: Family) -> ~str { Variant => ~"variant", Impl => ~"impl", Trait => ~"trait", + Class => ~"class", Struct => ~"struct", PublicField => ~"public field", PrivateField => ~"private field", diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index ce3f50e7184..95696a14156 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -635,7 +635,12 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml2::Serializer, /* Now, make an item for the class itself */ ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, local_def(item.id)); - encode_family(ebml_w, 'S'); + + match struct_def.ctor { + None => encode_family(ebml_w, 'S'), + Some(_) => encode_family(ebml_w, 'C') + } + encode_type_param_bounds(ebml_w, ecx, tps); encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id)); encode_name(ecx, ebml_w, item.ident); @@ -694,6 +699,21 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml2::Serializer, let bkts = create_index(idx); encode_index(ebml_w, bkts, write_int); ebml_w.end_tag(); + + /* Encode the constructor */ + for struct_def.ctor.each |ctor| { + debug!("encoding info for ctor %s %d", + ecx.tcx.sess.str_of(item.ident), ctor.node.id); + index.push({ + val: ctor.node.id, + pos: ebml_w.writer.tell() + }); + encode_info_for_ctor(ecx, ebml_w, ctor.node.id, item.ident, + path, if tps.len() > 0u { + Some(ii_ctor(*ctor, item.ident, tps, + local_def(item.id))) } + else { None }, tps); + } } item_impl(tps, opt_trait, _, methods) => { add_to_index(); diff --git a/src/rustc/middle/astencode.rs b/src/rustc/middle/astencode.rs index 95c3d08f60e..39ec58c079e 100644 --- a/src/rustc/middle/astencode.rs +++ b/src/rustc/middle/astencode.rs @@ -262,6 +262,13 @@ fn simplify_ast(ii: ast::inlined_item) -> ast::inlined_item { ast::ii_foreign(i) => { ast::ii_foreign(fld.fold_foreign_item(i)) } + ast::ii_ctor(ctor, nm, tps, parent_id) => { + let ctor_body = fld.fold_block(ctor.node.body); + let ctor_decl = fold::fold_fn_decl(ctor.node.dec, fld); + ast::ii_ctor({node: {body: ctor_body, dec: ctor_decl, + .. ctor.node}, + .. ctor}, nm, tps, parent_id) + } ast::ii_dtor(dtor, nm, tps, parent_id) => { let dtor_body = fld.fold_block(dtor.node.body); ast::ii_dtor({node: {body: dtor_body, @@ -295,6 +302,18 @@ fn renumber_ast(xcx: extended_decode_ctxt, ii: ast::inlined_item) ast::ii_foreign(i) => { ast::ii_foreign(fld.fold_foreign_item(i)) } + ast::ii_ctor(ctor, nm, tps, parent_id) => { + let ctor_body = fld.fold_block(ctor.node.body); + let ctor_attrs = fld.fold_attributes(ctor.node.attrs); + let ctor_decl = fold::fold_fn_decl(ctor.node.dec, fld); + let new_params = fold::fold_ty_params(tps, fld); + let ctor_id = fld.new_id(ctor.node.id); + let new_parent = xcx.tr_def_id(parent_id); + ast::ii_ctor({node: {body: ctor_body, attrs: ctor_attrs, + dec: ctor_decl, id: ctor_id, + .. ctor.node}, + .. ctor}, nm, new_params, new_parent) + } ast::ii_dtor(dtor, nm, tps, parent_id) => { let dtor_body = fld.fold_block(dtor.node.body); let dtor_attrs = fld.fold_attributes(dtor.node.attrs); @@ -350,8 +369,8 @@ impl ast::def: tr { xcx.tr_id(nid2), xcx.tr_id(nid3)) } - ast::def_class(did) => { - ast::def_class(did.tr(xcx)) + ast::def_class(did, has_constructor) => { + ast::def_class(did.tr(xcx), has_constructor) } ast::def_region(nid) => ast::def_region(xcx.tr_id(nid)), ast::def_typaram_binder(nid) => { diff --git a/src/rustc/middle/borrowck/check_loans.rs b/src/rustc/middle/borrowck/check_loans.rs index 6a9195b4509..cc8d89a8ace 100644 --- a/src/rustc/middle/borrowck/check_loans.rs +++ b/src/rustc/middle/borrowck/check_loans.rs @@ -17,6 +17,10 @@ enum check_loan_ctxt = @{ reported: HashMap<ast::node_id, ()>, + // Keep track of whether we're inside a ctor, so as to + // allow mutating immutable fields in the same class if + // we are in a ctor, we track the self id + mut in_ctor: bool, mut declared_purity: ast::purity, mut fn_args: @~[ast::node_id] }; @@ -58,6 +62,7 @@ fn check_loans(bccx: borrowck_ctxt, let clcx = check_loan_ctxt(@{bccx: bccx, req_maps: req_maps, reported: HashMap(), + mut in_ctor: false, mut declared_purity: ast::impure_fn, mut fn_args: @~[]}); let vt = visit::mk_vt(@{visit_expr: check_loans_in_expr, @@ -315,7 +320,10 @@ impl check_loan_ctxt { debug!("check_assignment(cmt=%s)", self.bccx.cmt_to_repr(cmt)); - if self.is_local_variable(cmt) && at.checked_by_liveness() { + if self.in_ctor && self.is_self_field(cmt) + && at.checked_by_liveness() { + // assigning to self.foo in a ctor is always allowed. + } else if self.is_local_variable(cmt) && at.checked_by_liveness() { // liveness guarantees that immutable local variables // are only assigned once } else { @@ -534,28 +542,42 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk, visitor: visit::vt<check_loan_ctxt>) { debug!("purity on entry=%?", copy self.declared_purity); - do save_and_restore(&mut(self.declared_purity)) { - do save_and_restore(&mut(self.fn_args)) { - let is_stack_closure = self.is_stack_closure(id); - let fty = ty::node_id_to_type(self.tcx(), id); - self.declared_purity = ty::determine_inherited_purity( - copy self.declared_purity, - ty::ty_fn_purity(fty), - ty::ty_fn_proto(fty)); - - match fk { - visit::fk_anon(*) | - visit::fk_fn_block(*) if is_stack_closure => { + do save_and_restore(&mut(self.in_ctor)) { + do save_and_restore(&mut(self.declared_purity)) { + do save_and_restore(&mut(self.fn_args)) { + let is_stack_closure = self.is_stack_closure(id); + let fty = ty::node_id_to_type(self.tcx(), id); + self.declared_purity = ty::determine_inherited_purity( + copy self.declared_purity, + ty::ty_fn_purity(fty), + ty::ty_fn_proto(fty)); + + // In principle, we could consider fk_anon(*) or + // fk_fn_block(*) to be in a ctor, I suppose, but the + // purpose of the in_ctor flag is to allow modifications + // of otherwise immutable fields and typestate wouldn't be + // able to "see" into those functions anyway, so it + // wouldn't be very helpful. + match fk { + visit::fk_ctor(*) => { + self.in_ctor = true; + self.fn_args = @decl.inputs.map(|i| i.id ); + } + visit::fk_anon(*) | + visit::fk_fn_block(*) if is_stack_closure => { + self.in_ctor = false; // inherits the fn_args from enclosing ctxt - } - visit::fk_anon(*) | visit::fk_fn_block(*) | - visit::fk_method(*) | visit::fk_item_fn(*) | - visit::fk_dtor(*) => { + } + visit::fk_anon(*) | visit::fk_fn_block(*) | + visit::fk_method(*) | visit::fk_item_fn(*) | + visit::fk_dtor(*) => { + self.in_ctor = false; self.fn_args = @decl.inputs.map(|i| i.id ); + } } - } - visit::visit_fn(fk, decl, body, sp, id, self, visitor); + visit::visit_fn(fk, decl, body, sp, id, self, visitor); + } } } debug!("purity on exit=%?", copy self.declared_purity); diff --git a/src/rustc/middle/borrowck/gather_loans.rs b/src/rustc/middle/borrowck/gather_loans.rs index 2a23ec86c51..5dfde8c9af6 100644 --- a/src/rustc/middle/borrowck/gather_loans.rs +++ b/src/rustc/middle/borrowck/gather_loans.rs @@ -71,11 +71,11 @@ fn req_loans_in_fn(fk: visit::fn_kind, self.root_ub = body.node.id; match fk { - visit::fk_anon(*) | visit::fk_fn_block(*) => {} - visit::fk_item_fn(*) | visit::fk_method(*) | - visit::fk_dtor(*) => { - self.item_ub = body.node.id; - } + visit::fk_anon(*) | visit::fk_fn_block(*) => {} + visit::fk_item_fn(*) | visit::fk_method(*) | + visit::fk_ctor(*) | visit::fk_dtor(*) => { + self.item_ub = body.node.id; + } } visit::visit_fn(fk, decl, body, sp, id, self, v); diff --git a/src/rustc/middle/kind.rs b/src/rustc/middle/kind.rs index 7b473cedb4c..e2b85441a8f 100644 --- a/src/rustc/middle/kind.rs +++ b/src/rustc/middle/kind.rs @@ -181,9 +181,9 @@ fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span, // variables. This list is used below to avoid checking and reporting // on a given variable twice. let cap_clause = match fk { - visit::fk_anon(_, cc) | visit::fk_fn_block(cc) => cc, - visit::fk_item_fn(*) | visit::fk_method(*) | - visit::fk_dtor(*) => @~[] + visit::fk_anon(_, cc) | visit::fk_fn_block(cc) => cc, + visit::fk_item_fn(*) | visit::fk_method(*) | + visit::fk_ctor(*) | visit::fk_dtor(*) => @~[] }; let captured_vars = do (*cap_clause).map |cap_item| { let cap_def = cx.tcx.def_map.get(cap_item.id); diff --git a/src/rustc/middle/liveness.rs b/src/rustc/middle/liveness.rs index 86f786e7706..12d63cdacbf 100644 --- a/src/rustc/middle/liveness.rs +++ b/src/rustc/middle/liveness.rs @@ -35,6 +35,12 @@ * Any use of the variable where the variable is dead afterwards is a * last use. * + * # Extension to handle constructors + * + * Each field is assigned an index just as with local variables. A use of + * `self` is considered a use of all fields. A use of `self.f` is just a use + * of `f`. + * * # Implementation details * * The actual implementation contains two (nested) walks over the AST. @@ -90,6 +96,8 @@ * - `no_ret_var`: a synthetic variable that is only 'read' from, the * fallthrough node. This allows us to detect functions where we fail * to return explicitly. + * + * - `self_var`: a variable representing 'self' */ use dvec::DVec; @@ -222,7 +230,7 @@ impl LiveNode { fn invalid_node() -> LiveNode { LiveNode(uint::max_value) } -enum RelevantDef { RelevantVar(node_id) } +enum RelevantDef { RelevantVar(node_id), RelevantSelf } type CaptureInfo = {ln: LiveNode, is_move: bool, rv: RelevantDef}; @@ -242,12 +250,15 @@ struct LocalInfo { enum VarKind { Arg(node_id, ident, rmode), Local(LocalInfo), + Field(ident), Self, ImplicitRet } fn relevant_def(def: def) -> Option<RelevantDef> { match def { + def_self(_) => Some(RelevantSelf), + def_binding(nid, _) | def_arg(nid, _) | def_local(nid, _) => Some(RelevantVar(nid)), @@ -265,6 +276,7 @@ struct IrMaps { mut num_vars: uint, live_node_map: HashMap<node_id, LiveNode>, variable_map: HashMap<node_id, Variable>, + field_map: HashMap<ident, Variable>, capture_map: HashMap<node_id, @~[CaptureInfo]>, mut var_kinds: ~[VarKind], mut lnks: ~[LiveNodeKind], @@ -281,6 +293,7 @@ fn IrMaps(tcx: ty::ctxt, method_map: typeck::method_map, live_node_map: HashMap(), variable_map: HashMap(), capture_map: HashMap(), + field_map: HashMap(), var_kinds: ~[], lnks: ~[] } @@ -310,12 +323,15 @@ impl IrMaps { self.num_vars += 1u; match vk { - Local(LocalInfo {id:node_id, _}) | - Arg(node_id, _, _) => { - self.variable_map.insert(node_id, v); - } - Self | ImplicitRet => { - } + Local(LocalInfo {id:node_id, _}) | + Arg(node_id, _, _) => { + self.variable_map.insert(node_id, v); + } + Field(name) => { + self.field_map.insert(name, v); + } + Self | ImplicitRet => { + } } debug!("%s is %?", v.to_str(), vk); @@ -335,10 +351,11 @@ impl IrMaps { fn variable_name(var: Variable) -> ~str { match copy self.var_kinds[*var] { - Local(LocalInfo {ident: nm, _}) | - Arg(_, nm, _) => self.tcx.sess.str_of(nm), - Self => ~"self", - ImplicitRet => ~"<implicit-ret>" + Local(LocalInfo {ident: nm, _}) | + Arg(_, nm, _) => self.tcx.sess.str_of(nm), + Field(nm) => ~"self." + self.tcx.sess.str_of(nm), + Self => ~"self", + ImplicitRet => ~"<implicit-ret>" } } @@ -382,7 +399,7 @@ impl IrMaps { (*v).push(id); } Arg(_, _, by_ref) | - Arg(_, _, by_val) | Self | ImplicitRet | + Arg(_, _, by_val) | Self | Field(_) | ImplicitRet | Local(LocalInfo {kind: FromMatch(bind_by_implicit_ref), _}) => { debug!("--but it is not owned"); } @@ -411,6 +428,13 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, // and so forth: visit::visit_fn(fk, decl, body, sp, id, fn_maps, v); + match fk { + visit::fk_ctor(_, _, _, _, class_did) => { + add_class_fields(fn_maps, class_did); + } + _ => {} + } + // Special nodes and variables: // - exit_ln represents the end of the fn, either by return or fail // - implicit_ret_var is a pseudo-variable that represents @@ -418,7 +442,8 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, let specials = { exit_ln: (*fn_maps).add_live_node(ExitNode), fallthrough_ln: (*fn_maps).add_live_node(ExitNode), - no_ret_var: (*fn_maps).add_variable(ImplicitRet) + no_ret_var: (*fn_maps).add_variable(ImplicitRet), + self_var: (*fn_maps).add_variable(Self) }; // compute liveness @@ -435,9 +460,18 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, }); check_vt.visit_block(body, lsets, check_vt); lsets.check_ret(id, sp, fk, entry_ln); + lsets.check_fields(sp, entry_ln); lsets.warn_about_unused_args(sp, decl, entry_ln); } +fn add_class_fields(self: @IrMaps, did: def_id) { + for ty::lookup_class_fields(self.tcx, did).each |field_ty| { + assert field_ty.id.crate == local_crate; + let var = self.add_variable(Field(field_ty.ident)); + self.field_map.insert(field_ty.ident, var); + } +} + fn visit_local(local: @local, &&self: @IrMaps, vt: vt<@IrMaps>) { let def_map = self.tcx.def_map; do pat_util::pat_bindings(def_map, local.node.pat) |_bm, p_id, sp, path| { @@ -561,7 +595,8 @@ fn invalid_users() -> users { type Specials = { exit_ln: LiveNode, fallthrough_ln: LiveNode, - no_ret_var: Variable + no_ret_var: Variable, + self_var: Variable }; const ACC_READ: uint = 1u; @@ -614,6 +649,7 @@ impl Liveness { fn variable_from_rdef(rv: RelevantDef, span: span) -> Variable { match rv { + RelevantSelf => self.s.self_var, RelevantVar(nid) => self.variable(nid, span) } } @@ -898,6 +934,14 @@ impl Liveness { } } + // as above, the "self" variable is a non-owned variable + self.acc(self.s.exit_ln, self.s.self_var, ACC_READ); + + // in a ctor, there is an implicit use of self.f for all fields f: + for self.ir.field_map.each_value |var| { + self.acc(self.s.exit_ln, var, ACC_READ|ACC_USE); + } + // the fallthrough exit is only for those cases where we do not // explicitly return: self.init_from_succ(self.s.fallthrough_ln, self.s.exit_ln); @@ -979,11 +1023,24 @@ impl Liveness { // Interesting cases with control flow or which gen/kill expr_path(_) => { - self.access_path(expr, succ, ACC_READ | ACC_USE) + self.access_path(expr, succ, ACC_READ | ACC_USE) } - expr_field(e, _, _) => { - self.propagate_through_expr(e, succ) + expr_field(e, nm, _) => { + // If this is a reference to `self.f` inside of a ctor, + // then we treat it as a read of that variable. + // Otherwise, we ignore it and just propagate down to + // process `e`. + match self.as_self_field(e, nm) { + Some((ln, var)) => { + self.init_from_succ(ln, succ); + self.acc(ln, var, ACC_READ | ACC_USE); + ln + } + None => { + self.propagate_through_expr(e, succ) + } + } } expr_fn(*) | expr_fn_block(*) => { @@ -1212,8 +1269,8 @@ impl Liveness { // In general, the full flow graph structure for an // assignment/move/etc can be handled in one of two ways, // depending on whether what is being assigned is a "tracked - // value" or not. A tracked value is basically a local - // variable or argument. + // value" or not. A tracked value is basically a local variable + // or argument, or a self-field (`self.f`) in a ctor. // // The two kinds of graphs are: // @@ -1236,11 +1293,12 @@ impl Liveness { // // # Tracked lvalues // - // A tracked lvalue is a local variable/argument `x`. In + // A tracked lvalue is either a local variable/argument `x` or + // else it is a self-field `self.f` in a constructor. In // these cases, the link_node where the write occurs is linked - // to node id of `x`. The `write_lvalue()` routine generates - // the contents of this node. There are no subcomponents to - // consider. + // to node id of `x` or `self`, respectively. The + // `write_lvalue()` routine generates the contents of this + // node. There are no subcomponents to consider. // // # Non-tracked lvalues // @@ -1257,9 +1315,12 @@ impl Liveness { // just ignore such cases and treat them as reads. match expr.node { - expr_path(_) => succ, - expr_field(e, _, _) => self.propagate_through_expr(e, succ), - _ => self.propagate_through_expr(expr, succ) + expr_path(_) => succ, + expr_field(e, nm, _) => match self.as_self_field(e, nm) { + Some(_) => succ, + None => self.propagate_through_expr(e, succ) + }, + _ => self.propagate_through_expr(expr, succ) } } @@ -1269,6 +1330,14 @@ impl Liveness { acc: uint) -> LiveNode { match expr.node { expr_path(_) => self.access_path(expr, succ, acc), + expr_field(e, nm, _) => match self.as_self_field(e, nm) { + Some((ln, var)) => { + self.init_from_succ(ln, succ); + self.acc(ln, var, acc); + ln + } + None => succ + }, // We do not track other lvalues, so just propagate through // to their subcomponents. Also, it may happen that @@ -1281,6 +1350,26 @@ impl Liveness { fn access_path(expr: @expr, succ: LiveNode, acc: uint) -> LiveNode { let def = self.tcx.def_map.get(expr.id); match relevant_def(def) { + Some(RelevantSelf) => { + // Accessing `self` is like accessing every field of + // the current object. This allows something like + // `self = ...;` (it will be considered a write to + // every field, sensibly enough), though the borrowck + // pass will reject it later on. + // + // Also, note that, within a ctor at least, an + // expression like `self.f` is "shortcircuiting" + // before it reaches this point by the code for + // expr_field. + let ln = self.live_node(expr.id, expr.span); + if acc != 0u { + self.init_from_succ(ln, succ); + for self.ir.field_map.each_value |var| { + self.acc(ln, var, acc); + } + } + ln + } Some(RelevantVar(nid)) => { let ln = self.live_node(expr.id, expr.span); if acc != 0u { @@ -1294,6 +1383,29 @@ impl Liveness { } } + fn as_self_field(expr: @expr, + fld: ident) -> Option<(LiveNode,Variable)> { + // If we checking a constructor, then we treat self.f as a + // variable. we use the live_node id that will be assigned to + // the reference to self but the variable id for `f`. + match expr.node { + expr_path(_) => { + let def = self.tcx.def_map.get(expr.id); + match def { + def_self(_) => { + // Note: the field_map is empty unless we are in a ctor + return self.ir.field_map.find(fld).map(|var| { + let ln = self.live_node(expr.id, expr.span); + (ln, *var) + }); + } + _ => return None + } + } + _ => return None + } + } + fn propagate_through_loop(expr: @expr, cond: Option<@expr>, body: blk, @@ -1501,7 +1613,24 @@ enum ReadKind { } impl @Liveness { - fn check_ret(id: node_id, sp: span, _fk: visit::fn_kind, + fn check_fields(sp: span, entry_ln: LiveNode) { + for self.ir.field_map.each |nm, var| { + match self.live_on_entry(entry_ln, var) { + None => { /* ok */ } + Some(ExitNode) => { + self.tcx.sess.span_err( + sp, fmt!("field `self.%s` is never initialized", + self.tcx.sess.str_of(nm))); + } + Some(lnk) => { + self.report_illegal_read( + sp, lnk, var, PossiblyUninitializedField); + } + } + } + } + + fn check_ret(id: node_id, sp: span, fk: visit::fn_kind, entry_ln: LiveNode) { if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() { // if no_ret_var is live, then we fall off the end of the @@ -1515,8 +1644,15 @@ impl @Liveness { self.tcx.sess.span_err( sp, ~"some control paths may return"); } else { - self.tcx.sess.span_err( - sp, ~"not all control paths return a value"); + match fk { + visit::fk_ctor(*) => { + // ctors are written as though they are unit. + } + _ => { + self.tcx.sess.span_err( + sp, ~"not all control paths return a value"); + } + } } } } @@ -1601,6 +1737,7 @@ impl @Liveness { let var = self.variable(nid, expr.span); self.warn_about_dead_assign(expr.span, ln, var); } + Some(RelevantSelf) => {} None => {} } } @@ -1659,6 +1796,13 @@ impl @Liveness { copy or move mode", self.tcx.sess.str_of(name))); return; } + Field(name) => { + self.tcx.sess.span_err( + move_span, + fmt!("illegal move from field `%s`", + self.tcx.sess.str_of(name))); + return; + } Self => { self.tcx.sess.span_err( move_span, diff --git a/src/rustc/middle/region.rs b/src/rustc/middle/region.rs index eb0bf8796f0..ff708b7f4ef 100644 --- a/src/rustc/middle/region.rs +++ b/src/rustc/middle/region.rs @@ -305,16 +305,16 @@ fn resolve_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk, visitor: visit::vt<ctxt>) { let fn_cx = match fk { - visit::fk_item_fn(*) | visit::fk_method(*) | - visit::fk_dtor(*) => { - // Top-level functions are a root scope. - ctxt {parent: Some(id),.. cx} - } + visit::fk_item_fn(*) | visit::fk_method(*) | + visit::fk_ctor(*) | visit::fk_dtor(*) => { + // Top-level functions are a root scope. + ctxt {parent: Some(id),.. cx} + } - visit::fk_anon(*) | visit::fk_fn_block(*) => { - // Closures continue with the inherited scope. - cx - } + visit::fk_anon(*) | visit::fk_fn_block(*) => { + // Closures continue with the inherited scope. + cx + } }; debug!("visiting fn with body %d. cx.parent: %? \ @@ -641,7 +641,7 @@ fn determine_rp_in_ty(ty: @ast::ty, match ty.node { ast::ty_path(path, id) => { match cx.def_map.get(id) { - ast::def_ty(did) | ast::def_class(did) => { + ast::def_ty(did) | ast::def_class(did, _) => { if did.crate == ast::local_crate { if cx.opt_region_is_relevant(path.rp) { cx.add_dep(did.node); diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs index b455cc257ef..5f30346a28e 100644 --- a/src/rustc/middle/resolve.rs +++ b/src/rustc/middle/resolve.rs @@ -757,7 +757,7 @@ struct Resolver { unused_import_lint_level: level, trait_info: HashMap<def_id,@HashMap<ident,()>>, - structs: HashMap<def_id,()>, + structs: HashMap<def_id,bool>, // The number of imports that are currently unresolved. mut unresolved_imports: uint, @@ -1069,15 +1069,36 @@ impl Resolver { } // These items live in both the type and value namespaces. - item_class(*) => { - let (name_bindings, new_parent) = - self.add_child(ident, parent, ~[TypeNS], sp); + item_class(struct_definition, _) => { + let new_parent = + match struct_definition.ctor { + None => { + let (name_bindings, new_parent) = + self.add_child(ident, parent, ~[TypeNS], sp); - (*name_bindings).define_type - (privacy, def_ty(local_def(item.id)), sp); + (*name_bindings).define_type + (privacy, def_ty(local_def(item.id)), sp); + new_parent + } + Some(ctor) => { + let (name_bindings, new_parent) = + self.add_child(ident, parent, ~[ValueNS, TypeNS], + sp); + + (*name_bindings).define_type + (privacy, def_ty(local_def(item.id)), sp); + + let purity = impure_fn; + let ctor_def = def_fn(local_def(ctor.node.id), + purity); + (*name_bindings).define_value(privacy, ctor_def, sp); + new_parent + } + }; // Record the def ID of this struct. - self.structs.insert(local_def(item.id), ()); + self.structs.insert(local_def(item.id), + struct_definition.ctor.is_some()); visit_item(item, new_parent, visitor); } @@ -1162,7 +1183,7 @@ impl Resolver { def_variant(item_id, local_def(variant.node.id)), variant.span); - self.structs.insert(local_def(variant.node.id), ()); + self.structs.insert(local_def(variant.node.id), false); } enum_variant_kind(enum_definition) => { (*child).define_type(privacy, @@ -1499,12 +1520,18 @@ impl Resolver { child_name_bindings.define_type(Public, def, dummy_sp()); } - def_class(def_id) => { + def_class(def_id, has_constructor) => { debug!("(building reduced graph for external \ - crate) building type %s", - final_ident); + crate) building type %s (value? %d)", + final_ident, + if has_constructor { 1 } else { 0 }); child_name_bindings.define_type(Public, def, dummy_sp()); - self.structs.insert(def_id, ()); + + if has_constructor { + child_name_bindings.define_value(Public, def, dummy_sp()); + } + + self.structs.insert(def_id, has_constructor); } def_self(*) | def_arg(*) | def_local(*) | def_prim_ty(*) | def_ty_param(*) | def_binding(*) | @@ -3279,6 +3306,7 @@ impl Resolver { struct_def.traits, struct_def.fields, struct_def.methods, + struct_def.ctor, struct_def.dtor, visitor); } @@ -3520,6 +3548,7 @@ impl Resolver { traits: ~[@trait_ref], fields: ~[@struct_field], methods: ~[@method], + optional_constructor: Option<class_ctor>, optional_destructor: Option<class_dtor>, visitor: ResolveVisitor) { @@ -3571,6 +3600,23 @@ impl Resolver { self.resolve_type(field.node.ty, visitor); } + // Resolve the constructor, if applicable. + match optional_constructor { + None => { + // Nothing to do. + } + Some(constructor) => { + self.resolve_function(NormalRibKind, + Some(@constructor.node.dec), + NoTypeParameters, + constructor.node.body, + HasSelfBinding(constructor.node. + self_id), + NoCaptureClause, + visitor); + } + } + // Resolve the destructor, if applicable. match optional_destructor { None => { @@ -4043,7 +4089,9 @@ impl Resolver { match self.resolve_path(path, TypeNS, false, visitor) { Some(def_ty(class_id)) if self.structs.contains_key(class_id) => { - let class_def = def_class(class_id); + let has_constructor = self.structs.get(class_id); + let class_def = def_class(class_id, + has_constructor); self.record_def(pattern.id, class_def); } Some(definition @ def_variant(_, variant_id)) @@ -4511,9 +4559,10 @@ impl Resolver { // let bar = Bar { ... } // no type parameters match self.resolve_path(path, TypeNS, false, visitor) { - Some(def_ty(class_id)) | Some(def_class(class_id)) + Some(def_ty(class_id)) | Some(def_class(class_id, _)) if self.structs.contains_key(class_id) => { - let class_def = def_class(class_id); + let has_constructor = self.structs.get(class_id); + let class_def = def_class(class_id, has_constructor); self.record_def(expr.id, class_def); } Some(definition @ def_variant(_, class_id)) diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index c605fe39873..95711f8da36 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -1596,14 +1596,18 @@ fn trans_closure(ccx: @crate_ctxt, path: path, decl: ast::fn_decl, // translation calls that don't have a return value (trans_crate, // trans_mod, trans_item, et cetera) and those that do // (trans_block, trans_expr, et cetera). - if body.node.expr.is_none() || ty::type_is_bot(block_ty) || - ty::type_is_nil(block_ty) - { + + if !ccx.class_ctors.contains_key(id) // hack -- + /* avoids the need for special cases to assign a type to + the constructor body (since it has no explicit return) */ + && + (body.node.expr.is_none() || + ty::type_is_bot(block_ty) || + ty::type_is_nil(block_ty)) { bcx = controlflow::trans_block(bcx, body, expr::Ignore); } else { bcx = controlflow::trans_block(bcx, body, expr::SaveIn(fcx.llretptr)); } - finish(bcx); cleanup_and_Br(bcx, bcx_top, fcx.llreturn); @@ -1694,6 +1698,60 @@ fn trans_enum_variant(ccx: @crate_ctxt, finish_fn(fcx, lltop); } +fn trans_class_ctor(ccx: @crate_ctxt, path: path, decl: ast::fn_decl, + body: ast::blk, llctor_decl: ValueRef, + psubsts: param_substs, ctor_id: ast::node_id, + parent_id: ast::def_id, sp: span) { + // Add ctor to the ctor map + ccx.class_ctors.insert(ctor_id, parent_id); + + // Translate the ctor + + // Set up the type for the result of the ctor + // kludgy -- this wouldn't be necessary if the typechecker + // special-cased constructors, then we could just look up + // the ctor's return type. + let rslt_ty = ty::mk_class(ccx.tcx, parent_id, + dummy_substs(psubsts.tys)); + + // Make the fn context + let fcx = new_fn_ctxt_w_id(ccx, path, llctor_decl, ctor_id, + Some(psubsts), Some(sp)); + let raw_llargs = create_llargs_for_fn_args(fcx, no_self, decl.inputs); + let mut bcx_top = top_scope_block(fcx, body.info()); + let lltop = bcx_top.llbb; + let arg_tys = ty::ty_fn_args(node_id_type(bcx_top, ctor_id)); + bcx_top = copy_args_to_allocas(fcx, bcx_top, decl.inputs, + raw_llargs, arg_tys); + + // Create a temporary for `self` that we will return at the end + let selfdatum = datum::scratch_datum(bcx_top, rslt_ty, true); + + // Initialize dtor flag (if any) to 1 + if ty::ty_dtor(bcx_top.tcx(), parent_id).is_some() { + let flag = GEPi(bcx_top, selfdatum.val, [0, 1]); + Store(bcx_top, C_u8(1), flag); + } + + // initialize fields to zero + let mut bcx = bcx_top; + + // note we don't want to take *or* drop self. + fcx.llself = Some(ValSelfData {v: selfdatum.val, + t: rslt_ty, + is_owned: false}); + + // Translate the body of the ctor + bcx = controlflow::trans_block(bcx, body, expr::Ignore); + + // Generate the return expression + bcx = selfdatum.move_to(bcx, datum::INIT, fcx.llretptr); + + cleanup_and_leave(bcx, None, Some(fcx.llreturn)); + Unreachable(bcx); + finish_fn(fcx, lltop); +} + fn trans_class_dtor(ccx: @crate_ctxt, path: path, body: ast::blk, dtor_id: ast::node_id, psubsts: Option<param_substs>, @@ -1863,6 +1921,14 @@ fn trans_struct_def(ccx: @crate_ctxt, struct_def: @ast::struct_def, tps: ~[ast::ty_param], path: @ast_map::path, ident: ast::ident, id: ast::node_id) { if tps.len() == 0u { + let psubsts = {tys: ty::ty_params_to_tys(ccx.tcx, tps), + vtables: None, + bounds: @~[]}; + do option::iter(&struct_def.ctor) |ctor| { + trans_class_ctor(ccx, *path, ctor.node.dec, ctor.node.body, + get_item_val(ccx, ctor.node.id), psubsts, + ctor.node.id, local_def(id), ctor.span); + } do option::iter(&struct_def.dtor) |dtor| { trans_class_dtor(ccx, *path, dtor.node.body, dtor.node.id, None, None, local_def(id)); @@ -2118,6 +2184,10 @@ fn get_item_val(ccx: @crate_ctxt, id: ast::node_id) -> ValueRef { } } } + ast_map::node_ctor(nm, _, ctor, _, pt) => { + let my_path = vec::append(*pt, ~[path_name(nm)]); + register_fn(ccx, ctor.span, my_path, ctor.node.id) + } ast_map::node_dtor(_, dt, parent_id, pt) => { /* Don't just call register_fn, since we don't want to add @@ -2642,6 +2712,7 @@ fn trans_crate(sess: session::session, crate_map: crate_map, mut uses_gc: false, dbg_cx: dbg_cx, + class_ctors: HashMap(), mut do_not_commit_warning_issued: false}; diff --git a/src/rustc/middle/trans/common.rs b/src/rustc/middle/trans/common.rs index b87d690a1c3..68e957bfe70 100644 --- a/src/rustc/middle/trans/common.rs +++ b/src/rustc/middle/trans/common.rs @@ -170,6 +170,11 @@ type crate_ctxt = { // is not emitted by LLVM's GC pass when no functions use GC. mut uses_gc: bool, dbg_cx: Option<debuginfo::debug_ctxt>, + // Mapping from class constructors to parent class -- + // used in base::trans_closure + // parent_class must be a def_id because ctors can be + // inlined, so the parent may be in a different crate + class_ctors: HashMap<ast::node_id, ast::def_id>, mut do_not_commit_warning_issued: bool}; // Types used for llself. diff --git a/src/rustc/middle/trans/debuginfo.rs b/src/rustc/middle/trans/debuginfo.rs index 068ec49d6c7..6cd4b49fa3b 100644 --- a/src/rustc/middle/trans/debuginfo.rs +++ b/src/rustc/middle/trans/debuginfo.rs @@ -732,6 +732,10 @@ fn create_function(fcx: fn_ctxt) -> @metadata<subprogram_md> { ast_map::node_method(method, _, _) => { (method.ident, method.decl.output, method.id) } + ast_map::node_ctor(nm, _, ctor, _, _) => { + // FIXME: output type may be wrong (#2194) + (nm, ctor.node.dec.output, ctor.node.id) + } ast_map::node_expr(expr) => { match expr.node { ast::expr_fn(_, decl, _, _) => { diff --git a/src/rustc/middle/trans/inline.rs b/src/rustc/middle/trans/inline.rs index ce9088d4b55..76888471bf9 100644 --- a/src/rustc/middle/trans/inline.rs +++ b/src/rustc/middle/trans/inline.rs @@ -34,6 +34,10 @@ fn maybe_instantiate_inline(ccx: @crate_ctxt, fn_id: ast::def_id) trans_item(ccx, *item); local_def(item.id) } + csearch::found(ast::ii_ctor(ctor, _, _, _)) => { + ccx.external.insert(fn_id, Some(ctor.node.id)); + local_def(ctor.node.id) + } csearch::found(ast::ii_foreign(item)) => { ccx.external.insert(fn_id, Some(item.id)); local_def(item.id) diff --git a/src/rustc/middle/trans/monomorphize.rs b/src/rustc/middle/trans/monomorphize.rs index 32a581454d3..17eaf591c9f 100644 --- a/src/rustc/middle/trans/monomorphize.rs +++ b/src/rustc/middle/trans/monomorphize.rs @@ -5,7 +5,7 @@ use syntax::ast_map::{path, path_mod, path_name}; use base::{trans_item, get_item_val, no_self, self_arg, trans_fn, impl_self, decl_internal_cdecl_fn, set_inline_hint_if_appr, set_inline_hint, - trans_enum_variant, trans_class_dtor, + trans_enum_variant, trans_class_ctor, trans_class_dtor, get_insn_ctxt}; use syntax::parse::token::special_idents; use type_of::type_of_fn_from_ty; @@ -71,6 +71,7 @@ fn monomorphic_fn(ccx: @crate_ctxt, return {val: get_item_val(ccx, fn_id.node), must_cast: true}; } + ast_map::node_ctor(nm, _, ct, _, pt) => (pt, nm, ct.span), ast_map::node_dtor(_, dtor, _, pt) => (pt, special_idents::dtor, dtor.span), ast_map::node_trait_method(*) => { @@ -161,6 +162,16 @@ fn monomorphic_fn(ccx: @crate_ctxt, meth::trans_method(ccx, pt, mth, psubsts, None, d); d } + ast_map::node_ctor(_, tps, ctor, parent_id, _) => { + // ctors don't have attrs, at least not right now + let d = mk_lldecl(); + let tp_tys = ty::ty_params_to_tys(ccx.tcx, tps); + trans_class_ctor(ccx, pt, ctor.node.dec, ctor.node.body, d, + option::get_default(&psubsts, + {tys:tp_tys, vtables: None, bounds: @~[]}), + fn_id.node, parent_id, ctor.span); + d + } ast_map::node_dtor(_, dtor, _, pt) => { let parent_id = match ty::ty_to_def_id(ty::node_id_to_type(ccx.tcx, dtor.node.self_id)) { diff --git a/src/rustc/middle/trans/reachable.rs b/src/rustc/middle/trans/reachable.rs index bfb8de76a6c..3c4439c918f 100644 --- a/src/rustc/middle/trans/reachable.rs +++ b/src/rustc/middle/trans/reachable.rs @@ -59,6 +59,10 @@ fn traverse_def_id(cx: ctx, did: def_id) { cx.rmap.insert(item.id, ()); } ast_map::node_variant(v, _, _) => { cx.rmap.insert(v.node.id, ()); } + // If it's a ctor, consider the parent reachable + ast_map::node_ctor(_, _, _, parent_id, _) => { + traverse_def_id(cx, parent_id); + } _ => () } } @@ -100,6 +104,13 @@ fn traverse_public_item(cx: ctx, item: @item) { } } item_class(struct_def, tps) => { + do option::iter(&struct_def.ctor) |ctor| { + cx.rmap.insert(ctor.node.id, ()); + if tps.len() > 0u || attr::find_inline_attr(ctor.node.attrs) + != attr::ia_none { + traverse_inline_body(cx, ctor.node.body); + } + } do option::iter(&struct_def.dtor) |dtor| { cx.rmap.insert(dtor.node.id, ()); if tps.len() > 0u || attr::find_inline_attr(dtor.node.attrs) diff --git a/src/rustc/middle/trans/type_use.rs b/src/rustc/middle/trans/type_use.rs index 1f9ad20dd03..ee247eb5db7 100644 --- a/src/rustc/middle/trans/type_use.rs +++ b/src/rustc/middle/trans/type_use.rs @@ -109,6 +109,9 @@ fn type_uses_for(ccx: @crate_ctxt, fn_id: def_id, n_tps: uint) for uint::range(0u, n_tps) |n| { cx.uses[n] |= flags;} } } + ast_map::node_ctor(_, _, ctor, _, _) => { + handle_body(cx, ctor.node.body); + } ast_map::node_dtor(_, dtor, _, _) => { handle_body(cx, dtor.node.body); } diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index ff916d55b6f..bbc3a06fb67 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -3318,7 +3318,7 @@ fn note_and_explain_type_err(cx: ctxt, err: &type_err) { fn def_has_ty_params(def: ast::def) -> bool { match def { - ast::def_fn(_, _) | ast::def_variant(_, _) | ast::def_class(_) + ast::def_fn(_, _) | ast::def_variant(_, _) | ast::def_class(_, _) => true, _ => false } @@ -3492,6 +3492,9 @@ fn item_path(cx: ctxt, id: ast::def_id) -> ast_map::path { ast_map::path_name(variant.node.name)) } + ast_map::node_ctor(nm, _, _, _, path) => { + vec::append_one(*path, ast_map::path_name(nm)) + } ast_map::node_dtor(_, _, _, path) => { vec::append_one(*path, ast_map::path_name( syntax::parse::token::special_idents::literally_dtor)) diff --git a/src/rustc/middle/typeck/astconv.rs b/src/rustc/middle/typeck/astconv.rs index 389c1adb016..14797fcdd6b 100644 --- a/src/rustc/middle/typeck/astconv.rs +++ b/src/rustc/middle/typeck/astconv.rs @@ -323,7 +323,7 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope Copy Owned>( Some(d) => d }; match a_def { - ast::def_ty(did) | ast::def_class(did) => { + ast::def_ty(did) | ast::def_class(did, _) => { ast_path_to_ty(self, rscope, did, path, id).ty } ast::def_prim_ty(nty) => { diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index 1d921497ccf..7cc2c8b0ad7 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -451,6 +451,18 @@ fn check_struct(ccx: @crate_ctxt, struct_def: @ast::struct_def, let tcx = ccx.tcx; let self_ty = ty::node_id_to_type(tcx, id); + do option::iter(&struct_def.ctor) |ctor| { + let class_t = {self_ty: self_ty, + self_id: ctor.node.self_id, + def_id: local_def(id), + explicit_self: {node: ast::sty_by_ref, + span: ast_util::dummy_sp()}}; + // typecheck the ctor + check_bare_fn(ccx, ctor.node.dec, + ctor.node.body, ctor.node.id, + Some(class_t)); + } + do option::iter(&struct_def.dtor) |dtor| { let class_t = {self_ty: self_ty, self_id: dtor.node.self_id, @@ -1913,7 +1925,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, // Resolve the path. let class_id; match tcx.def_map.find(id) { - Some(ast::def_class(type_def_id)) => { + Some(ast::def_class(type_def_id, _)) => { class_id = type_def_id; } _ => { @@ -2400,7 +2412,7 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) -> ast::def_fn(id, _) | ast::def_static_method(id, _) | ast::def_const(id) | ast::def_variant(_, id) | - ast::def_class(id) => { + ast::def_class(id, _) => { return ty::lookup_item_type(fcx.ccx.tcx, id); } ast::def_upvar(_, inner, _, _) => { diff --git a/src/rustc/middle/typeck/check/alt.rs b/src/rustc/middle/typeck/check/alt.rs index 24bcc2281fb..0b2e9c8ab3d 100644 --- a/src/rustc/middle/typeck/check/alt.rs +++ b/src/rustc/middle/typeck/check/alt.rs @@ -325,7 +325,7 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) { // Check to ensure that the struct is the one specified. match tcx.def_map.get(pat.id) { - ast::def_class(supplied_def_id) + ast::def_class(supplied_def_id, _) if supplied_def_id == class_id => { // OK. } diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs index 472c8a30459..18e29981af3 100644 --- a/src/rustc/middle/typeck/collect.rs +++ b/src/rustc/middle/typeck/collect.rs @@ -497,6 +497,30 @@ fn convert_struct(ccx: @crate_ctxt, tpt: ty::ty_param_bounds_and_ty, id: ast::node_id) { let tcx = ccx.tcx; + do option::iter(&struct_def.ctor) |ctor| { + // Write the ctor type + let t_args = ctor.node.dec.inputs.map( + |a| ty_of_arg(ccx, type_rscope(rp), *a, None) ); + let t_res = ty::mk_class( + tcx, local_def(id), + {self_r: rscope::bound_self_region(rp), + self_ty: None, + tps: ty::ty_params_to_tys(tcx, tps)}); + let proto = ty::proto_vstore(ty::vstore_slice(ty::re_static)); + let t_ctor = ty::mk_fn(tcx, FnTyBase { + meta: FnMeta {purity: ast::impure_fn, + proto: proto, + bounds: @~[], + ret_style: ast::return_val}, + sig: FnSig {inputs: t_args, + output: t_res} + }); + write_ty_to_tcx(tcx, ctor.node.id, t_ctor); + tcx.tcache.insert(local_def(ctor.node.id), + {bounds: tpt.bounds, + region_param: rp, + ty: t_ctor}); + } do option::iter(&struct_def.dtor) |dtor| { // Write the dtor type diff --git a/src/test/compile-fail/issue-2590.rs b/src/test/compile-fail/issue-2590.rs index 0f4a4804c75..f4ccd901fb3 100644 --- a/src/test/compile-fail/issue-2590.rs +++ b/src/test/compile-fail/issue-2590.rs @@ -10,7 +10,7 @@ trait parse { impl parser: parse { fn parse() -> ~[int] { - dvec::unwrap(move self.tokens) //~ ERROR moving out of immutable field + dvec::unwrap(move self.tokens) //~ ERROR illegal move from self } } diff --git a/src/test/compile-fail/regions-glb-free-free.rs b/src/test/compile-fail/regions-glb-free-free.rs index e4913f7056e..223665381da 100644 --- a/src/test/compile-fail/regions-glb-free-free.rs +++ b/src/test/compile-fail/regions-glb-free-free.rs @@ -19,7 +19,7 @@ mod argparse { fn set_desc(self, s: &str) -> Flag { Flag { //~ ERROR cannot infer an appropriate lifetime name: self.name, - desc: s, //~ ERROR cannot infer an appropriate lifetime + desc: s, max_count: self.max_count, value: self.value } |
