diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 9ab8a75b133..31f21676331 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -448,7 +448,7 @@ fn map(v: [T], f: fn(T) -> U) -> [U] { } #[doc = " -Apply a function eo each element of a vector and return a concatenation +Apply a function to each element of a vector and return a concatenation of each result vector "] fn flat_map(v: [T], f: fn(T) -> [U]) -> [U] { diff --git a/src/librustsyntax/ast.rs b/src/librustsyntax/ast.rs index 339c897256c..55bb480761f 100644 --- a/src/librustsyntax/ast.rs +++ b/src/librustsyntax/ast.rs @@ -636,6 +636,10 @@ enum attr_style { attr_outer, attr_inner, } #[auto_serialize] type attribute_ = {style: attr_style, value: meta_item}; +/* + iface_refs appear in both impls and in classes that implement ifaces. + resolve maps each iface_ref's id to its defining iface. + */ #[auto_serialize] type iface_ref = {path: @path, id: node_id}; @@ -661,14 +665,14 @@ enum item_ { node_id /* dtor id */, node_id /* ctor id */, region_param), item_class([ty_param], /* ty params for class */ - [iface_ref], /* ifaces this class implements */ + [@iface_ref], /* ifaces this class implements */ [@class_member], /* methods, etc. */ /* (not including ctor) */ class_ctor, region_param ), item_iface([ty_param], [ty_method]), - item_impl([ty_param], option<@ty> /* iface */, + item_impl([ty_param], option<@iface_ref> /* iface */, @ty /* self */, [@method]), } diff --git a/src/librustsyntax/ast_util.rs b/src/librustsyntax/ast_util.rs index f74f543a716..8dd1b3c7fea 100644 --- a/src/librustsyntax/ast_util.rs +++ b/src/librustsyntax/ast_util.rs @@ -2,20 +2,20 @@ import codemap::span; import ast::*; fn spanned(lo: uint, hi: uint, t: T) -> spanned { - ret respan(mk_sp(lo, hi), t); + respan(mk_sp(lo, hi), t) } fn respan(sp: span, t: T) -> spanned { - ret {node: t, span: sp}; + {node: t, span: sp} } fn dummy_spanned(t: T) -> spanned { - ret respan(dummy_sp(), t); + respan(dummy_sp(), t) } /* assuming that we're not in macro expansion */ fn mk_sp(lo: uint, hi: uint) -> span { - ret {lo: lo, hi: hi, expn_info: none}; + {lo: lo, hi: hi, expn_info: none} } // make this a const, once the compiler supports it @@ -334,6 +334,16 @@ impl inlined_item_methods for inlined_item { } } } + +/* True if d is either a def_self, or a chain of def_upvars + referring to a def_self */ +fn is_self(d: ast::def) -> bool { + alt d { + def_self(_) { true } + def_upvar(_, d, _) { is_self(*d) } + _ { false } + } +} // Local Variables: // mode: rust // fill-column: 78; diff --git a/src/librustsyntax/fold.rs b/src/librustsyntax/fold.rs index b70755a1d2c..e5c2c59a4d2 100644 --- a/src/librustsyntax/fold.rs +++ b/src/librustsyntax/fold.rs @@ -279,9 +279,7 @@ fn noop_fold_item_underscore(i: item_, fld: ast_fold) -> item_ { let ctor_id = fld.new_id(ctor.node.id); item_class( typms, - vec::map(ifaces, {|p| - {path: fld.fold_path(p.path), - id: fld.new_id(p.id)}}), + vec::map(ifaces, {|p| fold_iface_ref(p, fld) }), vec::map(items, fld.fold_class_item), {node: {body: ctor_body, dec: ctor_decl, @@ -290,7 +288,8 @@ fn noop_fold_item_underscore(i: item_, fld: ast_fold) -> item_ { rp) } item_impl(tps, ifce, ty, methods) { - item_impl(tps, option::map(ifce, fld.fold_ty), fld.fold_ty(ty), + item_impl(tps, option::map(ifce, {|p| fold_iface_ref(p, fld)}), + fld.fold_ty(ty), vec::map(methods, fld.fold_method)) } item_iface(tps, methods) { item_iface(tps, methods) } @@ -305,6 +304,10 @@ fn noop_fold_item_underscore(i: item_, fld: ast_fold) -> item_ { }; } +fn fold_iface_ref(&&p: @iface_ref, fld: ast_fold) -> @iface_ref { + @{path: fld.fold_path(p.path), id: fld.new_id(p.id)} +} + fn noop_fold_method(&&m: @method, fld: ast_fold) -> @method { ret @{ident: fld.fold_ident(m.ident), attrs: m.attrs, diff --git a/src/librustsyntax/parse/parser.rs b/src/librustsyntax/parse/parser.rs index 7ebac4f5c8b..5ce5c1480f5 100644 --- a/src/librustsyntax/parse/parser.rs +++ b/src/librustsyntax/parse/parser.rs @@ -5,6 +5,7 @@ import token::{can_begin_expr, is_ident, is_plain_ident}; import codemap::{span,fss_none}; import util::interner; import ast_util::{spanned, mk_sp, ident_to_path}; +import ast::{node_id}; import lexer::reader; import prec::{op_spec, as_prec}; import attr::{parse_outer_attrs_or_ext, @@ -1788,9 +1789,6 @@ fn parse_item_iface(p: parser, attrs: [ast::attribute]) -> @ast::item { // impl name for [T] { ... } fn parse_item_impl(p: parser, attrs: [ast::attribute]) -> @ast::item { let lo = p.last_span.lo; - fn wrap_path(p: parser, pt: @ast::path) -> @ast::ty { - @{id: p.get_id(), node: ast::ty_path(pt, p.get_id()), span: pt.span} - } let mut (ident, tps) = if !is_word(p, "of") { if p.token == token::LT { (none, parse_ty_params(p)) } else { (some(parse_ident(p)), parse_ty_params(p)) } @@ -1800,7 +1798,7 @@ fn parse_item_impl(p: parser, attrs: [ast::attribute]) -> @ast::item { if option::is_none(ident) { ident = some(vec::last(path.idents)); } - some(wrap_path(p, path)) + some(@{path: path, id: p.get_id()}) } else { none }; let ident = alt ident { some(name) { name } @@ -1855,9 +1853,13 @@ fn ident_to_path_tys(p: parser, i: ast::ident, } } -fn parse_iface_ref_list(p:parser) -> [ast::iface_ref] { +fn parse_iface_ref(p:parser) -> @ast::iface_ref { + @{path: parse_path(p), id: p.get_id()} +} + +fn parse_iface_ref_list(p:parser) -> [@ast::iface_ref] { parse_seq_to_before_end(token::LBRACE, seq_sep(token::COMMA), - {|p| {path: parse_path(p), id: p.get_id()}}, p) + parse_iface_ref, p) } fn parse_item_class(p: parser, attrs: [ast::attribute]) -> @ast::item { @@ -1866,7 +1868,7 @@ fn parse_item_class(p: parser, attrs: [ast::attribute]) -> @ast::item { let rp = parse_region_param(p); let ty_params = parse_ty_params(p); let class_path = ident_to_path_tys(p, class_name, ty_params); - let ifaces : [ast::iface_ref] = if eat_word(p, "implements") + let ifaces : [@ast::iface_ref] = if eat_word(p, "implements") { parse_iface_ref_list(p) } else { [] }; expect(p, token::LBRACE); @@ -1918,7 +1920,7 @@ fn parse_single_class_item(p: parser, privcy: ast::privacy) enum class_contents { ctor_decl(ast::fn_decl, ast::blk, codemap::span), members([@ast::class_member]) } -fn parse_class_item(p:parser, class_name_with_tps:@ast::path) +fn parse_class_item(p:parser, class_name_with_tps: @ast::path) -> class_contents { if eat_word(p, "new") { let lo = p.last_span.lo; diff --git a/src/librustsyntax/print/pprust.rs b/src/librustsyntax/print/pprust.rs index b979caab2e2..e7be554fca8 100644 --- a/src/librustsyntax/print/pprust.rs +++ b/src/librustsyntax/print/pprust.rs @@ -561,14 +561,11 @@ fn print_item(s: ps, &&item: @ast::item) { word(s.s, item.ident); print_type_params(s, tps); space(s.s); - alt ifce { - some(ty) { + option::iter(ifce, {|p| word_nbsp(s, "of"); - print_type(s, ty); + print_path(s, p.path, false); space(s.s); - } - _ {} - } + }); word_nbsp(s, "for"); print_type(s, ty); space(s.s); diff --git a/src/librustsyntax/visit.rs b/src/librustsyntax/visit.rs index 71dd5383e9a..ad6b787f180 100644 --- a/src/librustsyntax/visit.rs +++ b/src/librustsyntax/visit.rs @@ -134,7 +134,7 @@ fn visit_item(i: @item, e: E, v: vt) { } item_impl(tps, ifce, ty, methods) { v.visit_ty_params(tps, e, v); - alt ifce { some(ty) { v.visit_ty(ty, e, v); } none {} } + option::iter(ifce, {|p| visit_path(p.path, e, v)}); v.visit_ty(ty, e, v); for methods.each {|m| visit_method_helper(m, e, v) diff --git a/src/rustc/metadata/csearch.rs b/src/rustc/metadata/csearch.rs index 0b7dada4f2d..9ba99139b90 100644 --- a/src/rustc/metadata/csearch.rs +++ b/src/rustc/metadata/csearch.rs @@ -160,8 +160,9 @@ fn get_field_type(tcx: ty::ctxt, class_id: ast::def_id, ret {bounds: @[], rp: ast::rp_none, ty: ty}; } -fn get_impl_iface(tcx: ty::ctxt, def: ast::def_id) - -> option { +// Given a def_id for an impl or class, return the iface it implements, +// or none if it's not for an impl or for a class that implements ifaces +fn get_impl_iface(tcx: ty::ctxt, def: ast::def_id) -> option { let cstore = tcx.sess.cstore; let cdata = cstore::get_crate_data(cstore, def.crate); decoder::get_impl_iface(cdata, def.node, tcx) diff --git a/src/rustc/metadata/decoder.rs b/src/rustc/metadata/decoder.rs index 0fed31c20bc..50a1093153b 100644 --- a/src/rustc/metadata/decoder.rs +++ b/src/rustc/metadata/decoder.rs @@ -27,7 +27,6 @@ export get_class_method; export get_impl_method; export lookup_def; export lookup_item_name; -export get_impl_iface; export resolve_path; export get_crate_attributes; export list_crate_metadata; @@ -157,11 +156,9 @@ fn item_impl_iface(item: ebml::doc, tcx: ty::ctxt, cdata: cmd) -> option { let mut result = none; ebml::tagged_docs(item, tag_impl_iface) {|ity| - let t = parse_ty_data(ity.data, cdata.cnum, ity.start, tcx, {|did| - translate_def_id(cdata, did) - }); - result = some(t); - } + result = some(parse_ty_data(ity.data, cdata.cnum, ity.start, tcx, + {|did| translate_def_id(cdata, did)})); + }; result } diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index 8fd8adae04a..dc53c10e3b6 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -659,9 +659,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, } alt ifce { some(t) { - let i_ty = alt check t.node { - ty_path(_, id) { ty::node_id_to_type(tcx, id) } - }; + let i_ty = ty::node_id_to_type(tcx, t.id); ebml_w.start_tag(tag_impl_iface); write_type(ecx, ebml_w, i_ty); ebml_w.end_tag(); diff --git a/src/rustc/middle/ast_map.rs b/src/rustc/middle/ast_map.rs index ab7a884eaeb..2988cdc6f40 100644 --- a/src/rustc/middle/ast_map.rs +++ b/src/rustc/middle/ast_map.rs @@ -204,16 +204,16 @@ fn map_item(i: @item, cx: ctx, v: vt) { cx.map.insert(nitem.id, node_native_item(nitem, abi, @cx.path)); } } - item_class(_, _, items, ctor, _) { + item_class(tps, ifces, items, ctor, _) { + let (_, ms) = ast_util::split_class_items(items); + // Map iface refs to their parent classes. This is + // so we can find the self_ty + vec::iter(ifces) {|p| cx.map.insert(p.id, + node_item(i, item_path)); }; let d_id = ast_util::local_def(i.id); let p = extend(cx, i.ident); - for items.each {|ci| // only need to handle methods - alt ci.node { - class_method(m) { map_method(d_id, p, m, cx); } - _ {} - } - } + vec::iter(ms) {|m| map_method(d_id, p, m, cx); } } _ { } } diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs index 076bb7705ca..c7ba313655e 100644 --- a/src/rustc/middle/resolve.rs +++ b/src/rustc/middle/resolve.rs @@ -9,6 +9,7 @@ import metadata::{csearch, cstore}; import driver::session::session; import util::common::*; import std::map::{int_hash, str_hash, hashmap}; +import vec::each; import syntax::codemap::span; import syntax::visit; import visit::vt; @@ -402,6 +403,11 @@ fn maybe_insert(e: @env, id: node_id, def: option) { } } +fn resolve_iface_ref(p: @iface_ref, sc: scopes, e: @env) { + maybe_insert(e, p.id, + lookup_path_strict(*e, sc, p.path.span, p.path, ns_type)); +} + fn resolve_names(e: @env, c: @ast::crate) { e.used_imports.track = true; let v = @@ -431,9 +437,10 @@ fn resolve_names(e: @env, c: @ast::crate) { alt i.node { ast::item_class(_, ifaces, _, _, _) { /* visit the iface paths... */ - for ifaces.each {|p| - maybe_insert(e, p.id, - lookup_path_strict(*e, sc, p.path.span, p.path, ns_type))}; + for ifaces.each {|p| resolve_iface_ref(p, sc, e)}; + } + ast::item_impl(_, ifce, _, _) { + option::iter(ifce, {|p| resolve_iface_ref(p, sc, e)}); } _ {} } @@ -535,7 +542,7 @@ fn visit_item_with_scope(e: @env, i: @ast::item, sc: scopes, v: vt) { alt i.node { ast::item_impl(tps, ifce, sty, methods) { visit::visit_ty_params(tps, sc, v); - alt ifce { some(ty) { v.visit_ty(ty, sc, v); } _ {} } + option::iter(ifce) {|p| visit::visit_path(p.path, sc, v)}; v.visit_ty(sty, sc, v); for methods.each {|m| v.visit_ty_params(m.tps, sc, v); @@ -1109,11 +1116,11 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace, } ret some(df); } - _ {} - } - if left_fn { - left_fn_level2 = true; - } else if ns != ns_module { + _ {} + } + if left_fn { + left_fn_level2 = true; + } else if ns != ns_module { left_fn = scope_is_fn(hd); alt scope_closes(hd) { some(node_id) { closing += [node_id]; } @@ -1121,8 +1128,8 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace, } } sc = *tl; - } } + } }; } @@ -2103,6 +2110,8 @@ fn check_exports(e: @env) { // Impl resolution type method_info = {did: def_id, n_tps: uint, ident: ast::ident}; +/* what are the did and ident here? */ +/* ident = the name of the impl */ type _impl = {did: def_id, ident: ast::ident, methods: [@method_info]}; type iscopes = list<@[@_impl]>; @@ -2177,6 +2186,12 @@ fn find_impls_in_view_item(e: env, vi: @ast::view_item, } } +/* + Given an item , adds one record to the mutable vec + if the item is an impl; zero or more records if the + item is a class; and none otherwise. Each record describes + one interface implemented by i. + */ fn find_impls_in_item(e: env, i: @ast::item, &impls: [@_impl], name: option, ck_exports: option<@indexed_mod>) { @@ -2196,6 +2211,20 @@ fn find_impls_in_item(e: env, i: @ast::item, &impls: [@_impl], })}]; } } + ast::item_class(tps, ifces, items, _, _) { + let (_, mthds) = ast_util::split_class_items(items); + let n_tps = tps.len(); + vec::iter(ifces) {|p| + // The def_id, in this case, identifies the combination of + // class and iface + impls += [@{did: local_def(p.id), + ident: i.ident, + methods: vec::map(mthds, {|m| + @{did: local_def(m.id), + n_tps: n_tps + m.tps.len(), + ident: m.ident}})}]; + } + } _ {} } } diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs index 5b0f620661c..9219fc8d1ab 100644 --- a/src/rustc/middle/trans/alt.rs +++ b/src/rustc/middle/trans/alt.rs @@ -164,7 +164,7 @@ fn enter_opt(tcx: ty::ctxt, m: match, opt: opt, col: uint, alt p.node { ast::pat_enum(_, subpats) { if opt_eq(tcx, variant_opt(tcx, p.id), opt) { - some(option::get_or_default(subpats, + some(option::get_default(subpats, vec::from_elem(variant_size, dummy))) } else { none } } diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 4e7f495909e..85e2cd85176 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -1963,7 +1963,7 @@ fn monomorphic_fn(ccx: @crate_ctxt, fn_id: ast::def_id, real_substs: [ty::t], let mono_ty = ty::subst_tps(ccx.tcx, substs, item_ty); let llfty = type_of_fn_from_ty(ccx, mono_ty); - let depth = option::get_or_default(ccx.monomorphizing.find(fn_id), 0u); + let depth = option::get_default(ccx.monomorphizing.find(fn_id), 0u); // Random cut-off -- code that needs to instantiate the same function // recursively more than ten times can probably safely be assumed to be // causing an infinite expansion. diff --git a/src/rustc/middle/trans/impl.rs b/src/rustc/middle/trans/impl.rs index cb87000c7c6..8b1873247d3 100644 --- a/src/rustc/middle/trans/impl.rs +++ b/src/rustc/middle/trans/impl.rs @@ -5,13 +5,13 @@ import type_of::*; import build::*; import driver::session::session; import syntax::ast; -import syntax::ast_util::local_def; +import syntax::ast_util::{local_def, split_class_items}; import metadata::csearch; import back::{link, abi}; import lib::llvm::llvm; import lib::llvm::{ValueRef, TypeRef}; import lib::llvm::llvm::LLVMGetParam; -import ast_map::{path, path_mod, path_name}; +import ast_map::{path, path_mod, path_name, node_id_to_str}; import std::map::hashmap; fn trans_impl(ccx: @crate_ctxt, path: path, name: ast::ident, @@ -82,12 +82,21 @@ fn trans_vtable_callee(bcx: block, env: callee_env, vtable: ValueRef, {bcx: bcx, val: mptr, kind: owned, env: env} } +fn method_from_methods(ms: [@ast::method], name: ast::ident) -> ast::def_id { + local_def(option::get(vec::find(ms, {|m| m.ident == name})).id) +} + fn method_with_name(ccx: @crate_ctxt, impl_id: ast::def_id, name: ast::ident) -> ast::def_id { if impl_id.crate == ast::local_crate { alt check ccx.tcx.items.get(impl_id.node) { ast_map::node_item(@{node: ast::item_impl(_, _, _, ms), _}, _) { - local_def(option::get(vec::find(ms, {|m| m.ident == name})).id) + method_from_methods(ms, name) + } + ast_map::node_item(@{node: + ast::item_class(_, _, items, _, _), _}, _) { + let (_,ms) = split_class_items(items); + method_from_methods(ms, name) } } } else { @@ -245,9 +254,8 @@ fn make_impl_vtable(ccx: @crate_ctxt, impl_id: ast::def_id, substs: [ty::t], let tcx = ccx.tcx; let ifce_id = ty::ty_to_def_id(option::get(ty::impl_iface(tcx, impl_id))); let has_tps = (*ty::lookup_item_type(ccx.tcx, impl_id).bounds).len() > 0u; - make_vtable(ccx, vec::map(*ty::iface_methods(tcx, ifce_id), {|im| - let fty = ty::subst_tps(tcx, substs, - ty::mk_fn(tcx, im.fty)); + make_vtable(ccx, vec::map(*ty::iface_methods(tcx, ifce_id)) {|im| + let fty = ty::subst_tps(tcx, substs, ty::mk_fn(tcx, im.fty)); if (*im.tps).len() > 0u || ty::type_has_vars(fty) { C_null(T_ptr(T_nil())) } else { @@ -260,7 +268,7 @@ fn make_impl_vtable(ccx: @crate_ctxt, impl_id: ast::def_id, substs: [ty::t], trans_external_path(ccx, m_id, fty) } } - })) + }) } fn trans_cast(bcx: block, val: @ast::expr, id: ast::node_id, dest: dest) diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 9b25b057c3f..2d5a68d08ab 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -1326,6 +1326,17 @@ fn type_kind(cx: ctxt, ty: t) -> kind { } lowest } + // FIXME: (tjc) there are rules about when classes are copyable/ + // sendable, but I'm just treating them like records (#1726) + ty_class(did, substs) { + // also factor out this code, copied from the records case + let mut lowest = kind_sendable; + let flds = class_items_as_fields(cx, did, substs); + for flds.each {|f| + lowest = lower_kind(lowest, type_kind(cx, f.mt.ty)); + } + lowest + } // Tuples lower to the lowest of their members. ty_tup(tys) { let mut lowest = kind_sendable; @@ -1356,7 +1367,6 @@ fn type_kind(cx: ctxt, ty: t) -> kind { param_bounds_to_kind(cx.ty_param_bounds.get(did.node)) } ty_constr(t, _) { type_kind(cx, t) } - ty_class(_, _) { fail "FIXME"; } ty_var(_) { fail "FIXME"; } ty_self(_) { kind_noncopyable } }; @@ -2248,12 +2258,24 @@ fn iface_methods(cx: ctxt, id: ast::def_id) -> @[method] { fn impl_iface(cx: ctxt, id: ast::def_id) -> option { if id.crate == ast::local_crate { - alt cx.items.get(id.node) { - ast_map::node_item(@{node: ast::item_impl( - _, some(@{node: ast::ty_path(_, id), _}), _, _), _}, _) { - some(node_id_to_type(cx, id)) - } - _ { none } + alt cx.items.find(id.node) { + some(ast_map::node_item(@{node: ast::item_impl( + _, some(@{id: id, _}), _, _), _}, _)) { + some(node_id_to_type(cx, id)) + } + some(ast_map::node_item(@{node: ast::item_class(_, _, _, _, _), + _},_)) { + alt cx.def_map.find(id.node) { + some(def_ty(iface_id)) { + some(node_id_to_type(cx, id.node)) + } + _ { + cx.sess.bug("impl_iface: iface ref isn't in iface map \ + and isn't bound to a def_ty"); + } + } + } + _ { none } } } else { csearch::get_impl_iface(cx, id) diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 64d3c4f869b..78fefd3d236 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -20,6 +20,7 @@ import std::map; import std::map::{hashmap, int_hash}; import std::serialization::{serialize_uint, deserialize_uint}; import std::ufind; +import vec::each; import syntax::print::pprust::*; import util::common::indent; import std::list; @@ -43,10 +44,27 @@ type method_map = hashmap; // Resolutions for bounds of all parameters, left to right, for a given path. type vtable_res = @[vtable_origin]; + enum vtable_origin { + /* + Statically known vtable. def_id gives the class or impl item + from whence comes the vtable, and tys are the type substs. + vtable_res is the vtable itself + */ vtable_static(ast::def_id, [ty::t], vtable_res), - // Param number, bound number + /* + Dynamic vtable, comes from a parameter that has a bound on it: + fn foo(a: T) -- a's vtable would have a + vtable_param origin + + The first uint is the param number (identifying T in the example), + and the second is the bound number (identifying baz) + */ vtable_param(uint, uint), + /* + Dynamic vtable, comes from something known to have an interface + type. def_id refers to the iface item, tys are the substs + */ vtable_iface(ast::def_id, [ty::t]), } @@ -119,12 +137,12 @@ type fn_ctxt = // Determines whether the given node ID is a use of the def of // the self ID for the current method, if there is one +// self IDs in an outer scope count. so that means that you can +// call your own private methods from nested functions inside +// class methods fn self_ref(fcx: @fn_ctxt, id: ast::node_id) -> bool { - // check what def `id` was resolved to (if anything) - alt fcx.ccx.tcx.def_map.find(id) { - some(ast::def_self(_)) { true } - _ { false } - } + option::map_default(fcx.ccx.tcx.def_map.find(id), false, + ast_util::is_self) } fn lookup_local(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> ty_vid { @@ -465,19 +483,7 @@ fn ast_region_to_region( get_region_reporting_err(self.tcx(), span, res) } -// Parses the programmer's textual representation of a type into our -// internal notion of a type. `getter` is a function that returns the type -// corresponding to a definition ID: -fn ast_ty_to_ty( - self: AC, rscope: RS, &&ast_ty: @ast::ty) -> ty::t { - - fn ast_mt_to_mt( - self: AC, rscope: RS, mt: ast::mt) -> ty::mt { - - ret {ty: ast_ty_to_ty(self, rscope, mt.ty), mutbl: mt.mutbl}; - } - - fn instantiate( +fn instantiate( self: AC, rscope: RS, sp: span, id: ast::def_id, path_id: ast::node_id, args: [@ast::ty]) -> ty::t { @@ -511,6 +517,40 @@ fn ast_ty_to_ty( let ty = ty::subst(tcx, substs, ty); write_substs_to_tcx(tcx, path_id, substs.tps); ret ty; +} + +/* + Instantiates the path for the given iface reference, assuming that + it's bound to a valid iface type. Returns the def_id for the defining + iface + */ +fn instantiate_iface_ref(ccx: @crate_ctxt, t: @ast::iface_ref) + -> ast::def_id { + alt lookup_def_tcx(ccx.tcx, t.path.span, t.id) { + ast::def_ty(t_id) { + // tjc: will probably need to refer to + // impl or class ty params too + instantiate(ccx, empty_rscope, t.path.span, t_id, t.id, + t.path.types); + t_id + } + _ { + ccx.tcx.sess.span_fatal(t.path.span, + "can only implement interface types"); + } + } +} + +// Parses the programmer's textual representation of a type into our +// internal notion of a type. `getter` is a function that returns the type +// corresponding to a definition ID: +fn ast_ty_to_ty( + self: AC, rscope: RS, &&ast_ty: @ast::ty) -> ty::t { + + fn ast_mt_to_mt( + self: AC, rscope: RS, mt: ast::mt) -> ty::mt { + + ret {ty: ast_ty_to_ty(self, rscope, mt.ty), mutbl: mt.mutbl}; } fn mk_bounded( @@ -1169,6 +1209,7 @@ fn mk_substs(ccx: @crate_ctxt, atps: [ast::ty_param], rp: ast::region_param) fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method, impl_tps: uint, if_m: ty::method, substs: [ty::t], self_ty: ty::t) -> ty::t { + if impl_m.tps != if_m.tps { tcx.sess.span_err(sp, "method `" + if_m.ident + "` has an incompatible set of type parameters"); @@ -1371,39 +1412,37 @@ mod collect { tps: [ast::ty_param], rp: ast::region_param, selfty: ty::t, - t: @ast::ty, - ms: [@ast::method]) { + t: @ast::iface_ref, + ms: [@ast::method]) -> ast::def_id { let tcx = ccx.tcx; let i_bounds = ty_param_bounds(ccx, tps); let my_methods = convert_methods(ccx, ms, rp, i_bounds, selfty); - let iface_ty = ccx.to_ty(empty_rscope, t); - alt ty::get(iface_ty).struct { - ty::ty_iface(did, tys) { - // Store the iface type in the type node - alt check t.node { - ast::ty_path(_, t_id) { - write_ty_to_tcx(tcx, t_id, iface_ty); - } - } - if did.crate == ast::local_crate { - ensure_iface_methods(ccx, did.node); - } - for vec::each(*ty::iface_methods(tcx, did)) {|if_m| - alt vec::find(my_methods, - {|m| if_m.ident == m.mty.ident}) { - some({mty: m, id, span}) { - if m.purity != if_m.purity { - ccx.tcx.sess.span_err( - span, #fmt["method `%s`'s purity \ + let did = instantiate_iface_ref(ccx, t); + // not sure whether it's correct to use empty_rscope + // -- tjc + let tys = vec::map(t.path.types, + {|t| ccx.to_ty(empty_rscope,t)}); + // Store the iface type in the type node + write_ty_to_tcx(tcx, t.id, ty::mk_iface(ccx.tcx, did, tys)); + if did.crate == ast::local_crate { + ensure_iface_methods(ccx, did.node); + } + for vec::each(*ty::iface_methods(tcx, did)) {|if_m| + alt vec::find(my_methods, + {|m| if_m.ident == m.mty.ident}) { + some({mty: m, id, span}) { + if m.purity != if_m.purity { + ccx.tcx.sess.span_err( + span, #fmt["method `%s`'s purity \ not match the iface method's \ purity", m.ident]); - } - let mt = compare_impl_method( + } + let mt = compare_impl_method( ccx.tcx, span, m, vec::len(tps), if_m, tys, selfty); - let old = tcx.tcache.get(local_def(id)); - if old.ty != mt { + let old = tcx.tcache.get(local_def(id)); + if old.ty != mt { tcx.tcache.insert( local_def(id), {bounds: old.bounds, @@ -1412,27 +1451,24 @@ mod collect { write_ty_to_tcx(tcx, id, mt); } } - none { - tcx.sess.span_err(t.span, "missing method `" + + none { + tcx.sess.span_err(t.path.span, "missing method `" + if_m.ident + "`"); - } - } // alt - } // |if_m| - } // for - _ { - tcx.sess.span_fatal(t.span, "can only implement \ - interface types"); - } - } - } + } + } // alt + } // |if_m| + did + } // fn fn convert_class_item(ccx: @crate_ctxt, rp: ast::region_param, 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 */ let tt = ccx.to_ty(type_rscope(rp), v.ty); - #debug("convert_class_item: %s %?", v.ident, v.id); write_ty_to_tcx(ccx.tcx, v.id, tt); } @@ -1485,7 +1521,7 @@ mod collect { ty: selfty}); alt ifce { some(t) { - check_methods_against_iface( + check_methods_against_iface( ccx, tps, ast::rp_none, // NDM iface/impl regions selfty, t, ms); } @@ -1569,29 +1605,16 @@ mod collect { that it claims to implement. */ for ifaces.each { |ifce| - alt lookup_def_tcx(tcx, it.span, ifce.id) { - ast::def_ty(t_id) { - let t = ty::lookup_item_type(tcx, t_id).ty; - alt ty::get(t).struct { - ty::ty_iface(_,_) { - write_ty_to_tcx(tcx, ifce.id, t); - check_methods_against_iface( - ccx, tps, rp, selfty, - @{id: ifce.id, - node: ast::ty_path(ifce.path, ifce.id), - span: ifce.path.span}, - methods); - } - _ { - tcx.sess.span_fatal( - ifce.path.span, - "can only implement interface types"); - } - } - } - _ { tcx.sess.span_err(ifce.path.span, "not an interface \ - type"); } - } + let t_id = check_methods_against_iface(ccx, tps, rp, selfty, + ifce, methods); + // FIXME: This assumes classes only implement + // non-parameterized ifaces. add a test case for + // a class implementing a parameterized iface. + // -- tjc (#1726) + let t = ty::mk_iface(tcx, t_id, []); + write_ty_to_tcx(tcx, ifce.id, t); + // FIXME: likewise, assuming no bounds -- tjc + tcx.tcache.insert(local_def(ifce.id), no_params(t)); } } _ { @@ -1601,7 +1624,7 @@ mod collect { let tpt = ty_of_item(ccx, it); write_ty_to_tcx(tcx, it.id, tpt.ty); } - } + } } fn convert_native(ccx: @crate_ctxt, i: @ast::native_item) { // As above, this call populates the type table with the converted @@ -1696,8 +1719,7 @@ fn require_same_types( alt infer::compare_tys(tcx, t1, t2) { result::ok(()) { true } result::err(terr) { - tcx.sess.span_err( - span, msg() + ": " + + tcx.sess.span_err(span, msg() + ": " + ty::type_err_to_str(tcx, terr)); false } @@ -2368,12 +2390,28 @@ fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id) -> ty_param_substs_and_ty { let tcx = fcx.ccx.tcx; let {n_tps, raw_ty} = if did.crate == ast::local_crate { - alt check tcx.items.get(did.node) { - ast_map::node_item(@{node: ast::item_impl(ts, _, st, _), - _}, _) { - {n_tps: vec::len(ts), + alt check tcx.items.find(did.node) { + some(ast_map::node_item(@{node: ast::item_impl(ts, _, st, _), + _}, _)) { + {n_tps: ts.len(), raw_ty: fcx.to_ty(st)} } + // Node doesn't map to an impl. It might map to a class. + some(ast_map::node_item(@{node: ast::item_class(ts, + _,_,_,rp), id: class_id, _},_)) { + /* If the impl is a class, the self ty is just the class ty + (doing a no-op subst for the ty params; in the next step, + we substitute in fresh vars for them) + */ + {n_tps: ts.len(), + raw_ty: ty::mk_class(tcx, local_def(class_id), + {self_r: alt rp { + ast::rp_self { some(fcx.next_region_var()) } + ast::rp_none { none }}, + tps: ty::ty_params_to_tys(tcx, ts)})} + } + _ { tcx.sess.bug("impl_self_ty: unbound item or item that \ + doesn't have a self_ty"); } } } else { let ity = ty::lookup_item_type(tcx, did); @@ -4300,6 +4338,8 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) { check_bare_fn(class_ccx, ctor.node.dec, ctor.node.body, ctor.node.id, some(class_t)); + // Write the ctor's self's type + write_ty_to_tcx(tcx, ctor.node.self_id, class_t); // typecheck the members for members.each {|m| check_class_member(class_ccx, class_t, m); } @@ -4403,6 +4443,10 @@ mod vtable { @result } + /* + Look up the vtable to use when treating an item of type + as if it has type + */ fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span, ty: ty::t, iface_ty: ty::t, allow_unsafe: bool) -> vtable_origin { @@ -4449,14 +4493,17 @@ mod vtable { for list::each(isc) {|impls| let mut found = none; for vec::each(*impls) {|im| + /* What iface does this item implement? */ let match = alt ty::impl_iface(tcx, im.did) { some(ity) { alt check ty::get(ity).struct { + /* Does it match the one we're searching for? */ ty::ty_iface(id, _) { id == iface_id } } } _ { false } }; + /* Found a matching iface */ if match { let {substs: substs, ty: self_ty} = impl_self_ty(fcx, im.did); @@ -4566,10 +4613,21 @@ mod vtable { let target_ty = fcx.expr_ty(ex); alt ty::get(target_ty).struct { ty::ty_iface(_, _) { + /* Casting to an interface type. + Look up all impls for the cast expr... + */ let impls = cx.impl_map.get(ex.id); + /* + Look up vtables for the type we're casting to, + passing in the source and target type + */ let vtable = lookup_vtable(fcx, impls, ex.span, fcx.expr_ty(src), target_ty, true); + /* + Map this expression to that vtable (that is: "ex has + vtable ") + */ cx.vtable_map.insert(ex.id, @[vtable]); } _ {} diff --git a/src/rustdoc/tystr_pass.rs b/src/rustdoc/tystr_pass.rs index 3e5ac07f093..58f147ca6ff 100644 --- a/src/rustdoc/tystr_pass.rs +++ b/src/rustdoc/tystr_pass.rs @@ -249,8 +249,8 @@ fn fold_impl( ast_map::node_item(@{ node: ast::item_impl(_, iface_ty, self_ty, _), _ }, _) { - let iface_ty = option::map(iface_ty) {|iface_ty| - pprust::ty_to_str(iface_ty) + let iface_ty = option::map(iface_ty) {|p| + pprust::path_to_str(p.path) }; (iface_ty, some(pprust::ty_to_str(self_ty))) } diff --git a/src/test/compile-fail/class-cast-to-iface.rs b/src/test/compile-fail/class-cast-to-iface.rs new file mode 100644 index 00000000000..faebae34694 --- /dev/null +++ b/src/test/compile-fail/class-cast-to-iface.rs @@ -0,0 +1,42 @@ +// error-pattern: attempted access of field eat on type noisy +iface noisy { + fn speak(); +} + +class cat implements noisy { + priv { + let mut meows : uint; + fn meow() { + #error("Meow"); + self.meows += 1u; + if self.meows % 5u == 0u { + self.how_hungry += 1; + } + } + } + + let mut how_hungry : int; + let name : str; + + new(in_x : uint, in_y : int, in_name: str) + { self.meows = in_x; self.how_hungry = in_y; self.name = in_name; } + + fn speak() { self.meow(); } + + fn eat() -> bool { + if self.how_hungry > 0 { + #error("OM NOM NOM"); + self.how_hungry -= 2; + ret true; + } + else { + #error("Not hungry!"); + ret false; + } + } +} + +fn main() { + let nyan : noisy = cat(0u, 2, "nyan") as noisy; + nyan.eat(); +} \ No newline at end of file diff --git a/src/test/compile-fail/class-implements-int.rs b/src/test/compile-fail/class-implements-int.rs index b4111ef93b3..70eca5e0742 100644 --- a/src/test/compile-fail/class-implements-int.rs +++ b/src/test/compile-fail/class-implements-int.rs @@ -1,5 +1,11 @@ -// error-pattern:not an interface type -class cat implements int { +// xfail-test +/* + tjc: currently this results in a memory leak after a call to + span_fatal in typeck. I think it's the same issue as #2272, because + if I make type_needs_unwind_cleanup always return true, the test passes. + FIXME: Un-xfail this when #2272 is fixed. + */ +class cat implements int { //! ERROR can only implement interface types let meows: uint; new(in_x : uint) { self.meows = in_x; } } diff --git a/src/test/run-pass/class-cast-to-iface-multiple-types.rs b/src/test/run-pass/class-cast-to-iface-multiple-types.rs new file mode 100644 index 00000000000..d9129fae804 --- /dev/null +++ b/src/test/run-pass/class-cast-to-iface-multiple-types.rs @@ -0,0 +1,64 @@ +iface noisy { + fn speak() -> int; +} + +class dog implements noisy { + priv { + let barks : @mut uint; + fn bark() -> int { + #debug("Woof %u %d", *self.barks, *self.volume); + *self.barks += 1u; + if *self.barks % 3u == 0u { + *self.volume += 1; + } + if *self.barks % 10u == 0u { + *self.volume -= 2; + } + #debug("Grrr %u %d", *self.barks, *self.volume); + *self.volume + } + } + + let volume : @mut int; + + new() { self.volume = @mut 0; self.barks = @mut 0u; } + + fn speak() -> int { self.bark() } +} + +class cat implements noisy { + priv { + let meows : @mut uint; + fn meow() -> uint { + #debug("Meow"); + *self.meows += 1u; + if *self.meows % 5u == 0u { + *self.how_hungry += 1; + } + *self.meows + } + } + + let how_hungry : @mut int; + let name : str; + + new(in_x : uint, in_y : int, in_name: str) + { self.meows = @mut in_x; self.how_hungry = @mut in_y; + self.name = in_name; } + + fn speak() -> int { self.meow() as int } + fn meow_count() -> uint { *self.meows } +} + +fn annoy_neighbors(critter: T) { + uint::range(0u, 10u) {|i| critter.speak(); } +} + +fn main() { + let nyan : cat = cat(0u, 2, "nyan"); + let whitefang : dog = dog(); + annoy_neighbors(nyan as noisy); + annoy_neighbors(whitefang as noisy); + assert(nyan.meow_count() == 10u); + assert(*whitefang.volume == 1); +} \ No newline at end of file diff --git a/src/test/run-pass/class-cast-to-iface.rs b/src/test/run-pass/class-cast-to-iface.rs new file mode 100644 index 00000000000..90b3d67cb59 --- /dev/null +++ b/src/test/run-pass/class-cast-to-iface.rs @@ -0,0 +1,41 @@ +iface noisy { + fn speak(); +} + +class cat implements noisy { + priv { + let mut meows : uint; + fn meow() { + #error("Meow"); + self.meows += 1u; + if self.meows % 5u == 0u { + self.how_hungry += 1; + } + } + } + + let mut how_hungry : int; + let name : str; + + new(in_x : uint, in_y : int, in_name: str) + { self.meows = in_x; self.how_hungry = in_y; self.name = in_name; } + + fn speak() { self.meow(); } + + fn eat() -> bool { + if self.how_hungry > 0 { + #error("OM NOM NOM"); + self.how_hungry -= 2; + ret true; + } + else { + #error("Not hungry!"); + ret false; + } + } +} + +fn main() { + let nyan : noisy = cat(0u, 2, "nyan") as noisy; + nyan.speak(); +} \ No newline at end of file diff --git a/src/test/run-pass/class-implements-multiple-ifaces.rs b/src/test/run-pass/class-implements-multiple-ifaces.rs new file mode 100644 index 00000000000..5a238a46ed8 --- /dev/null +++ b/src/test/run-pass/class-implements-multiple-ifaces.rs @@ -0,0 +1,118 @@ +use std; +import std::map::*; +import vec::*; + +enum furniture { chair, couch, bed } +enum body_part { finger, toe, nose, ear } + +iface noisy { + fn speak() -> int; +} + +iface scratchy { + fn scratch() -> option; +} + +iface bitey { + fn bite() -> body_part; +} + +fn vec_includes(xs: [T], x: T) -> bool { + for each(xs) {|y| if y == x { ret true; }} + ret false; +} + +// vtables other than the 1st one don't seem to work +class cat implements noisy, scratchy, bitey { + priv { + let meows : @mut uint; + let scratched : @mut [furniture]; + let bite_counts : hashmap; + + fn meow() -> uint { + #debug("Meow: %u", *self.meows); + *self.meows += 1u; + if *self.meows % 5u == 0u { + *self.how_hungry += 1; + } + *self.meows + } + } + + let how_hungry : @mut int; + let name : str; + + new(in_x : uint, in_y : int, in_name: str) + { self.meows = @mut in_x; self.how_hungry = @mut in_y; + self.name = in_name; self.scratched = @mut []; + let hsher: hashfn = + fn@(p: body_part) -> uint { int::hash(p as int) }; + let eqer : eqfn = + fn@(p: body_part, q: body_part) -> bool { p == q }; + let t : hashmap = + hashmap::(hsher, eqer); + self.bite_counts = t; + iter([finger, toe, nose, ear]) {|p| + self.bite_counts.insert(p, 0u); + }; + } + + fn speak() -> int { self.meow() as int } + fn meow_count() -> uint { *self.meows } + fn scratch() -> option { + let all = [chair, couch, bed]; + log(error, *(self.scratched)); + let mut rslt = none; + for each(all) {|thing| if !vec_includes(*(self.scratched), thing) { + *self.scratched += [thing]; + ret some(thing); }} + rslt + } + fn bite() -> body_part { + #error("In bite()"); + let all = [toe, nose, ear]; + let mut min = finger; + iter(all) {|next| + #debug("min = %?", min); + if self.bite_counts.get(next) < self.bite_counts.get(min) { + min = next; + }}; + self.bite_counts.insert(min, self.bite_counts.get(min) + 1u); + #debug("Bit %?", min); + min + } +} + +fn annoy_neighbors(critter: T) { + uint::range(0u, 10u) {|i| + let what = critter.speak(); + #debug("%u %d", i, what); + } +} + +fn bite_everything(critter: T) -> bool { + let mut left : [body_part] = [finger, toe, nose, ear]; + while vec::len(left) > 0u { + let part = critter.bite(); + #debug("%? %?", left, part); + if vec_includes(left, part) { + left = vec::filter(left, {|p| p != part}); + } + else { + ret false; + } + } + true +} + +fn scratched_something(critter: T) -> bool { + option::is_some(critter.scratch()) +} + +fn main() { + let nyan : cat = cat(0u, 2, "nyan"); + annoy_neighbors(nyan as noisy); + assert(nyan.meow_count() == 10u); + assert(bite_everything(nyan as bitey)); + assert(scratched_something(nyan as scratchy)); +} \ No newline at end of file