diff options
| author | Tim Chevalier <chevalier@alum.wellesley.edu> | 2012-02-20 15:42:21 -0800 |
|---|---|---|
| committer | Tim Chevalier <chevalier@alum.wellesley.edu> | 2012-02-20 17:16:52 -0800 |
| commit | 2299d204e40e565396daabc7b8d5141cdef52c8b (patch) | |
| tree | d44318a7c1a3ca7d7d6e93a020626538c0a4b0e0 | |
| parent | 5837e1e809af6d783984d94ca27fe488823ecfe6 (diff) | |
| download | rust-2299d204e40e565396daabc7b8d5141cdef52c8b.tar.gz rust-2299d204e40e565396daabc7b8d5141cdef52c8b.zip | |
Further work on resolving and typechecking classes
Class tests aren't working yet, but they fail a little later :-) Also, make the parser correctly set a constructor's result type to its enclosing class type.
| -rw-r--r-- | src/comp/middle/resolve.rs | 69 | ||||
| -rw-r--r-- | src/comp/middle/ty.rs | 19 | ||||
| -rw-r--r-- | src/comp/middle/typeck.rs | 120 | ||||
| -rw-r--r-- | src/comp/syntax/parse/parser.rs | 11 | ||||
| -rw-r--r-- | src/comp/util/ppaux.rs | 2 | ||||
| -rw-r--r-- | src/test/run-pass/classes-simple.rs | 7 |
6 files changed, 200 insertions, 28 deletions
diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index 0b547bc0a04..951fe54e093 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -151,7 +151,7 @@ enum dir { inside, outside, } // when looking up a variable name that's not yet in scope to check // if it's already bound to a enum. enum namespace { ns_val(ns_value_type), ns_type, ns_module, } -enum ns_value_type { ns_a_enum, ns_any_value, } +enum ns_value_type { ns_an_enum, ns_any_value, } fn resolve_crate(sess: session, amap: ast_map::map, crate: @ast::crate) -> {def_map: def_map, exp_map: exp_map, impl_map: impl_map} { @@ -469,7 +469,7 @@ fn resolve_names(e: @env, c: @ast::crate) { variable a refers to a nullary enum. */ ast::pat_ident(p, none) { alt lookup_in_scope(*e, sc, p.span, path_to_ident(p), - ns_val(ns_a_enum)) { + ns_val(ns_an_enum)) { some(fnd@ast::def_variant(_,_)) { e.def_map.insert(pat.id, fnd); } @@ -519,11 +519,16 @@ fn visit_item_with_scope(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) { } ast::item_class(tps, members, ctor_id, ctor_decl, ctor_block) { visit::visit_ty_params(tps, sc, v); - let ctor_scope = cons(scope_fn_expr(ctor_decl, ctor_id, tps), @sc); + let class_scope = cons(scope_item(i), @sc); + /* visit the constructor... */ + visit_fn_with_scope(e, visit::fk_item_fn(i.ident, tps), ctor_decl, + ctor_block, ctor_block.span, ctor_id, + class_scope, v); + /* visit the items */ for cm in members { alt cm.node.decl { - class_method(i) { visit_item_with_scope(e, i, ctor_scope, v); } - _ { } // instance var -- nothing to do + class_method(i) { visit_item_with_scope(e, i, class_scope, v); } + instance_var(_,t,_,_) { v.visit_ty(t, class_scope, v); } } } } @@ -622,10 +627,10 @@ fn visit_local_with_scope(e: @env, loc: @local, sc:scopes, v:vt<scopes>) { // to enum foo, or is it binding a new name foo?) alt loc.node.pat.node { pat_ident(an_ident,_) { - // Be sure to pass ns_a_enum to lookup_in_scope so that + // Be sure to pass ns_an_enum to lookup_in_scope so that // if this is a name that's being shadowed, we don't die alt lookup_in_scope(*e, sc, loc.span, - path_to_ident(an_ident), ns_val(ns_a_enum)) { + path_to_ident(an_ident), ns_val(ns_an_enum)) { some(ast::def_variant(enum_id,variant_id)) { // Declaration shadows a enum that's in scope. // That's an error. @@ -804,7 +809,7 @@ fn ns_name(ns: namespace) -> str { ns_val(v) { alt (v) { ns_any_value { "name" } - ns_a_enum { "enum" } + ns_an_enum { "enum" } } } ns_module { "modulename" } @@ -1000,6 +1005,21 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace) ast::item_native_mod(m) { ret lookup_in_local_native_mod(e, it.id, sp, name, ns); } + ast::item_class(tps, members, ctor_id, _, _) { + if ns == ns_type { + ret lookup_in_ty_params(e, name, tps); + } + if ns == ns_val(ns_any_value) && name == it.ident { + ret some(ast::def_fn(local_def(ctor_id), + ast::impure_fn)); + } + if ns == ns_val(ns_any_value) { + ret lookup_in_class(local_def(it.id), + members, name); + } + // FIXME: AST allows other items to appear in a class, + // but that might not be wise + } _ { } } } @@ -1071,7 +1091,7 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace) /* If we were looking for a enum, at this point we know it's bound to a non-enum value, and we can return none instead of failing */ - ns_a_enum { ret none; } + ns_an_enum { ret none; } _ { "attempted dynamic environment-capture" } } } @@ -1146,6 +1166,29 @@ fn lookup_in_fn(e: env, name: ident, decl: ast::fn_decl, } } +/* + FIXME: not sure about this code. maybe this should be handled + using the mod_index stuff + */ +fn lookup_in_class(parent_id: def_id, + members: [@class_item], name: ident) + -> option<def> { + for m in members { + alt m.node.decl { + instance_var(v_name,_,_,id) { + if v_name == name { + ret some(def_class_field(parent_id, local_def(id))); + } + } + class_method(i) { + if i.ident == name { + ret some(def_class_method(parent_id, local_def(i.id))); + } + } + } + } + ret none; +} fn lookup_in_block(e: env, name: ident, sp: span, b: ast::blk_, pos: uint, loc_pos: uint, ns: namespace) -> option<def> { @@ -1430,7 +1473,7 @@ fn lookup_in_globs(e: env, globs: [glob_imp_def], sp: span, id: ident, if vec::len(matches) == 0u { ret none; } - else if vec::len(matches) == 1u || ns == ns_val(ns_a_enum) { + else if vec::len(matches) == 1u || ns == ns_val(ns_an_enum) { ret some(matches[0].def); } else { for match: glob_imp_def in matches { @@ -1449,7 +1492,7 @@ fn lookup_glob_in_mod(e: env, info: @indexed_mod, sp: span, id: ident, if !info.glob_imported_names.contains_key(id) { info.glob_imported_names.insert(id, glob_resolving(sp)); // kludge - let val_ns = if wanted_ns == ns_val(ns_a_enum) { ns_val(ns_a_enum) } + let val_ns = if wanted_ns == ns_val(ns_an_enum) { ns_val(ns_an_enum) } else { ns_val(ns_any_value) }; let globs = info.glob_imports; let val = lookup_in_globs(e, globs, sp, id, val_ns, dr); @@ -1615,7 +1658,7 @@ fn index_nmod(md: ast::native_mod) -> mod_index { // External lookups fn ns_for_def(d: def) -> namespace { alt d { - ast::def_variant(_, _) { ns_val(ns_a_enum) } + ast::def_variant(_, _) { ns_val(ns_an_enum) } ast::def_fn(_, _) | ast::def_self(_) | ast::def_const(_) | ast::def_arg(_, _) | ast::def_local(_) | ast::def_upvar(_, _, _) | ast::def_self(_) | @@ -1632,7 +1675,7 @@ fn ns_for_def(d: def) -> namespace { // a enum fn ns_ok(wanted:namespace, actual:namespace) -> bool { alt actual { - ns_val(ns_a_enum) { + ns_val(ns_an_enum) { alt wanted { ns_val(_) { true } _ { false } diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index 06823f4aa30..3e9822ab4fd 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -2183,6 +2183,21 @@ mod unify { } } } + ty_class(expected_class, expected_tys) { + alt get(actual).struct { + ty_class(actual_class, actual_tys) { + if expected_class != actual_class { + ret ures_err(terr_mismatch); + } + ret unify_tps(cx, expected_tys, actual_tys, variance, + {|tps| + ures_ok(mk_class(cx.tcx, expected_class, tps))}); + } + _ { + ret ures_err(terr_mismatch); + } + } + } _ { cx.tcx.sess.bug("unify: unexpected type"); } } } @@ -2478,13 +2493,11 @@ fn enum_variant_with_id(cx: ctxt, enum_id: ast::def_id, // If the given item is in an external crate, looks up its type and adds it to // the type cache. Returns the type parameters and type. +// a precondition (did.crate != ast::local_crate) would be nice fn lookup_item_type(cx: ctxt, did: ast::def_id) -> ty_param_bounds_and_ty { alt cx.tcache.find(did) { some(tpt) { ret tpt; } none { - /* where do things get added to the cache? - Have to add class members */ - // The item is in this crate. The caller should have added it to the // type cache already assert did.crate != ast::local_crate; diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index a54a376be2d..69db4b8bca6 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -48,8 +48,17 @@ type crate_ctxt = {mutable self_infos: [self_info], impl_map: resolve::impl_map, method_map: method_map, dict_map: dict_map, + // Not at all sure it's right to put these here + /* node_id for the class this fn is in -- + none if it's not in a class */ + enclosing_class_id: option<ast::node_id>, + /* map from node_ids for enclosing-class + vars and methods to types */ + enclosing_class: class_map, tcx: ty::ctxt}; +type class_map = hashmap<ast::node_id, ty::t>; + type fn_ctxt = // var_bindings, locals and next_var_id are shared // with any nested functions that capture the environment @@ -121,7 +130,6 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) -> } ast::def_fn(id, _) | ast::def_const(id) | ast::def_variant(_, id) | ast::def_class(id) - | ast::def_class_method(_, id) | ast::def_class_field(_, id) { ret ty::lookup_item_type(fcx.ccx.tcx, id); } ast::def_binding(id) { assert (fcx.locals.contains_key(id.node)); @@ -134,6 +142,20 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) -> ast::def_upvar(_, inner, _) { ret ty_param_bounds_and_ty_for_def(fcx, sp, *inner); } + ast::def_class_method(_, id) | ast::def_class_field(_, id) { + if id.crate != ast::local_crate { + fcx.ccx.tcx.sess.span_fatal(sp, + "class method or field referred to \ + out of scope"); + } + alt fcx.ccx.enclosing_class.find(id.node) { + some(a_ty) { ret {bounds: @[], ty: a_ty}; } + _ { fcx.ccx.tcx.sess.span_fatal(sp, + "class method or field referred to \ + out of scope"); } + } + } + _ { // FIXME: handle other names. fcx.ccx.tcx.sess.unimpl("definition variant"); @@ -316,7 +338,11 @@ fn ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t { ty::mk_fn(tcx, ty_of_fn_decl(tcx, mode, proto, decl)) } ast::ty_path(path, id) { - alt tcx.def_map.get(id) { + let a_def = alt tcx.def_map.find(id) { + none { tcx.sess.span_fatal(ast_ty.span, #fmt("unbound path %s", + path_to_str(path))); } + some(d) { d }}; + alt a_def { ast::def_ty(id) { instantiate(tcx, ast_ty.span, mode, id, path.node.types) } @@ -349,6 +375,23 @@ fn ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t { } } } + ast::def_class(class_id) { + alt tcx.items.find(class_id.node) { + some(ast_map::node_item( + @{node: ast::item_class(tps, _, _, _, _), _}, _)) { + if vec::len(tps) != vec::len(path.node.types) { + tcx.sess.span_err(ast_ty.span, "incorrect number of \ + type parameters to object type"); + } + ty::mk_class(tcx, class_id, vec::map(path.node.types, + {|ast_ty| ast_ty_to_ty(tcx, mode, ast_ty)})) + } + _ { + tcx.sess.span_bug(ast_ty.span, "class id is unbound \ + in items"); + } + } + } _ { tcx.sess.span_fatal(ast_ty.span, "found type name used as a variable"); @@ -787,9 +830,20 @@ mod collect { } } } - fn convert_class_item(_cx: @ctxt, _parent_ty: ty::t, - _ci: ast::class_member) { - /* TODO */ + fn convert_class_item(cx: @ctxt, ci: ast::class_member) { + /* 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(cx.tcx, m_collect, t); + write_ty(cx.tcx, id, tt); + } + ast::class_method(it) { convert(cx, it); } + } } fn convert(cx: @ctxt, it: @ast::item) { alt it.node { @@ -890,16 +944,23 @@ mod collect { ensure_iface_methods(cx.tcx, it.id); } ast::item_class(tps, members, ctor_id, ctor_decl, ctor_block) { - let parent_ty = ty::lookup_item_type(cx.tcx, local_def(it.id)); + // Write the class type + let {bounds,params} = mk_ty_params(cx.tcx, tps); + let class_ty = ty::mk_class(cx.tcx, local_def(it.id), params); + let tpt = {bounds: bounds, ty: class_ty}; + cx.tcx.tcache.insert(local_def(it.id), tpt); + write_ty(cx.tcx, it.id, class_ty); // Write the ctor type let t_ctor = ty::mk_fn(cx.tcx, ty_of_fn_decl(cx.tcx, m_collect, ast::proto_any, ctor_decl)); write_ty(cx.tcx, ctor_id, t_ctor); + cx.tcx.tcache.insert(local_def(ctor_id), + {bounds: 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(cx, parent_ty.ty, m.node.decl); + convert_class_item(cx, m.node.decl); } } _ { @@ -2252,7 +2313,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, } ast::expr_fn(proto, decl, body, captures) { check_expr_fn_with_unifier(fcx, expr, proto, decl, body, - unify, expected); + unify, expected); capture::check_capture_clause(tcx, expr.id, proto, *captures); } ast::expr_fn_block(decl, body) { @@ -2446,6 +2507,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, _ {} } } + ty::ty_class(_id, _params) { + // TODO (classes) + tcx.sess.span_bug(expr.span, + #fmt("can't check class field accesses yet: %s", + ty_to_str(fcx.ccx.tcx, base_t))); + } _ {} } if !handled { @@ -2874,6 +2941,30 @@ fn check_method(ccx: @crate_ctxt, method: @ast::method) { check_fn(ccx, ast::proto_bare, method.decl, method.body, method.id, none); } +fn class_types(ccx: @crate_ctxt, members: [@ast::class_item]) -> class_map { + let rslt = new_int_hash::<ty::t>(); + for m in members { + alt m.node.decl { + 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); + } + } + } + rslt +} + +fn check_class_member(ccx: @crate_ctxt, cm: ast::class_member) { + alt cm { + ast::instance_var(_,t,_,_) { // ??? Not sure + } + // not right yet -- need a scope + ast::class_method(i) { check_item(ccx, i); } + } +} + fn check_item(ccx: @crate_ctxt, it: @ast::item) { alt it.node { ast::item_const(_, e) { check_const(ccx, it.span, e, it.id); } @@ -2889,6 +2980,17 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) { for m in ms { check_method(ccx, m); } vec::pop(ccx.self_infos); } + ast::item_class(tps, members, ctor_id, ctor_decl, ctor_body) { + let cid = some(it.id); + let members_info = class_types(ccx, members); + let class_ccx = @{enclosing_class_id:cid, + enclosing_class:members_info with *ccx}; + // typecheck the ctor + check_fn(class_ccx, ast::proto_bare, ctor_decl, ctor_body, ctor_id, + none); + // typecheck the members + for m in members { check_class_member(class_ccx, m.node.decl); } + } _ {/* nothing to do */ } } } @@ -3149,6 +3251,8 @@ fn check_crate(tcx: ty::ctxt, impl_map: resolve::impl_map, impl_map: impl_map, method_map: std::map::new_int_hash(), dict_map: std::map::new_int_hash(), + enclosing_class_id: none, + enclosing_class: std::map::new_int_hash(), tcx: tcx}; let visit = visit::mk_simple_visitor(@{ visit_item: bind check_item(ccx, _) diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index eb34fb4b89d..35d5119c4e6 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -1972,13 +1972,14 @@ fn parse_item_res(p: parser, attrs: [ast::attribute]) -> @ast::item { fn parse_item_class(p: parser, attrs: [ast::attribute]) -> @ast::item { let lo = p.last_span.lo; let class_name = parse_value_ident(p); + let class_path = ident_to_path(p.last_span, class_name); let ty_params = parse_ty_params(p); expect(p, token::LBRACE); let items: [@ast::class_item] = []; let ctor_id = p.get_id(); let the_ctor : option<(ast::fn_decl, ast::blk)> = none; while p.token != token::RBRACE { - alt parse_class_item(p) { + alt parse_class_item(p, class_path) { ctor_decl(a_fn_decl, blk) { the_ctor = some((a_fn_decl, blk)); } @@ -2015,10 +2016,14 @@ enum class_contents { ctor_decl(ast::fn_decl, ast::blk), // none of these are a ctor decl priv_decls([ast::class_member])} -fn parse_class_item(p:parser) -> class_contents { + fn parse_class_item(p:parser, class_name:@ast::path) -> class_contents { if eat_word(p, "new") { // Can ctors have attrs? - let decl = parse_fn_decl(p, ast::impure_fn); + // result type is always the type of the class + let decl_ = parse_fn_decl(p, ast::impure_fn); + let decl = {output: @{node: ast::ty_path(class_name, p.get_id()), + span: decl_.output.span} + with decl_}; let body = parse_block(p); ret ctor_decl(decl, body); } diff --git a/src/comp/util/ppaux.rs b/src/comp/util/ppaux.rs index 2d253a1c3a9..547c2d5ee82 100644 --- a/src/comp/util/ppaux.rs +++ b/src/comp/util/ppaux.rs @@ -117,7 +117,7 @@ fn ty_to_str(cx: ctxt, typ: t) -> str { ty_param(id, _) { "'" + str::from_bytes([('a' as u8) + (id as u8)]) } - ty_enum(did, tps) | ty_res(did, _, tps) { + ty_enum(did, tps) | ty_res(did, _, tps) | ty_class(did, tps) { // Not sure why, but under some circumstances enum or resource types // do not have an associated id. I didn't investigate enough to know // if there is a good reason for this. - Niko, 2012-02-10 diff --git a/src/test/run-pass/classes-simple.rs b/src/test/run-pass/classes-simple.rs index 006c6392a6b..938afa6b2d3 100644 --- a/src/test/run-pass/classes-simple.rs +++ b/src/test/run-pass/classes-simple.rs @@ -7,4 +7,11 @@ class cat { let how_hungry : int; new(in_x : uint, in_y : int) { meows = in_x; how_hungry = in_y; } +} + +fn main() { + let nyan : cat = cat(52u, 99); + let kitty = cat(1000u, 2); + log(debug, nyan.how_hungry); + log(debug, kitty.how_hungry); } \ No newline at end of file |
