diff options
| author | Tim Chevalier <chevalier@alum.wellesley.edu> | 2012-03-19 10:19:00 -0700 |
|---|---|---|
| committer | Tim Chevalier <chevalier@alum.wellesley.edu> | 2012-03-20 17:07:07 -0700 |
| commit | b06dc884e57644a0c7e9c5391af9e0392e5f49ac (patch) | |
| tree | 1d1578a0f5770193799937b84cdcc96b217e52e9 | |
| parent | 855c99ea758186096a68a8c13edeb0d89105a410 (diff) | |
| download | rust-b06dc884e57644a0c7e9c5391af9e0392e5f49ac.tar.gz rust-b06dc884e57644a0c7e9c5391af9e0392e5f49ac.zip | |
Class methods WIP
In particular, use the ast::method type to represent a class method, and try to reuse as much iface code as possible. (This makes sense now since I'll be allowing polymorphic class methods.)
| -rw-r--r-- | src/rustc/driver/session.rs | 8 | ||||
| -rw-r--r-- | src/rustc/metadata/csearch.rs | 31 | ||||
| -rw-r--r-- | src/rustc/metadata/decoder.rs | 46 | ||||
| -rw-r--r-- | src/rustc/metadata/encoder.rs | 159 | ||||
| -rw-r--r-- | src/rustc/middle/ast_map.rs | 13 | ||||
| -rw-r--r-- | src/rustc/middle/resolve.rs | 4 | ||||
| -rw-r--r-- | src/rustc/middle/trans/reachable.rs | 11 | ||||
| -rw-r--r-- | src/rustc/middle/trans/type_of.rs | 16 | ||||
| -rw-r--r-- | src/rustc/middle/ty.rs | 83 | ||||
| -rw-r--r-- | src/rustc/middle/typeck.rs | 182 | ||||
| -rw-r--r-- | src/rustc/syntax/ast.rs | 4 | ||||
| -rw-r--r-- | src/rustc/syntax/ast_util.rs | 16 | ||||
| -rw-r--r-- | src/rustc/syntax/fold.rs | 6 | ||||
| -rw-r--r-- | src/rustc/syntax/parse/parser.rs | 32 | ||||
| -rw-r--r-- | src/rustc/syntax/print/pprust.rs | 20 | ||||
| -rw-r--r-- | src/rustc/syntax/visit.rs | 4 | ||||
| -rw-r--r-- | src/test/run-pass/classes-simple-method.rs | 8 |
17 files changed, 384 insertions, 259 deletions
diff --git a/src/rustc/driver/session.rs b/src/rustc/driver/session.rs index 081b18b08dd..d48ca3f495e 100644 --- a/src/rustc/driver/session.rs +++ b/src/rustc/driver/session.rs @@ -110,6 +110,14 @@ impl session for session { } } +// Seems out of place, but it uses session, so I'm putting it here +fn expect<T: copy>(sess: session, opt: option<T>, msg: fn() -> str) -> T { + alt opt { + some(t) { t } + none { sess.bug(msg()); } + } +} + fn building_library(req_crate_type: crate_type, crate: @ast::crate, testing: bool) -> bool { alt req_crate_type { diff --git a/src/rustc/metadata/csearch.rs b/src/rustc/metadata/csearch.rs index e6615197c01..1d8965897ad 100644 --- a/src/rustc/metadata/csearch.rs +++ b/src/rustc/metadata/csearch.rs @@ -1,15 +1,19 @@ // Searching for information from the cstore +import std::{ebml}; import syntax::ast; import syntax::ast_util; import middle::{ty, ast_map}; import option::{some, none}; import driver::session; +import driver::session::expect; import middle::trans::common::maps; +import common::*; import std::map::hashmap; export get_symbol; -export get_class_items; +export get_class_fields; +export get_field_type; export get_type_param_count; export lookup_defs; export lookup_method_purity; @@ -120,10 +124,10 @@ fn get_iface_methods(tcx: ty::ctxt, def: ast::def_id) -> @[ty::method] { decoder::get_iface_methods(cdata, def.node, tcx) } -fn get_class_items(tcx: ty::ctxt, def: ast::def_id) -> [@ty::class_item_ty] { +fn get_class_fields(tcx: ty::ctxt, def: ast::def_id) -> [ty::field_ty] { let cstore = tcx.sess.cstore; let cdata = cstore::get_crate_data(cstore, def.crate); - decoder::get_class_items(cdata, def.node, tcx) + decoder::get_class_fields(tcx, cdata, def.node) } fn get_type(tcx: ty::ctxt, def: ast::def_id) -> ty::ty_param_bounds_and_ty { @@ -132,6 +136,27 @@ fn get_type(tcx: ty::ctxt, def: ast::def_id) -> ty::ty_param_bounds_and_ty { decoder::get_type(cdata, def.node, tcx) } +/* FIXME: Refactor */ +fn get_field_type(tcx: ty::ctxt, class_id: ast::def_id, + def: ast::def_id) -> ty::ty_param_bounds_and_ty { + let cstore = tcx.sess.cstore; + let cdata = cstore::get_crate_data(cstore, class_id.crate); + let all_items = ebml::get_doc(ebml::doc(cdata.data), tag_items); + #debug("Looking up %?", class_id); + let class_doc = expect(tcx.sess, + decoder::maybe_find_item(class_id.node, all_items), + {|| #fmt("get_field_type: class ID %? not found", + class_id)}); + #debug("looking up %? : %?", def, class_doc); + let the_field = expect(tcx.sess, + decoder::maybe_find_item(def.node, class_doc), + {|| #fmt("get_field_type: in class %?, field ID %? not found", + class_id, def)}); + #debug("got field data %?", the_field); + let ty = decoder::item_type(def, the_field, tcx, cdata); + ret {bounds: @[], ty: ty}; +} + fn get_impl_iface(tcx: ty::ctxt, def: ast::def_id) -> option<ty::t> { let cstore = tcx.sess.cstore; diff --git a/src/rustc/metadata/decoder.rs b/src/rustc/metadata/decoder.rs index 1a3f6123d57..8916d32d393 100644 --- a/src/rustc/metadata/decoder.rs +++ b/src/rustc/metadata/decoder.rs @@ -14,8 +14,9 @@ import tydecode::{parse_ty_data, parse_def_id, parse_bounds_data, import syntax::print::pprust; import cmd=cstore::crate_metadata; import middle::trans::common::maps; +import util::ppaux::ty_to_str; -export get_class_items; +export get_class_fields; export get_symbol; export get_enum_variants; export get_type; @@ -35,6 +36,8 @@ export get_impls_for_mod; export get_iface_methods; export get_crate_module_paths; export get_item_path; +export maybe_find_item; // sketchy +export item_type; // sketchy export maybe_get_item_ast; export item_is_intrinsic; @@ -110,9 +113,9 @@ fn item_parent_item(d: ebml::doc) -> option<ast::def_id> { found } -fn class_field_id(d: ebml::doc) -> ast::def_id { +fn class_field_id(d: ebml::doc, cdata: cmd) -> ast::def_id { let tagdoc = ebml::get_doc(d, tag_def_id); - ret parse_def_id(ebml::doc_data(tagdoc)); + ret translate_def_id(cdata, parse_def_id(ebml::doc_data(tagdoc))); } fn variant_disr_val(d: ebml::doc) -> option<int> { @@ -406,37 +409,26 @@ fn get_iface_methods(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) } /* Take a node ID for a class, return a vector of the class's - member types */ -fn get_class_items(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) - -> [@ty::class_item_ty] { + field names/IDs */ +fn get_class_fields(tcx: ty::ctxt, + cdata: cmd, id: ast::node_id) -> [ty::field_ty] { let data = cdata.data; let item = lookup_item(id, data), result = []; - #debug("get_class_items: %s", item_name(item)); - #debug("item: %?", item); - // right tag? - ebml::tagged_docs(item, tag_items_class_member) {|an_item| + ebml::tagged_docs(item, tag_items_data_item) {|an_item| let fam = item_family(an_item); - let decl = alt check fam { - 'g' { - let name = item_name(an_item); - #debug("why hello there! %s", name); - let ty = doc_type(an_item, tcx, cdata); - let did = class_field_id(an_item); - {ident: name, - id: did.node, - contents: ty::var_ty(ty)} - } - _ { - fail; // FIXME - } - }; - result += [@decl]; + alt fam { + 'g' { + let name = item_name(an_item); + let _ty = doc_type(an_item, tcx, cdata); + let did = class_field_id(an_item, cdata); + result += [{ident: name, id: did}]; + } + _ { /* this only handles fields */} + } } result } - - fn family_has_type_params(fam_ch: char) -> bool { alt check fam_ch { 'c' | 'T' | 'm' | 'n' | 'g' | 'h' { false } diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index e36b982ff12..b55a3426244 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -82,8 +82,8 @@ fn encode_class_item_paths(ebml_w: ebml::writer, priv { cont; } pub { let (id, ident) = alt it.node.decl { - instance_var(v, _, _, vid) { (vid, v) } - class_method(it) { (it.id, it.ident) } + instance_var(v, _, _, vid) { (vid, v) } + class_method(it) { (it.id, it.ident) } }; add_to_index(ebml_w, path, index, ident); encode_named_def_id(ebml_w, ident, local_def(id)); @@ -145,18 +145,18 @@ fn encode_module_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt, ebml_w.end_tag(); } item_class(tps,items,ctor) { - add_to_index(ebml_w, path, index, it.ident); - ebml_w.start_tag(tag_paths_data_item); - encode_name(ebml_w, it.ident); - encode_def_id(ebml_w, local_def(it.id)); - ebml_w.end_tag(); - ebml_w.start_tag(tag_paths); - add_to_index(ebml_w, path, index, it.ident); - #debug("ctor id: %d", ctor.node.id); - encode_named_def_id(ebml_w, it.ident, local_def(ctor.node.id)); - encode_class_item_paths(ebml_w, items, path + [it.ident], + add_to_index(ebml_w, path, index, it.ident); + ebml_w.start_tag(tag_paths_data_item); + encode_name(ebml_w, it.ident); + encode_def_id(ebml_w, local_def(it.id)); + ebml_w.end_tag(); + ebml_w.start_tag(tag_paths); + add_to_index(ebml_w, path, index, it.ident); + #debug("ctor id: %d", ctor.node.id); + encode_named_def_id(ebml_w, it.ident, local_def(ctor.node.id)); + encode_class_item_paths(ebml_w, items, path + [it.ident], index); - ebml_w.end_tag(); + ebml_w.end_tag(); } item_enum(variants, tps) { add_to_index(ebml_w, path, index, it.ident); @@ -252,7 +252,12 @@ fn encode_type(ecx: @encode_ctxt, ebml_w: ebml::writer, typ: ty::t) { fn encode_symbol(ecx: @encode_ctxt, ebml_w: ebml::writer, id: node_id) { ebml_w.start_tag(tag_items_data_item_symbol); - ebml_w.writer.write(str::bytes(ecx.ccx.item_symbols.get(id))); + let sym = alt ecx.ccx.item_symbols.find(id) { + some(x) { x } + none { ecx.ccx.tcx.sess.bug(#fmt("encode_symbol: \ + id not found %d", id)); } + }; + ebml_w.writer.write(str::bytes(sym)); ebml_w.end_tag(); } @@ -346,43 +351,53 @@ fn encode_info_for_mod(ecx: @encode_ctxt, ebml_w: ebml::writer, md: _mod, ebml_w.end_tag(); } +/* Returns an index of items in this class */ fn encode_info_for_class(ecx: @encode_ctxt, ebml_w: ebml::writer, id: node_id, path: ast_map::path, name: ident, - tps: [ty_param], items: [@class_item]) { + tps: [ty_param], items: [@class_item]) + -> [entry<int>] { + let index = @mutable []; + let tcx = ecx.ccx.tcx; encode_def_id(ebml_w, local_def(id)); encode_family(ebml_w, 'C'); encode_type_param_bounds(ebml_w, ecx, tps); encode_type(ecx, ebml_w, node_id_to_type(tcx, id)); encode_name(ebml_w, name); + encode_path(ebml_w, path, ast_map::path_name(name)); for ci in items { - /* We encode both private and public fields -- need to include - private fields to get the offsets right */ - ebml_w.start_tag(tag_items_class_member); - alt ci.node.decl { - instance_var(nm, _, _, id) { - #debug("encode_info_for_class: doing %s %d", nm, id); - encode_family(ebml_w, 'g'); - encode_name(ebml_w, nm); - encode_type(ecx, ebml_w, node_id_to_type(tcx, id)); - /* TODO: mutability */ - encode_def_id(ebml_w, local_def(id)); - } - class_method(it) { - encode_family(ebml_w, 'h'); - encode_name(ebml_w, it.ident); - alt it.node { - item_fn(fdecl, tps, _) { - encode_info_for_fn(ecx, ebml_w, it.id, it.ident, - path, none, tps, fdecl); - } - _ { fail; /* TODO */ } - } - } - } - ebml_w.end_tag(); + /* We encode both private and public fields -- need to include + private fields to get the offsets right */ + alt ci.node.decl { + instance_var(nm, _, _, id) { + *index += [{val: id, pos: ebml_w.writer.tell()}]; + ebml_w.start_tag(tag_items_data_item); + #debug("encode_info_for_class: doing %s %d", nm, id); + encode_family(ebml_w, 'g'); + encode_name(ebml_w, nm); + encode_path(ebml_w, path, ast_map::path_name(nm)); + encode_type(ecx, ebml_w, node_id_to_type(tcx, id)); + /* TODO: mutability */ + encode_def_id(ebml_w, local_def(id)); + } + class_method(m) { + *index += [{val: m.id, pos: ebml_w.writer.tell()}]; + ebml_w.start_tag(tag_items_data_item); + encode_family(ebml_w, 'h'); + encode_name(ebml_w, m.ident); + let impl_path = path + [ast_map::path_name(m.ident)]; + /* + Recall methods are (currently) monomorphic, and we don't + repeat the class's ty params in the method decl + */ + encode_info_for_method(ecx, ebml_w, impl_path, + should_inline(m.attrs), id, m, []); + } + } + ebml_w.end_tag(); } + *index } fn encode_info_for_fn(ecx: @encode_ctxt, ebml_w: ebml::writer, @@ -408,6 +423,28 @@ fn encode_info_for_fn(ecx: @encode_ctxt, ebml_w: ebml::writer, ebml_w.end_tag(); } +fn encode_info_for_method(ecx: @encode_ctxt, ebml_w: ebml::writer, + impl_path: ast_map::path, should_inline: bool, + parent_id: node_id, + m: @method, all_tps: [ty_param]) { + #debug("encode_info_for_method: %d %s", m.id, m.ident); + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(m.id)); + encode_family(ebml_w, purity_fn_family(m.decl.purity)); + encode_type_param_bounds(ebml_w, ecx, all_tps); + encode_type(ecx, ebml_w, node_id_to_type(ecx.ccx.tcx, m.id)); + encode_name(ebml_w, m.ident); + encode_path(ebml_w, impl_path, ast_map::path_name(m.ident)); + if all_tps.len() > 0u || should_inline { + astencode::encode_inlined_item( + ecx, ebml_w, impl_path, + ii_method(local_def(parent_id), m)); + } else { + encode_symbol(ecx, ebml_w, m.id); + } + ebml_w.end_tag(); +} + fn purity_fn_family(p: purity) -> char { alt p { unsafe_fn { 'u' } @@ -417,15 +454,17 @@ fn purity_fn_family(p: purity) -> char { } } -fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, - index: @mutable [entry<int>], path: ast_map::path) { - fn should_inline(attrs: [attribute]) -> bool { - alt attr::find_inline_attr(attrs) { - attr::ia_none { false } - attr::ia_hint | attr::ia_always { true } - } +fn should_inline(attrs: [attribute]) -> bool { + alt attr::find_inline_attr(attrs) { + attr::ia_none { false } + attr::ia_hint | attr::ia_always { true } } +} + + +fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, + index: @mutable [entry<int>], path: ast_map::path) { let tcx = ecx.ccx.tcx; let must_write = alt item.node { item_enum(_, _) { true } _ { false } }; @@ -494,11 +533,12 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, path, index, tps); } item_class(tps,items,ctor) { - /* We're not forgetting about the ctor here! It gets - encoded elsewhere */ ebml_w.start_tag(tag_items_data_item); - encode_info_for_class(ecx, ebml_w, item.id, path, item.ident, - tps, items); + let idx = encode_info_for_class(ecx, ebml_w, item.id, path, + item.ident, tps, items); + /* each class must have its own index */ + let bkts = create_index(idx, hash_node_id); + encode_index(ebml_w, bkts, write_int); ebml_w.end_tag(); } item_res(_, tps, _, _, ctor_id) { @@ -553,21 +593,8 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, let impl_path = path + [ast_map::path_name(item.ident)]; for m in methods { *index += [{val: m.id, pos: ebml_w.writer.tell()}]; - ebml_w.start_tag(tag_items_data_item); - encode_def_id(ebml_w, local_def(m.id)); - encode_family(ebml_w, purity_fn_family(m.decl.purity)); - encode_type_param_bounds(ebml_w, ecx, tps + m.tps); - encode_type(ecx, ebml_w, node_id_to_type(tcx, m.id)); - encode_name(ebml_w, m.ident); - encode_path(ebml_w, impl_path, ast_map::path_name(m.ident)); - if tps.len() > 0u || m.tps.len() > 0u || should_inline(m.attrs) { - astencode::encode_inlined_item( - ecx, ebml_w, impl_path, - ii_method(local_def(item.id), m)); - } else { - encode_symbol(ecx, ebml_w, m.id); - } - ebml_w.end_tag(); + encode_info_for_method(ecx, ebml_w, impl_path, + should_inline(m.attrs), item.id, m, tps + m.tps); } } item_iface(tps, ms) { diff --git a/src/rustc/middle/ast_map.rs b/src/rustc/middle/ast_map.rs index 1b2dbaf9d58..947dbaab2f6 100644 --- a/src/rustc/middle/ast_map.rs +++ b/src/rustc/middle/ast_map.rs @@ -172,8 +172,19 @@ fn map_item(i: @item, cx: ctx, v: vt) { cx.map.insert(nitem.id, node_native_item(nitem, abi, @cx.path)); } } - item_class(_, _, ctor) { + item_class(_, items, ctor) { cx.map.insert(ctor.node.id, node_ctor(i, item_path)); + let d_id = ast_util::local_def(i.id); + let p = extend(cx, i.ident); + for ci in items { + // only need to handle methods + alt ci.node.decl { + class_method(m) { + map_method(d_id, p, m, cx); + } + _ {} + } + } } _ { } } diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs index 25996dcdf90..e2bb5494659 100644 --- a/src/rustc/middle/resolve.rs +++ b/src/rustc/middle/resolve.rs @@ -531,7 +531,9 @@ fn visit_item_with_scope(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) { /* visit the items */ for cm in members { alt cm.node.decl { - class_method(i) { visit_item_with_scope(e, i, class_scope, v); } + 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); } instance_var(_,t,_,_) { v.visit_ty(t, class_scope, v); } } } diff --git a/src/rustc/middle/trans/reachable.rs b/src/rustc/middle/trans/reachable.rs index eabb3c0cd5c..da0067400e0 100644 --- a/src/rustc/middle/trans/reachable.rs +++ b/src/rustc/middle/trans/reachable.rs @@ -102,14 +102,11 @@ fn traverse_public_item(cx: ctx, item: @item) { cx.rmap.insert(ctor.node.id, ()); for item in items { alt item.node.decl { - class_method(i) { - cx.rmap.insert(i.id, ()); + class_method(m) { + cx.rmap.insert(m.id, ()); if tps.len() > 0u || - attr::find_inline_attr(i.attrs) != attr::ia_none { - alt i.node { - item_fn(_, _, blk) { traverse_inline_body(cx, blk); } - _ {} - } + attr::find_inline_attr(m.attrs) != attr::ia_none { + traverse_inline_body(cx, m.body); } } _ {} diff --git a/src/rustc/middle/trans/type_of.rs b/src/rustc/middle/trans/type_of.rs index 77cf5e578d4..fcc2229ffbb 100644 --- a/src/rustc/middle/trans/type_of.rs +++ b/src/rustc/middle/trans/type_of.rs @@ -83,16 +83,12 @@ fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef { ty::ty_constr(subt,_) { type_of(cx, subt) } ty::ty_class(did, _) { let tys: [TypeRef] = []; - let cls_items = lookup_class_item_tys(cx.tcx, did); - for ci in cls_items { - // only instance vars are record fields at runtime - alt ci.contents { - var_ty(t) { - let fty = type_of(cx, t); - tys += [fty]; - } - _ {} - } + // only instance vars are record fields at runtime + let fields = lookup_class_fields(cx.tcx, did); + for f in fields { + let t = ty::lookup_field_type(cx.tcx, did, f.id); + let fty = type_of(cx, t); + tys += [fty]; } T_struct(tys) } diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index fd9765e3fc3..4a9f109e4e6 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -20,8 +20,6 @@ export arg; export args_eq; export ast_constr_to_constr; export block_ty; -export class_contents_ty; -export class_item_ty; export class_items_as_fields; export constr; export constr_general; @@ -32,6 +30,7 @@ export expr_has_ty_params; export expr_ty; export expr_ty_params_and_ty; export expr_is_lval; +export field_ty; export fold_ty; export field; export field_idx; @@ -41,7 +40,8 @@ export fm_general, fm_rptr; export get_element_type; export is_binopable; export is_pred_ty; -export lookup_class_item_tys; +export lookup_class_fields; +export lookup_field_type; export lookup_item_type; export method; export method_idx; @@ -158,17 +158,12 @@ type constr_table = hashmap<ast::node_id, [constr]>; type mt = {ty: t, mutbl: ast::mutability}; -type class_item_ty = { +// Just use <field> for class fields? +type field_ty = { ident: ident, - id: node_id, - contents: class_contents_ty + id: def_id, }; -enum class_contents_ty { - var_ty(t), // FIXME: need mutability, too - method_ty(fn_decl) -} - // Contains information needed to resolve types and (in the future) look up // the types of AST nodes. type creader_cache = hashmap<{cnum: int, pos: uint, len: uint}, t>; @@ -1971,6 +1966,9 @@ mod unify { cx: @uctxt, expected: t, actual: t, variance: variance, nxt: fn(t) -> ures<T>) -> ures<T> { + #debug("unify_step: %s %s", ty_to_str(cx.tcx, expected), + ty_to_str(cx.tcx, actual)); + // Fast path. if expected == actual { ret nxt(expected); } @@ -2434,15 +2432,40 @@ 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 +fn lookup_field_type(tcx: ctxt, class_id: def_id, id: def_id) -> ty::t { + 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) + } + else { + alt tcx.tcache.find(id) { + some(tpt) { ret tpt.ty; } + none { + let tpt = csearch::get_field_type(tcx, class_id, id); + // ok b/c fields are monomorphic + // TODO: Comment might be a lie, what if it mentions + // class-bound ty params? + tcx.tcache.insert(id, tpt); + ret tpt.ty; + } + } + } +} + // Look up the list of item types for a given class // Fails if the id is not bound to a class. -fn lookup_class_item_tys(cx: ctxt, did: ast::def_id) -> [@class_item_ty] { +fn lookup_class_fields(cx: ctxt, did: ast::def_id) -> [field_ty] { if did.crate == ast::local_crate { alt cx.items.find(did.node) { some(ast_map::node_item(i,_)) { alt i.node { ast::item_class(_, items, _) { - class_item_tys(cx, items) + class_field_tys(items) } _ { cx.sess.bug("class ID bound to non-class"); } } @@ -2451,27 +2474,19 @@ fn lookup_class_item_tys(cx: ctxt, did: ast::def_id) -> [@class_item_ty] { } } else { - ret csearch::get_class_items(cx, did); + ret csearch::get_class_fields(cx, did); } } // must be called after typechecking? -fn class_item_tys(cx: ctxt, items: [@class_item]) -> [@class_item_ty] { +fn class_field_tys(items: [@class_item]) -> [field_ty] { let rslt = []; for it in items { alt it.node.decl { instance_var(nm, _, _, id) { - rslt += [@{ident: nm, id: id, - contents: var_ty(node_id_to_type(cx, id)) }]; + rslt += [{ident: nm, id: ast_util::local_def(id)}]; } - class_method(it) { - alt it.node { - item_fn(dec, _, _) { - rslt += [@{ident: it.ident, id: it.id, - contents: method_ty(dec)}]; - } - _ { fail; /* TODO */ } - } + class_method(_) { } } } @@ -2482,19 +2497,11 @@ fn class_item_tys(cx: ctxt, items: [@class_item]) -> [@class_item_ty] { // (as if the class was a record). trans uses this fn class_items_as_fields(cx:ctxt, did: ast::def_id) -> [field] { let rslt = []; - for ci in lookup_class_item_tys(cx, did) { - alt ci.contents { - var_ty(t) { - // consider all instance vars mutable, because the - // constructor may mutate all vars - rslt += [{ident: ci.ident, mt: {ty: t, - mutbl: m_mutbl}}]; - } - /* do nothing, since methods don't have a runtime - representation? */ - method_ty(_) { - } - } + for f in lookup_class_fields(cx, did) { + // consider all instance vars mutable, because the + // constructor may mutate all vars + rslt += [{ident: f.ident, mt: {ty: lookup_field_type(cx, did, f.id), + mutbl: m_mutbl}}]; } rslt } diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 528e8fb9500..87bb55e1b76 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -1,7 +1,7 @@ import result::result; import syntax::{ast, ast_util}; import ast::spanned; -import syntax::ast_util::{local_def, respan}; +import syntax::ast_util::{local_def, respan, split_class_items}; import syntax::visit; import metadata::csearch; import driver::session::session; @@ -11,7 +11,7 @@ import pat_util::*; import middle::ty; import middle::ty::{node_id_to_type, arg, block_ty, expr_ty, field, node_type_table, mk_nil, - ty_param_bounds_and_ty, lookup_class_item_tys}; + ty_param_bounds_and_ty, lookup_class_fields}; import util::ppaux::ty_to_str; import std::smallintmap; import std::map::{hashmap, int_hash}; @@ -896,20 +896,38 @@ mod collect { } } } - fn convert_class_item(tcx: ty::ctxt, ci: ast::class_member) { + fn convert_class_item(tcx: ty::ctxt, v: ast_util::ivar) { /* we want to do something here, b/c within the scope of the class, it's ok to refer to fields & methods unqualified */ /* they have these types *within the scope* of the class. outside the class, it's done with expr_field */ - alt ci { - ast::instance_var(_,t,_,id) { - let tt = ast_ty_to_ty(tcx, m_collect, t); - write_ty(tcx, id, tt); - } - ast::class_method(it) { convert(tcx, it); } - } + let tt = ast_ty_to_ty(tcx, m_collect, v.ty); + #debug("convert_class_item: %s %?", v.ident, v.id); + write_ty(tcx, v.id, tt); + } + fn convert_methods(tcx: ty::ctxt, ms: [@ast::method], + i_bounds: @[ty::param_bounds], maybe_self: option<ty::t>) + -> [{mty: ty::method, id: ast::node_id, span: span}] { + let my_methods = []; + for m in ms { + alt maybe_self { + some(selfty) { + write_ty(tcx, m.self_id, selfty); + } + _ {} + } + let bounds = ty_param_bounds(tcx, m_collect, m.tps); + let mty = ty_of_method(tcx, m_collect, m); + my_methods += [{mty: mty, id: m.id, span: m.span}]; + let fty = ty::mk_fn(tcx, mty.fty); + tcx.tcache.insert(local_def(m.id), + {bounds: @(*i_bounds + *bounds), + ty: fty}); + write_ty(tcx, m.id, fty); + } + my_methods } fn convert(tcx: ty::ctxt, it: @ast::item) { alt it.node { @@ -922,22 +940,11 @@ mod collect { } ast::item_impl(tps, ifce, selfty, ms) { let i_bounds = ty_param_bounds(tcx, m_collect, tps); - let my_methods = []; let selfty = ast_ty_to_ty(tcx, m_collect, selfty); write_ty(tcx, it.id, selfty); tcx.tcache.insert(local_def(it.id), {bounds: i_bounds, ty: selfty}); - for m in ms { - write_ty(tcx, m.self_id, selfty); - let bounds = ty_param_bounds(tcx, m_collect, m.tps); - let mty = ty_of_method(tcx, m_collect, m); - my_methods += [{mty: mty, id: m.id, span: m.span}]; - let fty = ty::mk_fn(tcx, mty.fty); - tcx.tcache.insert(local_def(m.id), - {bounds: @(*i_bounds + *bounds), - ty: fty}); - write_ty(tcx, m.id, fty); - } + let my_methods = convert_methods(tcx, ms, i_bounds, some(selfty)); alt ifce { some(t) { let iface_ty = ast_ty_to_ty(tcx, m_collect, t); @@ -1028,9 +1035,11 @@ mod collect { {bounds: tpt.bounds, ty: t_ctor}); /* FIXME: check for proper public/privateness */ // Write the type of each of the members - for m in members { - convert_class_item(tcx, m.node.decl); + let (fields, methods) = split_class_items(members); + for f in fields { + convert_class_item(tcx, f); } + convert_methods(tcx, methods, @[], none); } _ { // This call populates the type cache with the converted type @@ -1732,8 +1741,10 @@ fn require_pure_call(ccx: @crate_ctxt, caller_purity: ast::purity, alt ccx.method_map.find(callee.id) { some(method_static(did)) { if did.crate == ast::local_crate { - alt check ccx.tcx.items.get(did.node) { + alt ccx.tcx.items.get(did.node) { ast_map::node_method(m, _, _) { m.decl.purity } + _ { ccx.tcx.sess.span_bug(sp, + "Node not bound to a method") } } } else { csearch::lookup_method_purity(ccx.tcx.sess.cstore, did) @@ -1834,6 +1845,43 @@ fn lookup_method(fcx: @fn_ctxt, expr: @ast::expr, node_id: ast::node_id, } } +enum method_parent { + cls(ast::def_id), + an_iface(ast::def_id) +} + +fn lookup_method_inner_(tcx: ty::ctxt, ms: [ty::method], + tps: [ty::t], parent: method_parent, name: ast::ident, sp: span) + -> option<{method_ty: ty::t, n_tps: uint, substs: [ty::t], + origin: method_origin, self_sub: option<self_subst>}> { + #debug("lookup_method_inner_: %? %? %s", ms, parent, name); + let i = 0u; + for m in ms { + if m.ident == name { + let fty = ty::mk_fn(tcx, {proto: ast::proto_box with m.fty}); + if ty::type_has_vars(fty) { + tcx.sess.span_fatal( + sp, "can not call a method that contains a \ + self type through a boxed iface"); + } else if (*m.tps).len() > 0u { + tcx.sess.span_fatal( + sp, "can not call a generic method through a \ + boxed iface"); + } + ret some({method_ty: fty, + n_tps: vec::len(*m.tps), + substs: tps, + origin: alt parent { + cls(did) { method_static(did) } + an_iface(did) { method_iface(did, i) } + }, + self_sub: none}); + } + i += 1u; + } + ret none; +} + fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr, name: ast::ident, ty: ty::t) -> option<{method_ty: ty::t, n_tps: uint, substs: [ty::t], @@ -1871,26 +1919,17 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr, } } ty::ty_iface(did, tps) { - let i = 0u; - for m in *ty::iface_methods(tcx, did) { - if m.ident == name { - let fty = ty::mk_fn(tcx, {proto: ast::proto_box with m.fty}); - if ty::type_has_vars(fty) { - tcx.sess.span_fatal( - expr.span, "can not call a method that contains a \ - self type through a boxed iface"); - } else if (*m.tps).len() > 0u { - tcx.sess.span_fatal( - expr.span, "can not call a generic method through a \ - boxed iface"); - } - ret some({method_ty: fty, - n_tps: vec::len(*m.tps), - substs: tps, - origin: method_iface(did, i), - self_sub: none}); - } - i += 1u; + alt lookup_method_inner_(tcx, *ty::iface_methods(tcx, did), tps, + an_iface(did), name, expr.span) { + some(r) { ret some(r); } + none { } + } + } + ty::ty_class(did, tps) { + alt lookup_method_inner_(tcx, *ty::iface_methods(tcx, did), tps, + cls(did), name, expr.span) { + some(r) { ret some(r); } + none { } } } _ {} @@ -1957,19 +1996,12 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr, result } -// problem -- class_item_ty should really be only used for internal stuff. -// or should have a privacy field. -fn lookup_field_ty(cx: ty::ctxt, items:[@ty::class_item_ty], - fieldname: ast::ident, sp: span) - -> ty::t { - for item in items { - #debug("%s $$$ %s", fieldname, item.ident); - alt item.contents { - ty::var_ty(t) if item.ident == fieldname { ret t; } - _ { } - } - } - cx.sess.span_fatal(sp, #fmt("unbound field %s", fieldname)); +// Only for fields! Returns <none> for methods> +// FIXME: privacy flags +fn lookup_field_ty(tcx: ty::ctxt, class_id: ast::def_id, + items:[ty::field_ty], fieldname: ast::ident) -> option<ty::t> { + option::map(vec::find(items, {|f| f.ident == fieldname}), + {|f| ty::lookup_field_type(tcx, class_id, f.id) }) } /* @@ -2053,6 +2085,7 @@ fn check_expr_fn_with_unifier(fcx: @fn_ctxt, fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, expected: ty::t) -> bool { + #debug("typechecking expr %s", syntax::print::pprust::expr_to_str(expr)); @@ -2191,10 +2224,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, fn check_call_full(fcx: @fn_ctxt, sp: span, id: ast::node_id, f: @ast::expr, args: [@ast::expr]) -> bool { let bot = check_call(fcx, sp, id, f, args); - /* here we're kind of hosed, as f can be any expr - need to restrict it to being an explicit expr_path if we're - inside a pure function, and need an environment mapping from - function name onto purity-designation */ + /* need to restrict oper to being an explicit expr_path if we're + inside a pure function */ require_pure_call(fcx.ccx, fcx.purity, f, sp); // Pull the return type out of the type of the function. @@ -2774,13 +2805,19 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, ty::ty_class(base_id, _params) { // (1) verify that the class id actually has a field called // field - let cls_items = lookup_class_item_tys(tcx, base_id); - let field_ty = lookup_field_ty(fcx.ccx.tcx, cls_items, field, - expr.span); - // (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; + #debug("class named %s", ty_to_str(tcx, base_t)); + let cls_items = lookup_class_fields(tcx, base_id); + #debug("cls_items: %?", cls_items); + 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"); } + } } _ {} } @@ -3226,8 +3263,9 @@ fn class_types(ccx: @crate_ctxt, members: [@ast::class_item]) -> class_map { ast::instance_var(_,t,_,id) { rslt.insert(id, ast_ty_to_ty(ccx.tcx, m_collect, t)); } - ast::class_method(it) { - rslt.insert(it.id, ty_of_item(ccx.tcx, m_collect, it).ty); + ast::class_method(mth) { + rslt.insert(mth.id, ty::mk_fn(ccx.tcx, + ty_of_method(ccx.tcx, m_collect, mth).fty)); } } } @@ -3239,7 +3277,7 @@ fn check_class_member(ccx: @crate_ctxt, cm: ast::class_member) { ast::instance_var(_,t,_,_) { // ??? Not sure } // not right yet -- need a scope - ast::class_method(i) { check_item(ccx, i); } + ast::class_method(m) { check_method(ccx, m); } } } diff --git a/src/rustc/syntax/ast.rs b/src/rustc/syntax/ast.rs index 43b0d5e0391..db0f6a56d80 100644 --- a/src/rustc/syntax/ast.rs +++ b/src/rustc/syntax/ast.rs @@ -659,10 +659,10 @@ type class_item = spanned<class_item_>; #[auto_serialize] enum class_member { instance_var(ident, @ty, class_mutability, node_id), - class_method(@item) // FIXME: methods aren't allowed to be - // type-parametric. + class_method(@method) // without constrained types, have to duplicate some stuff. or factor out // item to separate out things with type params? + // (FIXME) where do we enforce that type params is empty? } #[auto_serialize] diff --git a/src/rustc/syntax/ast_util.rs b/src/rustc/syntax/ast_util.rs index 1182a2c40c3..5625fecb953 100644 --- a/src/rustc/syntax/ast_util.rs +++ b/src/rustc/syntax/ast_util.rs @@ -433,6 +433,21 @@ pure fn class_item_ident(ci: @class_item) -> ident { } } +type ivar = {ident: ident, ty: @ty, cm: class_mutability, id: node_id}; + +fn split_class_items(cs: [@class_item]) -> ([ivar], [@method]) { + let vs = [], ms = []; + for c in cs { + alt c.node.decl { + instance_var(i, t, cm, id) { + vs += [{ident: i, ty: t, cm: cm, id: id}]; + } + class_method(m) { ms += [m]; } + } + } + (vs, ms) +} + impl inlined_item_methods for inlined_item { fn ident() -> ident { alt self { @@ -455,7 +470,6 @@ impl inlined_item_methods for inlined_item { } } } - // Local Variables: // mode: rust // fill-column: 78; diff --git a/src/rustc/syntax/fold.rs b/src/rustc/syntax/fold.rs index 0a3e42aee34..0927c94887f 100644 --- a/src/rustc/syntax/fold.rs +++ b/src/rustc/syntax/fold.rs @@ -249,7 +249,7 @@ fn noop_fold_class_item(&&ci: @class_item, fld: ast_fold) instance_var(ident, t, cm, id) { instance_var(ident, fld.fold_ty(t), cm, id) } - class_method(i) { class_method(fld.fold_item(i)) } + class_method(m) { class_method(fld.fold_method(m)) } }}, span: fld.new_span(ci.span)} } @@ -656,8 +656,8 @@ fn make_fold(afp: ast_fold_precursor) -> ast_fold { instance_var(nm, f_ty(afp, f, t), mt, id) } - class_method(i) { - class_method(afp.fold_item(i, f)) + class_method(m) { + class_method(afp.fold_method(m, f)) } }}, span: afp.new_span(ci.span)} } diff --git a/src/rustc/syntax/parse/parser.rs b/src/rustc/syntax/parse/parser.rs index 06d59be0654..4fe7aade41c 100644 --- a/src/rustc/syntax/parse/parser.rs +++ b/src/rustc/syntax/parse/parser.rs @@ -1648,10 +1648,10 @@ fn parse_let(p: parser) -> @ast::decl { ret @spanned(lo, p.last_span.hi, ast::decl_local(locals)); } +/* assumes "let" token has already been consumed */ fn parse_instance_var(p:parser) -> (ast::class_member, codemap::span) { let is_mutbl = ast::class_immutable; let lo = p.span.lo; - expect_word(p, "let"); if eat_word(p, "mut") || eat_word(p, "mutable") { is_mutbl = ast::class_mutable; } @@ -2104,15 +2104,14 @@ enum class_contents { ctor_decl(ast::fn_decl, ast::blk, codemap::span), expect(p, token::LBRACE); let results = []; while p.token != token::RBRACE { - alt parse_item(p, []) { - some(i) { - results += [(ast::class_method(i), i.span)]; - } - _ { - let a_var = parse_instance_var(p); - expect(p, token::SEMI); - results += [a_var]; - } + if eat_word(p, "let") { + let a_var = parse_instance_var(p); + expect(p, token::SEMI); + results += [a_var]; + } + else { + let m = parse_method(p); + results += [(ast::class_method(m), m.span)]; } } p.bump(); @@ -2120,15 +2119,14 @@ enum class_contents { ctor_decl(ast::fn_decl, ast::blk, codemap::span), } else { // Probably need to parse attrs - alt parse_item(p, []) { - some(i) { - ret plain_decl(ast::class_method(i), i.span); - } - _ { + ret if eat_word(p, "let") { let (a_var, a_span) = parse_instance_var(p); expect(p, token::SEMI); - ret plain_decl(a_var, a_span); - } + plain_decl(a_var, a_span) + } + else { + let m = parse_method(p); + plain_decl(ast::class_method(m), m.span) } } } diff --git a/src/rustc/syntax/print/pprust.rs b/src/rustc/syntax/print/pprust.rs index 34d6d09b5ad..e10262117a4 100644 --- a/src/rustc/syntax/print/pprust.rs +++ b/src/rustc/syntax/print/pprust.rs @@ -526,8 +526,8 @@ fn print_item(s: ps, &&item: @ast::item) { print_type(s, t); word(s.s, ";"); } - ast::class_method(i) { - print_item(s, i); + ast::class_method(m) { + print_method(s, m); } } alt ci.node.privacy { @@ -555,12 +555,7 @@ fn print_item(s: ps, &&item: @ast::item) { space(s.s); bopen(s); for meth in methods { - hardbreak_if_not_bol(s); - maybe_print_comment(s, meth.span.lo); - print_outer_attributes(s, meth.attrs); - print_fn(s, meth.decl, meth.ident, meth.tps); - word(s.s, " "); - print_block_with_attrs(s, meth.body, meth.attrs); + print_method(s, meth); } bclose(s, item.span); } @@ -621,6 +616,15 @@ fn print_ty_method(s: ps, m: ast::ty_method) { word(s.s, ";"); } +fn print_method(s: ps, meth: @ast::method) { + hardbreak_if_not_bol(s); + maybe_print_comment(s, meth.span.lo); + print_outer_attributes(s, meth.attrs); + print_fn(s, meth.decl, meth.ident, meth.tps); + word(s.s, " "); + print_block_with_attrs(s, meth.body, meth.attrs); +} + fn print_outer_attributes(s: ps, attrs: [ast::attribute]) { let count = 0; for attr: ast::attribute in attrs { diff --git a/src/rustc/syntax/visit.rs b/src/rustc/syntax/visit.rs index 5f92d533f32..c0b7eea6ace 100644 --- a/src/rustc/syntax/visit.rs +++ b/src/rustc/syntax/visit.rs @@ -157,8 +157,8 @@ fn visit_class_item<E>(_s: span, _p: privacy, cm: class_member, instance_var(ident, t, mt, id) { v.visit_ty(t, e, v); } - class_method(i) { - v.visit_item(i, e, v); + class_method(m) { + visit_method_helper(m, e, v); } } } diff --git a/src/test/run-pass/classes-simple-method.rs b/src/test/run-pass/classes-simple-method.rs index 5d2eaf9d37e..2c7c2c7c480 100644 --- a/src/test/run-pass/classes-simple-method.rs +++ b/src/test/run-pass/classes-simple-method.rs @@ -8,7 +8,11 @@ class cat { new(in_x : uint, in_y : int) { meows = in_x; how_hungry = in_y; } - fn speak() { fail; } + fn speak() {} + /* + fn speak() { meows += 1u; } + fn meow_count() -> uint { meows } + */ } fn main() { @@ -16,4 +20,6 @@ fn main() { let kitty = cat(1000u, 2); assert(nyan.how_hungry == 99); assert(kitty.how_hungry == 2); + nyan.speak(); + // assert(nyan.meow_count() == 53u); } |
