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).

As per rust-lang#3979
  • Loading branch information
catamorphism committed Jan 21, 2013
1 parent e5bf6d1 commit d78265a
Show file tree
Hide file tree
Showing 15 changed files with 358 additions and 77 deletions.
9 changes: 6 additions & 3 deletions src/librustc/middle/astencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,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 @@ -573,10 +573,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 @@ -190,6 +190,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

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Jan 25, 2013

Nit: I find this style of densely packing in comments and code really hard to read. This is a personal thing so do as you feel, but I would prefer this code to be formatted as so:

// <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 = ...;
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 @@ -233,8 +256,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 @@ -240,6 +240,7 @@ export AutoRef;
export AutoRefKind, AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn;
export iter_bound_traits_and_supertraits;
export count_traits_and_supertraits;
export get_impl_id;

// Data types

Expand Down Expand Up @@ -471,6 +472,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 @@ -1023,7 +1027,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 @@ -3090,7 +3096,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 @@ -4329,9 +4336,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 @@ -4343,34 +4355,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 @@ -4385,6 +4408,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
92 changes: 66 additions & 26 deletions src/librustc/middle/typeck/check/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ use middle::typeck::check;
use middle::typeck::coherence::get_base_type_def_id;
use middle::typeck::infer;
use middle::typeck::{method_map_entry, method_origin, method_param};
use middle::typeck::{method_self, method_static, method_trait};
use middle::typeck::{method_self, method_static, method_trait, method_super};
use util::common::indenter;
use util::ppaux::expr_repr;

Expand Down Expand Up @@ -529,30 +529,66 @@ impl LookupContext {
did: def_id,
substs: &ty::substs) {
let tcx = self.tcx();
let methods = ty::trait_methods(tcx, did); // XXX: Inherited methods.
let index;
// First, try self methods
let mut method = None;
let methods = ty::trait_methods(tcx, did);
let mut index = None;
let mut trait_did = None;

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Jan 25, 2013

NIT: Can't you use a single tuple or something? The match below is somewhat awkward, and these variables are never set individually.

match vec::position(*methods, |m| m.ident == self.m_name) {
Some(i) => index = i,
None => return
Some(i) => {
index = Some(i);
trait_did = Some(did);
method = Some((methods[i].self_ty, methods[i].tps.len()));
}
None => ()
}
let method = &methods[index];

let rcvr_substs = { self_ty: Some(self_ty), ../*bad*/copy *substs };
let (rcvr_ty, rcvr_substs) =
self.create_rcvr_ty_and_substs_for_method(
method.self_ty,
self_ty,
move rcvr_substs,
TransformTypeNormally);
// No method found yet? Check each supertrait
if method.is_none() {
for ty::trait_supertraits(tcx, did).each() |trait_ref| {

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Jan 25, 2013

It seems like this should be taking type parameter substitutions into account.

let supertrait_methods =
ty::trait_methods(tcx, trait_ref.def_id);
match vec::position(*supertrait_methods,
|m| m.ident == self.m_name) {
Some(i) => {
index = Some(i);
trait_did = Some(trait_ref.def_id);
method = Some((supertrait_methods[i].self_ty,
supertrait_methods[i].tps.len()));
break;
}
None => ()
}
}
}
match (method, index, trait_did) {
(Some((method_self_ty, method_num_tps)),
Some(index), Some(trait_did)) => {

self.inherent_candidates.push(Candidate {
rcvr_ty: rcvr_ty,
rcvr_substs: move rcvr_substs,
explicit_self: method.self_ty,
num_method_tps: method.tps.len(),
self_mode: get_mode_from_self_type(method.self_ty),
origin: method_self(did, index)
});
// We've found a method -- return it
let rcvr_substs = { self_ty: Some(self_ty), ..copy *substs };

This comment has been minimized.

Copy link
@nikomatsakis

nikomatsakis Jan 25, 2013

Later on, don't we expect that the combined type parameters of the method are receiver + method? It seems like here you are always using the tps from the self_ty, but in fact you want the appropriate tps for the supertype.

let (rcvr_ty, rcvr_substs) =
self.create_rcvr_ty_and_substs_for_method(
method_self_ty,
self_ty,
move rcvr_substs,
TransformTypeNormally);
let origin = if trait_did == did {
method_self(trait_did, index)
}
else {
method_super(trait_did, index)
};
self.inherent_candidates.push(Candidate {
rcvr_ty: rcvr_ty,
rcvr_substs: move rcvr_substs,
explicit_self: method_self_ty,
num_method_tps: method_num_tps,
self_mode: get_mode_from_self_type(method_self_ty),
origin: origin
});
}
_ => return
}
}

fn push_inherent_impl_candidates_for_type(did: def_id) {
Expand Down Expand Up @@ -1034,7 +1070,8 @@ impl LookupContext {
* vtable and hence cannot be monomorphized. */

match candidate.origin {
method_static(*) | method_param(*) | method_self(*) => {
method_static(*) | method_param(*) |
method_self(*) | method_super(*) => {
return; // not a call to a trait instance
}
method_trait(*) => {}
Expand All @@ -1058,7 +1095,8 @@ impl LookupContext {
// No code can call the finalize method explicitly.
let bad;
match candidate.origin {
method_static(method_id) | method_self(method_id, _) => {
method_static(method_id) | method_self(method_id, _)
| method_super(method_id, _) => {
bad = self.tcx().destructors.contains_key(method_id);
}
method_param(method_param { trait_id: trait_id, _ }) |
Expand Down Expand Up @@ -1088,7 +1126,8 @@ impl LookupContext {
method_param(ref mp) => {
type_of_trait_method(self.tcx(), mp.trait_id, mp.method_num)
}
method_trait(did, idx, _) | method_self(did, idx) => {
method_trait(did, idx, _) | method_self(did, idx) |
method_super(did, idx) => {
type_of_trait_method(self.tcx(), did, idx)
}
};
Expand All @@ -1109,7 +1148,8 @@ impl LookupContext {
method_param(ref mp) => {
self.report_param_candidate(idx, (*mp).trait_id)
}
method_trait(trait_did, _, _) | method_self(trait_did, _) => {
method_trait(trait_did, _, _) | method_self(trait_did, _)
| method_super(trait_did, _) => {
self.report_param_candidate(idx, trait_did)
}
}
Expand Down
Loading

0 comments on commit d78265a

Please sign in to comment.