Allow classes to be cast to ifaces that are in the same crate
I had to xfail one existing test case (class-implements-int) because, I think, of the same bug described in #2272.
This commit is contained in:
parent
1c39fda0ea
commit
f7641286b2
@ -448,7 +448,7 @@ fn map<T, U>(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<T, U>(v: [T], f: fn(T) -> [U]) -> [U] {
|
||||
|
@ -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]),
|
||||
}
|
||||
|
||||
|
@ -2,20 +2,20 @@ import codemap::span;
|
||||
import ast::*;
|
||||
|
||||
fn spanned<T: copy>(lo: uint, hi: uint, t: T) -> spanned<T> {
|
||||
ret respan(mk_sp(lo, hi), t);
|
||||
respan(mk_sp(lo, hi), t)
|
||||
}
|
||||
|
||||
fn respan<T: copy>(sp: span, t: T) -> spanned<T> {
|
||||
ret {node: t, span: sp};
|
||||
{node: t, span: sp}
|
||||
}
|
||||
|
||||
fn dummy_spanned<T: copy>(t: T) -> spanned<T> {
|
||||
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;
|
||||
|
@ -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,
|
||||
|
@ -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<T> 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;
|
||||
|
@ -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);
|
||||
|
@ -134,7 +134,7 @@ fn visit_item<E>(i: @item, e: E, v: vt<E>) {
|
||||
}
|
||||
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)
|
||||
|
@ -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<ty::t> {
|
||||
// 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<ty::t> {
|
||||
let cstore = tcx.sess.cstore;
|
||||
let cdata = cstore::get_crate_data(cstore, def.crate);
|
||||
decoder::get_impl_iface(cdata, def.node, tcx)
|
||||
|
@ -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<ty::t> {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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); }
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
|
@ -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<def>) {
|
||||
}
|
||||
}
|
||||
|
||||
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<scopes>) {
|
||||
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 <i>, adds one record to the mutable vec
|
||||
<impls> 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<ident>,
|
||||
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}})}];
|
||||
}
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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<t> {
|
||||
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)
|
||||
|
@ -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<ast::node_id, method_origin>;
|
||||
|
||||
// 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<T: quux, baz, bar>(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<AC: ast_conv, RS: region_scope>(
|
||||
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<AC: ast_conv, RS: region_scope copy>(
|
||||
self: AC, rscope: RS, &&ast_ty: @ast::ty) -> ty::t {
|
||||
|
||||
fn ast_mt_to_mt<AC: ast_conv, RS: region_scope copy>(
|
||||
self: AC, rscope: RS, mt: ast::mt) -> ty::mt {
|
||||
|
||||
ret {ty: ast_ty_to_ty(self, rscope, mt.ty), mutbl: mt.mutbl};
|
||||
}
|
||||
|
||||
fn instantiate<AC: ast_conv, RS: region_scope copy>(
|
||||
fn instantiate<AC: ast_conv, RS: region_scope copy>(
|
||||
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<AC: ast_conv, RS: region_scope copy>(
|
||||
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<AC: ast_conv, RS: region_scope copy>(
|
||||
self: AC, rscope: RS, &&ast_ty: @ast::ty) -> ty::t {
|
||||
|
||||
fn ast_mt_to_mt<AC: ast_conv, RS: region_scope copy>(
|
||||
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<AC: ast_conv, RS: region_scope copy>(
|
||||
@ -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 <t>
|
||||
as if it has type <iface_ty>
|
||||
*/
|
||||
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 <vtable>")
|
||||
*/
|
||||
cx.vtable_map.insert(ex.id, @[vtable]);
|
||||
}
|
||||
_ {}
|
||||
|
@ -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)))
|
||||
}
|
||||
|
42
src/test/compile-fail/class-cast-to-iface.rs
Normal file
42
src/test/compile-fail/class-cast-to-iface.rs
Normal file
@ -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();
|
||||
}
|
@ -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; }
|
||||
}
|
||||
|
64
src/test/run-pass/class-cast-to-iface-multiple-types.rs
Normal file
64
src/test/run-pass/class-cast-to-iface-multiple-types.rs
Normal file
@ -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<T: noisy>(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);
|
||||
}
|
41
src/test/run-pass/class-cast-to-iface.rs
Normal file
41
src/test/run-pass/class-cast-to-iface.rs
Normal file
@ -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();
|
||||
}
|
118
src/test/run-pass/class-implements-multiple-ifaces.rs
Normal file
118
src/test/run-pass/class-implements-multiple-ifaces.rs
Normal file
@ -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<furniture>;
|
||||
}
|
||||
|
||||
iface bitey {
|
||||
fn bite() -> body_part;
|
||||
}
|
||||
|
||||
fn vec_includes<T>(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<body_part, uint>;
|
||||
|
||||
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<body_part> =
|
||||
fn@(p: body_part) -> uint { int::hash(p as int) };
|
||||
let eqer : eqfn<body_part> =
|
||||
fn@(p: body_part, q: body_part) -> bool { p == q };
|
||||
let t : hashmap<body_part, uint> =
|
||||
hashmap::<body_part, uint>(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<furniture> {
|
||||
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<T: noisy>(critter: T) {
|
||||
uint::range(0u, 10u) {|i|
|
||||
let what = critter.speak();
|
||||
#debug("%u %d", i, what);
|
||||
}
|
||||
}
|
||||
|
||||
fn bite_everything<T: bitey>(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<T: scratchy>(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));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user