Skip to content

Commit

Permalink
auto merge of #8562 : bblum/rust/superkinds, r=nikomatsakis
Browse files Browse the repository at this point in the history
For #7083.

The metadata issue with the old version is now fixed. Ready for review.

This is also not the full solution to #7083, because this is not supported yet:
```
trait Foo : Send { }

impl <T: Send> Foo for T { }

fn foo<T: Foo>(val: T, chan: std::comm::Chan<T>) {
    chan.send(val);
}
```

cc @nikomatsakis
  • Loading branch information
bors committed Aug 22, 2013
2 parents fca7519 + b795fab commit 3ddfb72
Show file tree
Hide file tree
Showing 22 changed files with 519 additions and 96 deletions.
20 changes: 19 additions & 1 deletion src/librustc/metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,21 @@ pub fn get_trait_def(cdata: cmd,
let tp_defs = item_ty_param_defs(item_doc, tcx, cdata,
tag_items_data_item_ty_param_bounds);
let rp = item_ty_region_param(item_doc);
let mut bounds = ty::EmptyBuiltinBounds();
// Collect the builtin bounds from the encoded supertraits.
// FIXME(#8559): They should be encoded directly.
do reader::tagged_docs(item_doc, tag_item_super_trait_ref) |trait_doc| {
// NB. Bypasses real supertraits. See get_supertraits() if you wanted them.
let trait_ref = doc_trait_ref(trait_doc, tcx, cdata);
do tcx.lang_items.to_builtin_kind(trait_ref.def_id).map_move |bound| {
bounds.add(bound);
};
true
};
ty::TraitDef {
generics: ty::Generics {type_param_defs: tp_defs,
region_param: rp},
bounds: bounds,
trait_ref: @item_trait_ref(item_doc, tcx, cdata)
}
}
Expand Down Expand Up @@ -929,7 +941,13 @@ pub fn get_supertraits(cdata: cmd, id: ast::NodeId, tcx: ty::ctxt)
let mut results = ~[];
let item_doc = lookup_item(id, cdata.data);
do reader::tagged_docs(item_doc, tag_item_super_trait_ref) |trait_doc| {
results.push(@doc_trait_ref(trait_doc, tcx, cdata));
// NB. Only reads the ones that *aren't* builtin-bounds. See also
// get_trait_def() for collecting the builtin bounds.
// FIXME(#8559): The builtin bounds shouldn't be encoded in the first place.
let trait_ref = doc_trait_ref(trait_doc, tcx, cdata);
if tcx.lang_items.to_builtin_kind(trait_ref.def_id).is_none() {
results.push(@trait_ref);
}
true
};
return results;
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,9 @@ fn encode_info_for_item(ecx: &EncodeContext,
ebml_w.end_tag();
}
encode_path(ecx, ebml_w, path, ast_map::path_name(item.ident));
// FIXME(#8559): This should use the tcx's supertrait cache instead of
// reading the AST's list, because the former has already filtered out
// the builtin-kinds-as-supertraits. See corresponding fixme in decoder.
for ast_trait_ref in super_traits.iter() {
let trait_ref = ty::node_id_to_trait_ref(ecx.tcx, ast_trait_ref.ref_id);
encode_trait_ref(ebml_w, ecx, trait_ref, tag_item_super_trait_ref);
Expand Down
74 changes: 35 additions & 39 deletions src/librustc/middle/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ use syntax::visit::Visitor;
// primitives in the stdlib are explicitly annotated to only take sendable
// types.

pub static try_adding: &'static str = "Try adding a move";

#[deriving(Clone)]
pub struct Context {
tcx: ty::ctxt,
Expand All @@ -77,9 +75,6 @@ impl Visitor<Context> for KindAnalysisVisitor {
fn visit_item(&mut self, i:@item, e:Context) {
check_item(self, i, e);
}
fn visit_block(&mut self, b:&Block, e:Context) {
check_block(self, b, e);
}
}

pub fn check_crate(tcx: ty::ctxt,
Expand Down Expand Up @@ -125,46 +120,47 @@ fn check_struct_safe_for_destructor(cx: Context,
}
}

fn check_block(visitor: &mut KindAnalysisVisitor,
block: &Block,
cx: Context) {
visit::walk_block(visitor, block, cx);
fn check_impl_of_trait(cx: Context, it: @item, trait_ref: &trait_ref, self_type: &Ty) {
let ast_trait_def = cx.tcx.def_map.find(&trait_ref.ref_id)
.expect("trait ref not in def map!");
let trait_def_id = ast_util::def_id_of_def(*ast_trait_def);
let trait_def = cx.tcx.trait_defs.find(&trait_def_id)
.expect("trait def not in trait-defs map!");

// If this trait has builtin-kind supertraits, meet them.
let self_ty: ty::t = ty::node_id_to_type(cx.tcx, it.id);
error!("checking impl with self type %?", ty::get(self_ty).sty);
do check_builtin_bounds(cx, self_ty, trait_def.bounds) |missing| {
cx.tcx.sess.span_err(self_type.span,
fmt!("the type `%s', which does not fulfill `%s`, cannot implement this \
trait", ty_to_str(cx.tcx, self_ty), missing.user_string(cx.tcx)));
cx.tcx.sess.span_note(self_type.span,
fmt!("types implementing this trait must fulfill `%s`",
trait_def.bounds.user_string(cx.tcx)));
}

// If this is a destructor, check kinds.
if cx.tcx.lang_items.drop_trait() == Some(trait_def_id) {
match self_type.node {
ty_path(_, ref bounds, path_node_id) => {
assert!(bounds.is_none());
let struct_def = cx.tcx.def_map.get_copy(&path_node_id);
let struct_did = ast_util::def_id_of_def(struct_def);
check_struct_safe_for_destructor(cx, self_type.span, struct_did);
}
_ => {
cx.tcx.sess.span_bug(self_type.span,
"the self type for the Drop trait impl is not a path");
}
}
}
}

fn check_item(visitor: &mut KindAnalysisVisitor, item: @item, cx: Context) {
// If this is a destructor, check kinds.
if !attr::contains_name(item.attrs, "unsafe_destructor") {
match item.node {
item_impl(_, Some(ref trait_ref), ref self_type, _) => {
match cx.tcx.def_map.find(&trait_ref.ref_id) {
None => cx.tcx.sess.bug("trait ref not in def map!"),
Some(&trait_def) => {
let trait_def_id = ast_util::def_id_of_def(trait_def);
if cx.tcx.lang_items.drop_trait() == Some(trait_def_id) {
// Yes, it's a destructor.
match self_type.node {
ty_path(_, ref bounds, path_node_id) => {
assert!(bounds.is_none());
let struct_def = cx.tcx.def_map.get_copy(
&path_node_id);
let struct_did =
ast_util::def_id_of_def(struct_def);
check_struct_safe_for_destructor(
cx,
self_type.span,
struct_did);
}
_ => {
cx.tcx.sess.span_bug(self_type.span,
"the self type for \
the Drop trait \
impl is not a \
path");
}
}
}
}
}
check_impl_of_trait(cx, item, trait_ref, self_type);
}
_ => {}
}
Expand Down
13 changes: 13 additions & 0 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use driver::session::Session;
use metadata::csearch::each_lang_item;
use metadata::cstore::iter_crate_data;
use middle::ty::{BuiltinBound, BoundFreeze, BoundSend, BoundSized};
use syntax::ast::{Crate, def_id, MetaItem};
use syntax::ast_util::local_def;
use syntax::attr::AttrMetaMethods;
Expand Down Expand Up @@ -158,6 +159,18 @@ impl LanguageItems {
}
}

pub fn to_builtin_kind(&self, id: def_id) -> Option<BuiltinBound> {
if Some(id) == self.freeze_trait() {
Some(BoundFreeze)
} else if Some(id) == self.send_trait() {
Some(BoundSend)
} else if Some(id) == self.sized_trait() {
Some(BoundSized)
} else {
None
}
}

pub fn freeze_trait(&self) -> Option<def_id> {
self.items[FreezeTraitLangItem as uint]
}
Expand Down
63 changes: 50 additions & 13 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,7 @@ pub struct ty_param_bounds_and_ty {
/// As `ty_param_bounds_and_ty` but for a trait ref.
pub struct TraitDef {
generics: Generics,
bounds: BuiltinBounds,
trait_ref: @ty::TraitRef,
}

Expand Down Expand Up @@ -2160,17 +2161,19 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
// def-id.
assert_eq!(p.def_id.crate, ast::LOCAL_CRATE);

type_param_def_to_contents(
cx, cx.ty_param_defs.get(&p.def_id.node))
let tp_def = cx.ty_param_defs.get(&p.def_id.node);
kind_bounds_to_contents(cx, &tp_def.bounds.builtin_bounds,
tp_def.bounds.trait_bounds)
}

ty_self(_) => {
// Currently, self is not bounded, so we must assume the
// worst. But in the future we should examine the super
// traits.
//
ty_self(def_id) => {
// FIXME(#4678)---self should just be a ty param
TC_ALL

// Self may be bounded if the associated trait has builtin kinds
// for supertraits. If so we can use those bounds.
let trait_def = lookup_trait_def(cx, def_id);
let traits = [trait_def.trait_ref];
kind_bounds_to_contents(cx, &trait_def.bounds, traits)
}

ty_infer(_) => {
Expand Down Expand Up @@ -2314,14 +2317,12 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
st + mt + bt
}

fn type_param_def_to_contents(cx: ctxt,
type_param_def: &TypeParameterDef) -> TypeContents
{
debug!("type_param_def_to_contents(%s)", type_param_def.repr(cx));
fn kind_bounds_to_contents(cx: ctxt, bounds: &BuiltinBounds, traits: &[@TraitRef])
-> TypeContents {
let _i = indenter();

let mut tc = TC_ALL;
for bound in type_param_def.bounds.builtin_bounds.iter() {
do each_inherited_builtin_bound(cx, bounds, traits) |bound| {
debug!("tc = %s, bound = %?", tc.to_str(), bound);
tc = tc - match bound {
BoundStatic => TypeContents::nonstatic(cx),
Expand All @@ -2334,6 +2335,23 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {

debug!("result = %s", tc.to_str());
return tc;

// Iterates over all builtin bounds on the type parameter def, including
// those inherited from traits with builtin-kind-supertraits.
fn each_inherited_builtin_bound(cx: ctxt, bounds: &BuiltinBounds,
traits: &[@TraitRef], f: &fn(BuiltinBound)) {
for bound in bounds.iter() {
f(bound);
}

do each_bound_trait_and_supertraits(cx, traits) |trait_ref| {
let trait_def = lookup_trait_def(cx, trait_ref.def_id);
for bound in trait_def.bounds.iter() {
f(bound);
}
true
};
}
}
}

Expand Down Expand Up @@ -3727,6 +3745,25 @@ pub fn impl_trait_ref(cx: ctxt, id: ast::def_id) -> Option<@TraitRef> {
return ret;
}

pub fn trait_ref_to_def_id(tcx: ctxt, tr: &ast::trait_ref) -> ast::def_id {
let def = tcx.def_map.find(&tr.ref_id).expect("no def-map entry for trait");
ast_util::def_id_of_def(*def)
}

pub fn try_add_builtin_trait(tcx: ctxt,
trait_def_id: ast::def_id,
builtin_bounds: &mut BuiltinBounds) -> bool {
//! Checks whether `trait_ref` refers to one of the builtin
//! traits, like `Send`, and adds the corresponding
//! bound to the set `builtin_bounds` if so. Returns true if `trait_ref`
//! is a builtin trait.
match tcx.lang_items.to_builtin_kind(trait_def_id) {
Some(bound) => { builtin_bounds.add(bound); true }
None => false
}
}

pub fn ty_to_def_id(ty: t) -> Option<ast::def_id> {
match get(ty).sty {
ty_trait(id, _, _, _, _) | ty_struct(id, _) | ty_enum(id, _) => Some(id),
Expand Down
28 changes: 2 additions & 26 deletions src/librustc/middle/typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -773,9 +773,8 @@ fn conv_builtin_bounds(tcx: ty::ctxt, ast_bounds: &Option<OptVec<ast::TyParamBou
ast::TraitTyParamBound(ref b) => {
match lookup_def_tcx(tcx, b.path.span, b.ref_id) {
ast::def_trait(trait_did) => {
if try_add_builtin_trait(tcx,
trait_did,
&mut builtin_bounds) {
if ty::try_add_builtin_trait(tcx, trait_did,
&mut builtin_bounds) {
loop; // success
}
}
Expand Down Expand Up @@ -807,26 +806,3 @@ fn conv_builtin_bounds(tcx: ty::ctxt, ast_bounds: &Option<OptVec<ast::TyParamBou
(&None, ty::RegionTraitStore(*)) => ty::EmptyBuiltinBounds(),
}
}

pub fn try_add_builtin_trait(tcx: ty::ctxt,
trait_def_id: ast::def_id,
builtin_bounds: &mut ty::BuiltinBounds) -> bool {
//! Checks whether `trait_ref` refers to one of the builtin
//! traits, like `Send`, and adds the corresponding
//! bound to the set `builtin_bounds` if so. Returns true if `trait_ref`
//! is a builtin trait.
let li = &tcx.lang_items;
if Some(trait_def_id) == li.send_trait() {
builtin_bounds.add(ty::BoundSend);
true
} else if Some(trait_def_id) == li.freeze_trait() {
builtin_bounds.add(ty::BoundFreeze);
true
} else if Some(trait_def_id) == li.sized_trait() {
builtin_bounds.add(ty::BoundSized);
true
} else {
false
}
}
Loading

0 comments on commit 3ddfb72

Please sign in to comment.