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:
Tim Chevalier 2012-05-03 09:17:58 -07:00
parent 1226669172
commit 4f105e4025
6 changed files with 109 additions and 12 deletions

View file

@ -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)
}

View file

@ -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);

View file

@ -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;

View file

@ -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}

View file

@ -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 {

View 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));
}