Skip to content

Commit

Permalink
Handle supertrait calls in default methods
Browse files Browse the repository at this point in the history
Add a new method_super origin for supertrait methods. Also make
coherence create a table that maps pairs of trait IDs and self types
to impl IDs, so that it's possible to check a supertrait method
knowing only its index in its trait's methods (without knowing all
supertraits for a given trait).

r=nmatsakis and graydon -- with hope, we'll revamp all of this code as
per #4678, but for now this fixes the bug.

Closes #3979
  • Loading branch information
catamorphism committed Jan 30, 2013
1 parent b927e48 commit a30ea01
Show file tree
Hide file tree
Showing 14 changed files with 354 additions and 74 deletions.
9 changes: 6 additions & 3 deletions src/librustc/middle/astencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ impl method_origin: tr {
fn tr(xcx: extended_decode_ctxt) -> method_origin {
match self {
typeck::method_static(did) => {
typeck::method_static(did.tr(xcx))
typeck::method_static(did.tr(xcx))
}
typeck::method_param(ref mp) => {
typeck::method_param(
Expand All @@ -575,10 +575,13 @@ impl method_origin: tr {
)
}
typeck::method_trait(did, m, vstore) => {
typeck::method_trait(did.tr(xcx), m, vstore)
typeck::method_trait(did.tr(xcx), m, vstore)
}
typeck::method_self(did, m) => {
typeck::method_self(did.tr(xcx), m)
typeck::method_self(did.tr(xcx), m)
}
typeck::method_super(trait_did, m) => {
typeck::method_super(trait_did.tr(xcx), m)
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/librustc/middle/privacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ use core::prelude::*;

use middle::ty::{ty_struct, ty_enum};
use middle::ty;
use middle::typeck::{method_map, method_origin, method_param, method_self};
use middle::typeck::{method_map, method_origin, method_param, method_self,
method_super};
use middle::typeck::{method_static, method_trait};

use core::dvec::DVec;
Expand Down Expand Up @@ -138,7 +139,8 @@ fn check_crate(tcx: ty::ctxt, method_map: &method_map, crate: @ast::crate) {
_
}) |
method_trait(trait_id, method_num, _) |
method_self(trait_id, method_num) => {
method_self(trait_id, method_num) |
method_super(trait_id, method_num) => {
if trait_id.crate == local_crate {
match tcx.items.find(trait_id.node) {
Some(node_item(item, _)) => {
Expand Down
27 changes: 25 additions & 2 deletions src/librustc/middle/trans/meth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,29 @@ fn trans_method_callee(bcx: block, callee_id: ast::node_id,
method_name);
origin = typeck::method_static(method_id);
}
typeck::method_super(trait_id, method_index) => {
// <self_ty> is the self type for this method call
let self_ty = node_id_type(bcx, self.id);
let tcx = bcx.tcx();
// <impl_id> is the ID of the implementation of
// trait <trait_id> for type <self_ty>
let impl_id = ty::get_impl_id(tcx, trait_id, self_ty);
// Get the supertrait's methods
let supertrait_methods = ty::trait_methods(tcx, trait_id);
// Make sure to fail with a readable error message if
// there's some internal error here
if !(method_index < supertrait_methods.len()) {
tcx.sess.bug(~"trans_method_callee: supertrait method \
index is out of bounds");
}
// Get the method name using the method index in the origin
let method_name = supertrait_methods[method_index].ident;
// Now that we know the impl ID, we can look up the method
// ID from its name
origin = typeck::method_static(method_with_name(bcx.ccx(),
impl_id,
method_name));
}
typeck::method_static(*) | typeck::method_param(*) |
typeck::method_trait(*) => {}
}
Expand Down Expand Up @@ -236,8 +259,8 @@ fn trans_method_callee(bcx: block, callee_id: ast::node_id,
vstore,
mentry.explicit_self)
}
typeck::method_self(*) => {
fail ~"method_self should have been handled above"
typeck::method_self(*) | typeck::method_super(*) => {
fail ~"method_self or method_super should have been handled above"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/trans/monomorphize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ fn make_mono_id(ccx: @crate_ctxt, item: ast::def_id, substs: ~[ty::t],
let precise_param_ids = match vtables {
Some(vts) => {
let bounds = ty::lookup_item_type(ccx.tcx, item).bounds;
let mut i = 0u;
let mut i = 0;
vec::map2(*bounds, substs, |bounds, subst| {
let mut v = ~[];
for bounds.each |bound| {
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/middle/trans/type_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@ fn mark_for_method_call(cx: ctx, e_id: node_id, callee_id: node_id) {
}) => {
cx.uses[param] |= use_tydesc;
}
typeck::method_trait(*) | typeck::method_self(*) => (),
typeck::method_trait(*) | typeck::method_self(*)
| typeck::method_super(*) => (),
}
}
}
Expand Down
95 changes: 67 additions & 28 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ export iter_bound_traits_and_supertraits;
export count_traits_and_supertraits;
export IntVarValue, IntType, UintType;
export creader_cache_key;
export get_impl_id;

// Data types

Expand Down Expand Up @@ -486,6 +487,9 @@ type ctxt =

// Records the value mode (read, copy, or move) for every value.
value_modes: HashMap<ast::node_id, ValueMode>,

// Maps a trait onto a mapping from self-ty to impl
trait_impls: HashMap<ast::def_id, HashMap<t, @Impl>>
};

enum tbox_flag {
Expand Down Expand Up @@ -1051,7 +1055,9 @@ fn mk_ctxt(s: session::Session,
supertraits: HashMap(),
destructor_for_type: HashMap(),
destructors: HashMap(),
value_modes: HashMap()}
value_modes: HashMap(),
trait_impls: HashMap()
}
}


Expand Down Expand Up @@ -3130,7 +3136,8 @@ fn method_call_bounds(tcx: ctxt, method_map: typeck::method_map,
trait_id: trt_id,
method_num: n_mth, _}) |
typeck::method_trait(trt_id, n_mth, _) |
typeck::method_self(trt_id, n_mth) => {
typeck::method_self(trt_id, n_mth) |
typeck::method_super(trt_id, n_mth) => {
// ...trait methods bounds, in contrast, include only the
// method bounds, so we must preprend the tps from the
// trait itself. This ought to be harmonized.
Expand Down Expand Up @@ -4372,9 +4379,14 @@ pure fn determine_inherited_purity(parent_purity: ast::purity,

// Iterate over a type parameter's bounded traits and any supertraits
// of those traits, ignoring kinds.
// Here, the supertraits are the transitive closure of the supertrait
// relation on the supertraits from each bounded trait's constraint
// list.
fn iter_bound_traits_and_supertraits(tcx: ctxt,
bounds: param_bounds,
f: &fn(t) -> bool) {
let mut fin = false;

for bounds.each |bound| {

let bound_trait_ty = match *bound {
Expand All @@ -4386,34 +4398,45 @@ fn iter_bound_traits_and_supertraits(tcx: ctxt,
}
};

let mut worklist = ~[];

let init_trait_ty = bound_trait_ty;

worklist.push(init_trait_ty);

let mut supertrait_map = HashMap();
let mut seen_def_ids = ~[];
let mut i = 0;
while i < worklist.len() {
let init_trait_ty = worklist[i];
i += 1;

let init_trait_id = match ty_to_def_id(init_trait_ty) {
Some(id) => id,
None => tcx.sess.bug(
~"trait type should have def_id")
};

// Add supertraits to worklist
let supertraits = trait_supertraits(tcx,
init_trait_id);
for supertraits.each |supertrait| {
worklist.push(supertrait.tpt.ty);
}

if !f(init_trait_ty) {
return;
let trait_ty_id = ty_to_def_id(bound_trait_ty).expect(
~"iter_trait_ty_supertraits got a non-trait type");
let mut trait_ty = bound_trait_ty;

debug!("iter_bound_traits_and_supertraits: trait_ty = %s",
ty_to_str(tcx, trait_ty));

// Add the given trait ty to the hash map
supertrait_map.insert(trait_ty_id, trait_ty);
seen_def_ids.push(trait_ty_id);

if f(trait_ty) {
// Add all the supertraits to the hash map,
// executing <f> on each of them
while i < supertrait_map.size() && !fin {
let init_trait_id = seen_def_ids[i];
i += 1;
// Add supertraits to supertrait_map
let supertraits = trait_supertraits(tcx, init_trait_id);
for supertraits.each |supertrait| {
let super_t = supertrait.tpt.ty;
let d_id = ty_to_def_id(super_t).expect("supertrait \
should be a trait ty");
if !supertrait_map.contains_key(d_id) {
supertrait_map.insert(d_id, super_t);
trait_ty = super_t;
seen_def_ids.push(d_id);
}
debug!("A super_t = %s", ty_to_str(tcx, trait_ty));
if !f(trait_ty) {
fin = true;
}
}
}
}
};
fin = false;
}
}

Expand All @@ -4428,6 +4451,22 @@ fn count_traits_and_supertraits(tcx: ctxt,
return total;
}

// Given a trait and a type, returns the impl of that type
fn get_impl_id(tcx: ctxt, trait_id: def_id, self_ty: t) -> def_id {
match tcx.trait_impls.find(trait_id) {
Some(ty_to_impl) => match ty_to_impl.find(self_ty) {
Some(the_impl) => the_impl.did,
None => // try autoderef!
match deref(tcx, self_ty, false) {
Some(some_ty) => get_impl_id(tcx, trait_id, some_ty.ty),
None => tcx.sess.bug(~"get_impl_id: no impl of trait for \
this type")
}
},
None => tcx.sess.bug(~"get_impl_id: trait isn't in trait_impls")
}
}

impl mt : cmp::Eq {
pure fn eq(&self, other: &mt) -> bool {
(*self).ty == (*other).ty && (*self).mutbl == (*other).mutbl
Expand Down
Loading

0 comments on commit a30ea01

Please sign in to comment.