From 69322d941032202b7d5308dbe16e31b62c752cd2 Mon Sep 17 00:00:00 2001 From: Ben Blum Date: Fri, 16 Aug 2013 16:57:42 -0400 Subject: [PATCH] Allow traits to use builtin kinds as supertraits for #7083. --- src/librustc/metadata/decoder.rs | 20 +++++++- src/librustc/metadata/encoder.rs | 3 ++ src/librustc/middle/kind.rs | 74 +++++++++++++-------------- src/librustc/middle/lang_items.rs | 15 ++++-- src/librustc/middle/ty.rs | 20 ++++++-- src/librustc/middle/typeck/astconv.rs | 28 +--------- src/librustc/middle/typeck/collect.rs | 51 +++++++++++------- 7 files changed, 119 insertions(+), 92 deletions(-) diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 81a2e863bde00..9eb09806bc07d 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -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) } } @@ -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; diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 4a3704dc3aa4e..6a999a0c26628 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -1076,6 +1076,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); diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index 6d7d8112991ef..edea267d22686 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -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, @@ -77,9 +75,6 @@ impl Visitor 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, @@ -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); } _ => {} } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 8dd5ee2058552..47e2937ca8a8a 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -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; @@ -158,10 +159,16 @@ impl LanguageItems { } } - pub fn is_builtin_kind(&self, id: def_id) -> bool { - Some(id) == self.freeze_trait() || - Some(id) == self.send_trait() || - Some(id) == self.sized_trait() + pub fn to_builtin_kind(&self, id: def_id) -> Option { + 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 { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 56c3616d6710f..757f05c208b0e 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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, } @@ -3725,10 +3726,23 @@ pub fn impl_trait_ref(cx: ctxt, id: ast::def_id) -> Option<@TraitRef> { return ret; } -pub fn trait_ref_is_builtin_kind(tcx: ctxt, tr: &ast::trait_ref) -> bool { +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"); - let def_id = ast_util::def_id_of_def(*def); - tcx.lang_items.is_builtin_kind(def_id) + 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 { diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index c666e98c9c15f..db76d75a5a583 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -773,9 +773,8 @@ fn conv_builtin_bounds(tcx: ty::ctxt, ast_bounds: &Option { 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 } } @@ -807,26 +806,3 @@ fn conv_builtin_bounds(tcx: ty::ctxt, ast_bounds: &Option 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 - } -} diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 2f6728891ff2a..a22879654e810 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -398,28 +398,39 @@ pub fn ensure_supertraits(ccx: &CrateCtxt, sp: codemap::span, rp: Option, ast_trait_refs: &[ast::trait_ref], - generics: &ast::Generics) + generics: &ast::Generics) -> ty::BuiltinBounds { let tcx = ccx.tcx; - if tcx.supertraits.contains_key(&local_def(id)) { return; } + + // Called only the first time trait_def_of_item is called. + // Supertraits are ensured at the same time. + assert!(!tcx.supertraits.contains_key(&local_def(id))); let self_ty = ty::mk_self(ccx.tcx, local_def(id)); let mut ty_trait_refs: ~[@ty::TraitRef] = ~[]; + let mut bounds = ty::EmptyBuiltinBounds(); for ast_trait_ref in ast_trait_refs.iter() { + let trait_def_id = ty::trait_ref_to_def_id(ccx.tcx, ast_trait_ref); + // FIXME(#8559): Need to instantiate the trait_ref whether or not it's a + // builtin trait, so that the trait's node id appears in the tcx trait_ref + // map. This is only needed for metadata; see the similar fixme in encoder.rs. let trait_ref = instantiate_trait_ref(ccx, ast_trait_ref, rp, generics, self_ty); - - // FIXME(#5527) Could have same trait multiple times - if ty_trait_refs.iter().any(|other_trait| other_trait.def_id == trait_ref.def_id) { - // This means a trait inherited from the same supertrait more - // than once. - tcx.sess.span_err(sp, "Duplicate supertrait in trait declaration"); - break; - } else { - ty_trait_refs.push(trait_ref); + if !ty::try_add_builtin_trait(ccx.tcx, trait_def_id, &mut bounds) { + + // FIXME(#5527) Could have same trait multiple times + if ty_trait_refs.iter().any(|other_trait| other_trait.def_id == trait_ref.def_id) { + // This means a trait inherited from the same supertrait more + // than once. + tcx.sess.span_err(sp, "Duplicate supertrait in trait declaration"); + break; + } else { + ty_trait_refs.push(trait_ref); + } } } tcx.supertraits.insert(local_def(id), @ty_trait_refs); + bounds } /** @@ -870,8 +881,9 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::item) { parent_visibility); for t in opt_trait_ref.iter() { // Prevent the builtin kind traits from being manually implemented. - if ty::trait_ref_is_builtin_kind(ccx.tcx, t) { - ccx.tcx.sess.span_err(it.span, + let trait_def_id = ty::trait_ref_to_def_id(tcx, t); + if tcx.lang_items.to_builtin_kind(trait_def_id).is_some() { + tcx.sess.span_err(it.span, "cannot provide an explicit implementation \ for a builtin kind"); } @@ -879,11 +891,9 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::item) { check_methods_against_trait(ccx, generics, rp, selfty, t, cms); } } - ast::item_trait(ref generics, ref supertraits, ref trait_methods) => { - let trait_def = trait_def_of_item(ccx, it); - tcx.trait_defs.insert(local_def(it.id), trait_def); + ast::item_trait(ref generics, _, ref trait_methods) => { + let _trait_def = trait_def_of_item(ccx, it); ensure_trait_methods(ccx, it.id); - ensure_supertraits(ccx, it.id, it.span, rp, *supertraits, generics); let (_, provided_methods) = split_trait_methods(*trait_methods); @@ -1039,13 +1049,16 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::item) -> @ty::TraitDef { } let rp = tcx.region_paramd_items.find(&it.id).map_move(|x| *x); match it.node { - ast::item_trait(ref generics, _, _) => { + ast::item_trait(ref generics, ref supertraits, _) => { let self_ty = ty::mk_self(tcx, def_id); let (ty_generics, substs) = mk_item_substs(ccx, generics, rp, Some(self_ty)); + let bounds = ensure_supertraits(ccx, it.id, it.span, rp, + *supertraits, generics); let trait_ref = @ty::TraitRef {def_id: def_id, substs: substs}; let trait_def = @ty::TraitDef {generics: ty_generics, + bounds: bounds, trait_ref: trait_ref}; tcx.trait_defs.insert(def_id, trait_def); return trait_def; @@ -1225,7 +1238,7 @@ pub fn ty_generics(ccx: &CrateCtxt, TraitTyParamBound(ref b) => { let ty = ty::mk_param(ccx.tcx, param_ty.idx, param_ty.def_id); let trait_ref = instantiate_trait_ref(ccx, b, rp, generics, ty); - if !astconv::try_add_builtin_trait( + if !ty::try_add_builtin_trait( ccx.tcx, trait_ref.def_id, &mut param_bounds.builtin_bounds) {