#[auto_serialize]
type attribute_ = {style: attr_style, value: meta_item};
+#[auto_serialize]
+type iface_ref = {path: @path, id: node_id};
+
#[auto_serialize]
type item = {ident: ident, attrs: [attribute],
id: node_id, node: item_, span: span};
item_res(fn_decl /* dtor */, [ty_param], blk /* dtor body */,
node_id /* dtor id */, node_id /* ctor id */),
item_class([ty_param], /* ty params for class */
+ [iface_ref], /* ifaces this class implements */
[@class_member], /* methods, etc. */
/* (not including ctor) */
class_ctor
item_enum(vec::map(variants, fld.fold_variant),
fold_ty_params(typms, fld))
}
- item_class(typms, items, ctor) {
+ item_class(typms, ifaces, items, ctor) {
let ctor_body = fld.fold_block(ctor.node.body);
let ctor_decl = fold_fn_decl(ctor.node.dec, fld);
let ctor_id = fld.new_id(ctor.node.id);
- item_class(typms,
+ item_class(typms, vec::map(ifaces, {|p|
+ {path: fld.fold_path(p.path),
+ id: fld.new_id(p.id)}}),
vec::map(items, fld.fold_class_item),
{node: {body: ctor_body,
dec: ctor_decl,
@spanned(s.lo, s.hi, p_)
}
+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)
+}
+
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 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")
+ { parse_iface_ref_list(p) }
+ else { [] };
expect(p, token::LBRACE);
let mut ms: [@ast::class_member] = [];
let ctor_id = p.get_id();
p.bump();
alt the_ctor {
some((ct_d, ct_b, ct_s)) {
- ret mk_item(p, lo, p.last_span.hi,
- class_name,
- ast::item_class(ty_params, ms,
+ ret mk_item(p, lo, p.last_span.hi, class_name,
+ ast::item_class(ty_params, ifaces, ms,
{node: {id: ctor_id,
self_id: p.get_id(),
dec: ct_d,
bclose(s, item.span);
}
}
- ast::item_class(tps,items,ctor) {
+ ast::item_class(tps,ifaces,items,ctor) {
head(s, "class");
word_nbsp(s, item.ident);
print_type_params(s, tps);
+ word_space(s, "implements");
+ commasep(s, inconsistent, ifaces, {|s, p|
+ print_path(s, p.path, false)});
bopen(s);
hardbreak_if_not_bol(s);
maybe_print_comment(s, ctor.span.lo);
visit_method_helper(m, e, v)
}
}
- item_class(tps, members, ctor) {
+ item_class(tps, ifaces, members, ctor) {
v.visit_ty_params(tps, e, v);
for members.each {|m|
v.visit_class_item(m, e, v);
}
+ for ifaces.each {|p| visit_path(p.path, e, v); }
visit_class_ctor_helper(ctor, i.ident, tps,
ast_util::local_def(i.id), e, v);
}
encode_def_id(ebml_w, local_def(it.id));
ebml_w.end_tag();
}
- item_class(tps,items,ctor) {
+ item_class(_, _, 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_enum_variant_info(ecx, ebml_w, item.id, variants,
path, index, tps);
}
- item_class(tps,items,ctor) {
+ item_class(tps, _ifaces, items,ctor) {
/* First, encode the fields and methods
These come first because we need to write them to make
the index, and the index needs to be in the item for the
encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id));
encode_name(ebml_w, item.ident);
encode_path(ebml_w, path, ast_map::path_name(item.ident));
-
+ /* FIXME: encode ifaces */
/* Encode def_ids for each field and method
for methods, write all the stuff get_iface_method
needs to know*/
encode_info_for_item(ecx, ebml_w, i, index, *pt);
/* encode ctor, then encode items */
alt i.node {
- item_class(tps,_,ctor) {
+ item_class(tps,_,_,ctor) {
/* this is assuming that ctors aren't inlined...
probably shouldn't assume that */
#debug("encoding info for ctor %s %d", i.ident,
cx.map.insert(nitem.id, node_native_item(nitem, abi, @cx.path));
}
}
- item_class(_, items, ctor) {
+ item_class(_, _, items, ctor) {
let d_id = ast_util::local_def(i.id);
let p = extend(cx, i.ident);
for items.each {|ci|
fn visit_item(item: @item, &&cx: @ctx, v: visit::vt<@ctx>) {
alt item.node {
- item_class(tps, items, ctor) {
+ item_class(tps, _, items, ctor) {
v.visit_ty_params(tps, cx, v);
vec::map::<@class_member, ()>(items,
{|i| v.visit_class_item(i, cx, v); });
{parent: pa_fn_item(item.id),
scope: cx.scope.binding_subscope(item.id)}
}
- ast::item_impl(_, _, _, _) | ast::item_class(_, _, _) {
+ ast::item_impl(_, _, _, _) | ast::item_class(_, _, _, _) {
{parent: pa_item(item.id),
scope: cx.scope.self_subscope(item.id)}
}
e.used_imports.track = true;
let v =
@{visit_native_item: visit_native_item_with_scope,
- visit_item: bind visit_item_with_scope(e, _, _, _),
+ visit_item: bind walk_item(e, _, _, _),
visit_block: visit_block_with_scope,
visit_decl: visit_decl_with_scope,
visit_arm: visit_arm_with_scope,
e.used_imports.track = false;
e.sess.abort_if_errors();
+ fn walk_item(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) {
+ visit_item_with_scope(e, i, sc, v);
+ /*
+ Resolve the ifaces that a class implements; do nothing for
+ non-class items
+ */
+ 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.node,
+ ns_type))};
+ }
+ _ {}
+ }
+ }
+
fn walk_expr(e: @env, exp: @ast::expr, sc: scopes, v: vt<scopes>) {
visit::visit_expr(exp, sc, v);
alt exp.node {
v.visit_ty(m.decl.output, msc, v);
}
}
- ast::item_class(tps, members, ctor) {
+ ast::item_class(tps, ifaces, members, ctor) {
visit::visit_ty_params(tps, sc, v);
// Can maybe skip this now that we require self on class fields
let class_scope = cons(scope_item(i), @sc);
ast::item_native_mod(m) {
ret lookup_in_local_native_mod(e, it.id, sp, name, ns);
}
- ast::item_class(tps, members, ctor) {
+ ast::item_class(tps, _, members, ctor) {
if ns == ns_type {
ret lookup_in_ty_params(e, name, tps);
}
_ { }
}
}
- ast::item_class(_, _, _) {
+ ast::item_class(_, _, _, _) {
if ns == ns_type {
ret some(ast::def_class(local_def(i.id)));
}
fn lookup_in_local_mod(e: env, node_id: node_id, sp: span, id: ident,
ns: namespace, dr: dir) -> option<def> {
- let info = e.mod_map.get(node_id);
+ let info = alt e.mod_map.find(node_id) {
+ some(x) { x }
+ none { e.sess.span_bug(sp, #fmt("lookup_in_local_mod: \
+ module %d not in mod_map", node_id)); }
+ };
if dr == outside && !is_exported(e, id, info) {
// if we're in a native mod, then dr==inside, so info.m is some _mod
ret none; // name is not visible
variant_idx += 1u;
}
}
- ast::item_class(tps, items, ctor) {
+ ast::item_class(tps, _, items, ctor) {
// add the class name itself
add_to_index(index, it.ident, mie_item(it));
// add the constructor decl
};
native::trans_native_mod(ccx, native_mod, abi);
}
- ast::item_class(tps, items, ctor) {
+ ast::item_class(tps, _ifaces, items, ctor) {
if tps.len() == 0u {
let psubsts = {tys: ty::ty_params_to_tys(ccx.tcx, tps),
+ // FIXME: vtables have to get filled in depending
+ // on ifaces
vtables: none,
bounds: @[]};
trans_class_ctor(ccx, *path, ctor.node.dec, ctor.node.body,
}
}
}
- item_class(tps, items, ctor) {
+ item_class(tps, _ifaces, items, ctor) {
cx.rmap.insert(ctor.node.id, ());
for vec::each(items) {|item|
alt item.node {
ccx: ccx};
find_pre_post_fn(fcx, body);
}
- item_class(_,_,_) {
- fail "find_pre_post_item: implement item_class";
+ item_class(_,_,_,_) {
+ fail "find_pre_post_item: shouldn't be called on item_class";
}
item_impl(_, _, _, ms) {
for ms.each {|m| find_pre_post_method(ccx, m); }
alt cx.items.find(did.node) {
some(ast_map::node_item(i,_)) {
alt i.node {
- ast::item_class(_, items, _) {
+ ast::item_class(_, _, items, _) {
class_field_tys(items)
}
_ { cx.sess.bug("class ID bound to non-class"); }
fn lookup_class_method_ids(cx: ctxt, did: ast::def_id)
: is_local(did) -> [{name: ident, id: node_id, privacy: privacy}] {
alt cx.items.find(did.node) {
- some(ast_map::node_item(@{node: item_class(_,items,_), _}, _)) {
+ some(ast_map::node_item(@{node: item_class(_,_,items,_), _}, _)) {
let (_,ms) = split_class_items(items);
vec::map(ms, {|m| {name: m.ident, id: m.id,
privacy: m.privacy}})
}
}
-fn lookup_def(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> ast::def {
- alt fcx.ccx.tcx.def_map.find(id) {
+fn lookup_def_tcx(tcx: ty::ctxt, sp: span, id: ast::node_id) -> ast::def {
+ alt tcx.def_map.find(id) {
some(x) { x }
_ {
- fcx.ccx.tcx.sess.span_fatal(sp,
- "internal error looking up a definition")
+ tcx.sess.span_fatal(sp, "internal error looking up a definition")
}
}
}
+fn lookup_def_ccx(ccx: @crate_ctxt, sp: span, id: ast::node_id) -> ast::def {
+ lookup_def_tcx(ccx.tcx, sp, id)
+}
+
+fn lookup_def(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> ast::def {
+ lookup_def_ccx(fcx.ccx, sp, id)
+}
+
// Returns the type parameter count and the type for the given definition.
fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
ty_param_bounds_and_ty {
tcx.tcache.insert(local_def(it.id), tpt);
ret tpt;
}
- ast::item_class(tps,_,_) {
+ ast::item_class(tps,_,_,_) {
let {bounds,params} = mk_ty_params(tcx, tps);
let t = ty::mk_class(tcx, local_def(it.id), params);
let tpt = {bounds: bounds, ty: t};
}
}
+
// Item collection - a pair of bootstrap passes:
//
// (1) Collect the IDs of all type items (typedefs) and store them in a table.
store_methods::<ast::ty_method>(tcx, id, ms, {|m|
ty_of_ty_method(tcx, m_collect, m)});
}
- ast_map::node_item(@{node: ast::item_class(_,its,_), _}, _) {
+ ast_map::node_item(@{node: ast::item_class(_,_,its,_), _}, _) {
let (_,ms) = split_class_items(its);
// All methods need to be stored, since lookup_method
// relies on the same method cache for self-calls
}
}
}
+ fn check_methods_against_iface(tcx: ty::ctxt, tps: [ast::ty_param],
+ selfty: ty::t, t: @ast::ty, ms: [@ast::method]) {
+ let i_bounds = ty_param_bounds(tcx, m_collect, tps);
+ let my_methods = convert_methods(tcx, ms, i_bounds, some(selfty));
+ let iface_ty = ast_ty_to_ty(tcx, m_collect, 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(tcx, 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 {
+ tcx.sess.span_err(
+ span, "method `" + m.ident + "`'s purity \
+ not match the iface method's \
+ purity");
+ }
+ let mt = compare_impl_method(
+ tcx, span, m, vec::len(tps), if_m, tys,
+ selfty);
+ let old = tcx.tcache.get(local_def(id));
+ if old.ty != mt {
+ tcx.tcache.insert(local_def(id),
+ {bounds: old.bounds,
+ ty: mt});
+ write_ty_to_tcx(tcx, id, mt);
+ }
+ }
+ none {
+ tcx.sess.span_err(t.span, "missing method `" +
+ if_m.ident + "`");
+ }
+ } // alt
+ } // |if_m|
+ } // for
+ _ {
+ tcx.sess.span_fatal(t.span, "can only implement \
+ interface types");
+ }
+ }
+ }
+
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 &
write_ty_to_tcx(tcx, it.id, selfty);
tcx.tcache.insert(local_def(it.id), {bounds: i_bounds,
ty: selfty});
- 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);
- 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(tcx, 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 {
- tcx.sess.span_err(
- span, "method `" + m.ident + "`'s purity \
- not match the iface method's \
- purity");
- }
- let mt = compare_impl_method(
- tcx, span, m, vec::len(tps), if_m, tys,
- selfty);
- let old = tcx.tcache.get(local_def(id));
- if old.ty != mt {
- tcx.tcache.insert(local_def(id),
- {bounds: old.bounds,
- ty: mt});
- write_ty_to_tcx(tcx, id, mt);
- }
- }
- none {
- tcx.sess.span_err(t.span, "missing method `" +
- if_m.ident + "`");
- }
- }
- }
- }
- _ {
- tcx.sess.span_fatal(t.span, "can only implement \
- interface types");
- }
- }
+ check_methods_against_iface(tcx, tps, selfty,
+ t, ms); }
+ _ {
+ // Still have to do this to write method types
+ // into the table
+ convert_methods(tcx, ms, i_bounds, some(selfty));
}
- _ {}
}
}
ast::item_res(decl, tps, _, dtor_id, ctor_id) {
write_ty_to_tcx(tcx, it.id, tpt.ty);
ensure_iface_methods(tcx, it.id);
}
- ast::item_class(tps, members, ctor) {
+ ast::item_class(tps, ifaces, members, ctor) {
// Write the class type
let tpt = ty_of_item(tcx, m_collect, it);
write_ty_to_tcx(tcx, it.id, tpt.ty);
// Need to convert all methods so we can check internal
// references to private methods
convert_methods(tcx, methods, @[], some(selfty));
+ /*
+ Finally, check that the class really implements the ifaces
+ 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(tcx, tps, 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"); }
+ };
+ }
}
_ {
// This call populates the type cache with the converted type
let self_ty = replace_self_region(ccx.tcx, self_region, self_ty);
for ms.each {|m| check_method(ccx, m, self_ty);}
}
- ast::item_class(tps, members, ctor) {
- let cid = some(it.id);
- let class_t = ty::node_id_to_type(ccx.tcx, it.id);
+ ast::item_class(tps, ifaces, members, ctor) {
+ let cid = some(it.id), tcx = ccx.tcx;
+ let class_t = ty::node_id_to_type(tcx, it.id);
let members_info = class_types(ccx, members);
// can also ditch the enclosing_class stuff once we move to self
// FIXME
--- /dev/null
+// error-pattern:unresolved typename: nonexistent
+class cat implements nonexistent {
+ let meows: uint;
+ new(in_x : uint) { self.meows = in_x; }
+}
+
+fn main() {
+ let nyan = cat(0u);
+}
\ No newline at end of file
--- /dev/null
+// error-pattern:not an interface type
+class cat implements int {
+ let meows: uint;
+ new(in_x : uint) { self.meows = in_x; }
+}
+
+fn main() {
+ let nyan = cat(0u);
+}
\ No newline at end of file
--- /dev/null
+// error-pattern:missing method `eat`
+iface animal {
+ fn eat();
+}
+
+class cat implements animal {
+ let meows: uint;
+ new(in_x : uint) { self.meows = in_x; }
+}
+
+fn main() {
+ let nyan = cat(0u);
+}
\ No newline at end of file
--- /dev/null
+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 mutable 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 = cat(0u, 2, "nyan");
+ nyan.eat();
+ assert(!nyan.eat());
+ uint::range(1u, 10u, {|_i| nyan.speak(); });
+ assert(nyan.eat());
+}
\ No newline at end of file