In resolve, visit the path in an iface ref
Necessary to resolve any type arguments in a ref to a parameterized
iface. This meant that, for example:
class A implements B<int> { ...
didn't work before, because the "int" in B's argument wasn't getting
visited, and thus wasn't getting resolved. Now it works.
Partially addresses Issue #2288, but I also want to check that class
ty params can appear as the type arguments to ifaces (for example,
class A<T> implements B<T> {...
should work.)
This commit is contained in:
parent
1226669172
commit
4f105e4025
6 changed files with 109 additions and 12 deletions
|
|
@ -242,6 +242,9 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
|
|||
ty::ty_class(parent_id, ts) {
|
||||
/* ...and if it has a class type, prepend the
|
||||
class bounds onto the method bounds */
|
||||
/* n.b. this code is very likely sketchy --
|
||||
currently, class-impl-very-parameterized-iface
|
||||
fails here and is thus xfailed */
|
||||
bounds =
|
||||
@(*ty::lookup_item_type(cx.tcx, parent_id).bounds
|
||||
+ *bounds);
|
||||
|
|
@ -260,6 +263,13 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
|
|||
}
|
||||
}
|
||||
};
|
||||
if vec::len(ts) != vec::len(*bounds) {
|
||||
// Fail earlier to make debugging easier
|
||||
fail #fmt("Internal error: in kind::check_expr, length \
|
||||
mismatch between actual and declared bounds: actual = \
|
||||
%s (%u tys), declared = %s (%u tys)", ts, ts.len(),
|
||||
*bounds, bounds.len());
|
||||
}
|
||||
vec::iter2(ts, *bounds) {|ty, bound|
|
||||
check_bounds(cx, e.span, ty, bound)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -430,17 +430,17 @@ fn resolve_names(e: @env, c: @ast::crate) {
|
|||
|
||||
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| resolve_iface_ref(p, sc, e) ;}
|
||||
}
|
||||
/* At this point, the code knows what ifaces the iface refs
|
||||
refer to, so it's possible to resolve them.
|
||||
*/
|
||||
ast::item_impl(_, _, ifce, _, _) {
|
||||
ifce.iter { |p| resolve_iface_ref(p, sc, e); }
|
||||
ifce.iter {|p| resolve_iface_ref(p, sc, e);}
|
||||
}
|
||||
ast::item_class(_, ifaces, _, _, _) {
|
||||
for ifaces.each {|p|
|
||||
resolve_iface_ref(p, sc, e);
|
||||
}
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
|
|
@ -529,6 +529,10 @@ fn resolve_names(e: @env, c: @ast::crate) {
|
|||
|
||||
|
||||
// Visit helper functions
|
||||
/*
|
||||
This is used in more than one context, thus should only call generic
|
||||
visit methods. Called both from map_crate and resolve_names.
|
||||
*/
|
||||
fn visit_item_with_scope(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) {
|
||||
// Some magic here. Items with the !resolve_unexported attribute
|
||||
// cause us to consider every name to be exported when resolving their
|
||||
|
|
@ -567,6 +571,12 @@ fn visit_item_with_scope(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) {
|
|||
/* visit the constructor... */
|
||||
let ctor_scope = cons(scope_method(ctor.node.self_id, tps),
|
||||
@class_scope);
|
||||
/*
|
||||
but, I should visit the ifaces refs in the class scope, no?
|
||||
*/
|
||||
for ifaces.each {|p|
|
||||
visit::visit_path(p.path, class_scope, v);
|
||||
}
|
||||
visit_fn_with_scope(e, visit::fk_item_fn(i.ident, tps), ctor.node.dec,
|
||||
ctor.node.body, ctor.span, ctor.node.id,
|
||||
ctor_scope, v);
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ import metadata::csearch;
|
|||
import util::common::*;
|
||||
import util::ppaux::region_to_str;
|
||||
import util::ppaux::vstore_to_str;
|
||||
import util::ppaux::ty_to_str;
|
||||
import util::ppaux::ty_constr_to_str;
|
||||
import util::ppaux::{ty_to_str, tys_to_str, ty_constr_to_str};
|
||||
import syntax::print::pprust::*;
|
||||
|
||||
export ty_vid, region_vid, vid;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import result::{result, extensions};
|
||||
import syntax::{ast, ast_util};
|
||||
import ast::spanned;
|
||||
import ast_map::node_id_to_str;
|
||||
import syntax::ast_util::{local_def, respan, split_class_items};
|
||||
import syntax::visit;
|
||||
import metadata::csearch;
|
||||
|
|
@ -12,7 +13,7 @@ import middle::ty;
|
|||
import middle::ty::{arg, field, node_type_table, mk_nil,
|
||||
ty_param_bounds_and_ty, lookup_public_fields};
|
||||
import middle::ty::{ty_vid, region_vid, vid};
|
||||
import util::ppaux::{ty_to_str, region_to_str,
|
||||
import util::ppaux::{ty_to_str, tys_to_str, region_to_str,
|
||||
bound_region_to_str, vstore_to_str};
|
||||
import std::smallintmap;
|
||||
import std::smallintmap::map;
|
||||
|
|
@ -1639,6 +1640,8 @@ mod collect {
|
|||
let fty = ty::mk_fn(tcx, mty.fty);
|
||||
tcx.tcache.insert(
|
||||
local_def(m.id),
|
||||
// n.b. This code is kind of sketchy (concat'ing i_bounds
|
||||
// with bounds), but removing *i_bounds breaks other stuff
|
||||
{bounds: @(*i_bounds + *bounds), rp: rp, ty: fty});
|
||||
write_ty_to_tcx(tcx, m.id, fty);
|
||||
{mty: mty, id: m.id, span: m.span}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,12 @@ fn vstore_to_str(cx: ctxt, vs: ty::vstore) -> str {
|
|||
}
|
||||
}
|
||||
|
||||
fn tys_to_str(cx: ctxt, ts: [t]) -> str {
|
||||
let mut rs = "";
|
||||
for ts.each {|t| rs += ty_to_str(cx, t); }
|
||||
rs
|
||||
}
|
||||
|
||||
fn ty_to_str(cx: ctxt, typ: t) -> str {
|
||||
fn fn_input_to_str(cx: ctxt, input: {mode: ast::mode, ty: t}) ->
|
||||
str {
|
||||
|
|
|
|||
69
src/test/run-pass/class-impl-parameterized-iface.rs
Normal file
69
src/test/run-pass/class-impl-parameterized-iface.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// xfail-fast
|
||||
use std;
|
||||
import std::map::*;
|
||||
|
||||
class cat implements map<int, bool> {
|
||||
priv {
|
||||
// Yes, you can have negative meows
|
||||
let mut meows : int;
|
||||
fn meow() {
|
||||
self.meows += 1;
|
||||
#error("Meow %d", self.meows);
|
||||
if self.meows % 5 == 0 {
|
||||
self.how_hungry += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut how_hungry : int;
|
||||
let name : str;
|
||||
|
||||
new(in_x : int, 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 size() -> uint { self.meows as uint }
|
||||
fn insert(&&k: int, &&v: bool) -> bool {
|
||||
if v { self.meows += k; } else { self.meows -= k; };
|
||||
true
|
||||
}
|
||||
fn contains_key(&&k: int) -> bool { k <= self.meows }
|
||||
fn get(&&k:int) -> bool { k <= self.meows }
|
||||
fn find(&&k:int) -> option<bool> { some(self.get(k)) }
|
||||
fn remove(&&k:int) -> option<bool> { self.meows -= k; some(true) }
|
||||
fn each(f: fn(&&int, &&bool) -> bool) {
|
||||
let mut n = int::abs(self.meows);
|
||||
while n > 0 {
|
||||
if !f(n, true) { break; }
|
||||
n -= 1;
|
||||
}
|
||||
}
|
||||
fn each_key(&&f: fn(&&int) -> bool) {
|
||||
for self.each {|k, _v| if !f(k) { break; } cont;};
|
||||
}
|
||||
fn each_value(&&f: fn(&&bool) -> bool) {
|
||||
for self.each {|_k, v| if !f(v) { break; } cont;};
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let nyan : cat = cat(0, 2, "nyan");
|
||||
uint::range(1u, 5u) {|_i| nyan.speak(); }
|
||||
// cat returns true if uint input is greater than
|
||||
// the number of meows so far
|
||||
assert(nyan.get(1));
|
||||
assert(!nyan.get(10));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue