Skip to content

Commit

Permalink
In resolve, visit the path in an iface ref
Browse files Browse the repository at this point in the history
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.)
  • Loading branch information
catamorphism committed May 7, 2012
1 parent 1226669 commit 4f105e4
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 12 deletions.
10 changes: 10 additions & 0 deletions src/rustc/middle/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
}
Expand Down
28 changes: 19 additions & 9 deletions src/rustc/middle/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
_ {}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 1 addition & 2 deletions src/rustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 4 additions & 1 deletion src/rustc/middle/typeck.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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}
Expand Down
6 changes: 6 additions & 0 deletions src/rustc/util/ppaux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
69 changes: 69 additions & 0 deletions src/test/run-pass/class-impl-parameterized-iface.rs
Original file line number Diff line number Diff line change
@@ -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));
}

0 comments on commit 4f105e4

Please sign in to comment.