From 4f105e4025729047f60d2adef01d31e905aec222 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Thu, 3 May 2012 09:17:58 -0700 Subject: [PATCH] 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 { ... 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 implements B {... should work.) --- src/rustc/middle/kind.rs | 10 +++ src/rustc/middle/resolve.rs | 28 +++++--- src/rustc/middle/ty.rs | 3 +- src/rustc/middle/typeck.rs | 5 +- src/rustc/util/ppaux.rs | 6 ++ .../class-impl-parameterized-iface.rs | 69 +++++++++++++++++++ 6 files changed, 109 insertions(+), 12 deletions(-) create mode 100644 src/test/run-pass/class-impl-parameterized-iface.rs diff --git a/src/rustc/middle/kind.rs b/src/rustc/middle/kind.rs index 56dd30cc02e5..f17a2836ee5e 100644 --- a/src/rustc/middle/kind.rs +++ b/src/rustc/middle/kind.rs @@ -242,6 +242,9 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt) { 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) { } } }; + 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) } diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs index 01d7d68f7cac..a25b84ea89b5 100644 --- a/src/rustc/middle/resolve.rs +++ b/src/rustc/middle/resolve.rs @@ -430,17 +430,17 @@ fn resolve_names(e: @env, c: @ast::crate) { fn walk_item(e: @env, i: @ast::item, sc: scopes, v: vt) { 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) { // 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) { /* 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); diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index a64c1a5a777d..dd2c3071b3b8 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -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; diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index c1ed3e5fa5c0..5d63d8c4e680 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -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} diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs index 80af3229bc40..d5758605d63f 100644 --- a/src/rustc/util/ppaux.rs +++ b/src/rustc/util/ppaux.rs @@ -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 { diff --git a/src/test/run-pass/class-impl-parameterized-iface.rs b/src/test/run-pass/class-impl-parameterized-iface.rs new file mode 100644 index 000000000000..1acce7da7d6d --- /dev/null +++ b/src/test/run-pass/class-impl-parameterized-iface.rs @@ -0,0 +1,69 @@ +// xfail-fast +use std; +import std::map::*; + +class cat implements map { + 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 { some(self.get(k)) } + fn remove(&&k:int) -> option { 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)); +}