From ae10e478eb61e75ebf3a7bf672b34b582555fd8e Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 26 May 2015 17:12:39 +0300 Subject: [PATCH] Implement defaults for associated types --- src/librustc/metadata/decoder.rs | 35 ++++++---- src/librustc/metadata/encoder.rs | 23 ++++--- src/librustc/middle/traits/project.rs | 56 +++++++-------- src/librustc/middle/ty.rs | 5 +- src/librustc/util/ppaux.rs | 2 +- src/librustc_typeck/check/mod.rs | 17 ++--- src/librustc_typeck/collect.rs | 48 ++++++------- src/librustdoc/clean/inline.rs | 8 ++- src/librustdoc/clean/mod.rs | 69 +++++++++---------- src/test/run-pass/default-associated-types.rs | 30 ++++++++ 10 files changed, 170 insertions(+), 123 deletions(-) create mode 100644 src/test/run-pass/default-associated-types.rs diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 42dcc9661ca..5eefb99b058 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -252,6 +252,13 @@ fn doc_type<'tcx>(doc: rbml::Doc, tcx: &ty::ctxt<'tcx>, cdata: Cmd) -> Ty<'tcx> |_, did| translate_def_id(cdata, did)) } +fn maybe_doc_type<'tcx>(doc: rbml::Doc, tcx: &ty::ctxt<'tcx>, cdata: Cmd) -> Option> { + reader::maybe_get_doc(doc, tag_items_data_item_type).map(|tp| { + parse_ty_data(tp.data, cdata.cnum, tp.start, tcx, + |_, did| translate_def_id(cdata, did)) + }) +} + fn doc_method_fty<'tcx>(doc: rbml::Doc, tcx: &ty::ctxt<'tcx>, cdata: Cmd) -> ty::BareFnTy<'tcx> { let tp = reader::get_doc(doc, tag_item_method_fty); @@ -875,24 +882,24 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc, id: ast::NodeId, tcx: &ty::ctxt<'tcx>) -> ty::ImplOrTraitItem<'tcx> { - let method_doc = lookup_item(id, cdata.data()); + let item_doc = lookup_item(id, cdata.data()); - let def_id = item_def_id(method_doc, cdata); + let def_id = item_def_id(item_doc, cdata); - let container_id = item_require_parent_item(cdata, method_doc); + let container_id = item_require_parent_item(cdata, item_doc); let container_doc = lookup_item(container_id.node, cdata.data()); let container = match item_family(container_doc) { Trait => TraitContainer(container_id), _ => ImplContainer(container_id), }; - let name = item_name(&*intr, method_doc); - let vis = item_visibility(method_doc); + let name = item_name(&*intr, item_doc); + let vis = item_visibility(item_doc); - match item_sort(method_doc) { + match item_sort(item_doc) { Some('C') => { - let ty = doc_type(method_doc, tcx, cdata); - let default = get_provided_source(method_doc, cdata); + let ty = doc_type(item_doc, tcx, cdata); + let default = get_provided_source(item_doc, cdata); ty::ConstTraitItem(Rc::new(ty::AssociatedConst { name: name, ty: ty, @@ -903,11 +910,11 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc, })) } Some('r') | Some('p') => { - let generics = doc_generics(method_doc, tcx, cdata, tag_method_ty_generics); - let predicates = doc_predicates(method_doc, tcx, cdata, tag_method_ty_generics); - let fty = doc_method_fty(method_doc, tcx, cdata); - let explicit_self = get_explicit_self(method_doc); - let provided_source = get_provided_source(method_doc, cdata); + let generics = doc_generics(item_doc, tcx, cdata, tag_method_ty_generics); + let predicates = doc_predicates(item_doc, tcx, cdata, tag_method_ty_generics); + let fty = doc_method_fty(item_doc, tcx, cdata); + let explicit_self = get_explicit_self(item_doc); + let provided_source = get_provided_source(item_doc, cdata); ty::MethodTraitItem(Rc::new(ty::Method::new(name, generics, @@ -920,8 +927,10 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc, provided_source))) } Some('t') => { + let ty = maybe_doc_type(item_doc, tcx, cdata); ty::TypeTraitItem(Rc::new(ty::AssociatedType { name: name, + ty: ty, vis: vis, def_id: def_id, container: container, diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 0908ffd249f..8eefb4d5011 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -894,12 +894,12 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, rbml_w.end_tag(); } -fn encode_info_for_associated_type(ecx: &EncodeContext, - rbml_w: &mut Encoder, - associated_type: &ty::AssociatedType, - impl_path: PathElems, - parent_id: NodeId, - impl_item_opt: Option<&ast::ImplItem>) { +fn encode_info_for_associated_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, + rbml_w: &mut Encoder, + associated_type: &ty::AssociatedType<'tcx>, + impl_path: PathElems, + parent_id: NodeId, + impl_item_opt: Option<&ast::ImplItem>) { debug!("encode_info_for_associated_type({:?},{:?})", associated_type.def_id, token::get_name(associated_type.name)); @@ -913,8 +913,6 @@ fn encode_info_for_associated_type(ecx: &EncodeContext, encode_parent_item(rbml_w, local_def(parent_id)); encode_item_sort(rbml_w, 't'); - encode_bounds_and_type_for_item(rbml_w, ecx, associated_type.def_id.local_id()); - let stab = stability::lookup(ecx.tcx, associated_type.def_id); encode_stability(rbml_w, stab); @@ -923,7 +921,14 @@ fn encode_info_for_associated_type(ecx: &EncodeContext, if let Some(ii) = impl_item_opt { encode_attributes(rbml_w, &ii.attrs); - encode_type(ecx, rbml_w, ty::node_id_to_type(ecx.tcx, ii.id)); + } else { + encode_predicates(rbml_w, ecx, + &ty::lookup_predicates(ecx.tcx, associated_type.def_id), + tag_item_generics); + } + + if let Some(ty) = associated_type.ty { + encode_type(ecx, rbml_w, ty); } rbml_w.end_tag(); diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index f6bde80e298..700aaad8b72 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -857,37 +857,39 @@ fn confirm_impl_candidate<'cx,'tcx>( -> (Ty<'tcx>, Vec>) { // there don't seem to be nicer accessors to these: - let impl_items_map = selcx.tcx().impl_items.borrow(); let impl_or_trait_items_map = selcx.tcx().impl_or_trait_items.borrow(); - let impl_items = impl_items_map.get(&impl_vtable.impl_def_id).unwrap(); - let mut impl_ty = None; - for impl_item in impl_items { - let assoc_type = match *impl_or_trait_items_map.get(&impl_item.def_id()).unwrap() { - ty::TypeTraitItem(ref assoc_type) => assoc_type.clone(), - ty::ConstTraitItem(..) | ty::MethodTraitItem(..) => { continue; } - }; - - if assoc_type.name != obligation.predicate.item_name { - continue; - } - - let impl_poly_ty = ty::lookup_item_type(selcx.tcx(), assoc_type.def_id); - impl_ty = Some(impl_poly_ty.ty.subst(selcx.tcx(), &impl_vtable.substs)); - break; - } - - match impl_ty { - Some(ty) => (ty, impl_vtable.nested.into_vec()), - None => { - // This means that the impl is missing a - // definition for the associated type. This error - // ought to be reported by the type checker method - // `check_impl_items_against_trait`, so here we - // just return ty_err. - (selcx.tcx().types.err, vec!()) + for impl_item in &selcx.tcx().impl_items.borrow()[&impl_vtable.impl_def_id] { + if let ty::TypeTraitItem(ref assoc_ty) = impl_or_trait_items_map[&impl_item.def_id()] { + if assoc_ty.name == obligation.predicate.item_name { + return (assoc_ty.ty.unwrap().subst(selcx.tcx(), &impl_vtable.substs), + impl_vtable.nested.into_vec()); + } } } + + let trait_ref = obligation.predicate.trait_ref; + for trait_item in ty::trait_items(selcx.tcx(), trait_ref.def_id).iter() { + if let &ty::TypeTraitItem(ref assoc_ty) = trait_item { + if assoc_ty.name == obligation.predicate.item_name { + if let Some(ty) = assoc_ty.ty { + return (ty.subst(selcx.tcx(), trait_ref.substs), + impl_vtable.nested.into_vec()); + } else { + // This means that the impl is missing a + // definition for the associated type. This error + // ought to be reported by the type checker method + // `check_impl_items_against_trait`, so here we + // just return ty_err. + return (selcx.tcx().types.err, vec!()); + } + } + } + } + + selcx.tcx().sess.span_bug(obligation.cause.span, + &format!("No associated type for {}", + trait_ref.repr(selcx.tcx()))); } impl<'tcx> Repr<'tcx> for ProjectionTyError<'tcx> { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 2ccbb0c5c10..a67a968ea2c 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -136,7 +136,7 @@ impl ImplOrTraitItemContainer { pub enum ImplOrTraitItem<'tcx> { ConstTraitItem(Rc>), MethodTraitItem(Rc>), - TypeTraitItem(Rc), + TypeTraitItem(Rc>), } impl<'tcx> ImplOrTraitItem<'tcx> { @@ -267,8 +267,9 @@ pub struct AssociatedConst<'tcx> { } #[derive(Clone, Copy, Debug)] -pub struct AssociatedType { +pub struct AssociatedType<'tcx> { pub name: ast::Name, + pub ty: Option>, pub vis: ast::Visibility, pub def_id: ast::DefId, pub container: ImplOrTraitItemContainer, diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index cf2911ab182..6f71def1188 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -1077,7 +1077,7 @@ impl<'tcx> Repr<'tcx> for ty::AssociatedConst<'tcx> { } } -impl<'tcx> Repr<'tcx> for ty::AssociatedType { +impl<'tcx> Repr<'tcx> for ty::AssociatedType<'tcx> { fn repr(&self, tcx: &ctxt<'tcx>) -> String { format!("AssociatedType(name: {}, vis: {}, def_id: {})", self.name.repr(tcx), diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index eb6e90414e3..05aad1d64f7 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1070,7 +1070,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, // Check for missing items from trait let provided_methods = ty::provided_trait_methods(tcx, impl_trait_ref.def_id); let associated_consts = ty::associated_consts(tcx, impl_trait_ref.def_id); - let mut missing_methods = Vec::new(); + let mut missing_items = Vec::new(); for trait_item in &*trait_items { match *trait_item { ty::ConstTraitItem(ref associated_const) => { @@ -1086,8 +1086,8 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, associated_consts.iter().any(|ac| ac.default.is_some() && ac.name == associated_const.name); if !is_implemented && !is_provided { - missing_methods.push(format!("`{}`", - token::get_name(associated_const.name))); + missing_items.push(format!("`{}`", + token::get_name(associated_const.name))); } } ty::MethodTraitItem(ref trait_method) => { @@ -1103,7 +1103,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let is_provided = provided_methods.iter().any(|m| m.name == trait_method.name); if !is_implemented && !is_provided { - missing_methods.push(format!("`{}`", token::get_name(trait_method.name))); + missing_items.push(format!("`{}`", token::get_name(trait_method.name))); } } ty::TypeTraitItem(ref associated_type) => { @@ -1115,17 +1115,18 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, _ => false, } }); - if !is_implemented { - missing_methods.push(format!("`{}`", token::get_name(associated_type.name))); + let is_provided = associated_type.ty.is_some(); + if !is_implemented && !is_provided { + missing_items.push(format!("`{}`", token::get_name(associated_type.name))); } } } } - if !missing_methods.is_empty() { + if !missing_items.is_empty() { span_err!(tcx.sess, impl_span, E0046, "not all trait items implemented, missing: {}", - missing_methods.connect(", ")); + missing_items.connect(", ")); } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 2fba967b3b2..6507f6dc372 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -718,15 +718,17 @@ fn convert_associated_const<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, .insert(local_def(id), ty::ConstTraitItem(associated_const)); } -fn as_refsociated_type<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, - container: ImplOrTraitItemContainer, - ident: ast::Ident, - id: ast::NodeId, - vis: ast::Visibility) +fn convert_associated_type<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, + container: ImplOrTraitItemContainer, + ident: ast::Ident, + id: ast::NodeId, + vis: ast::Visibility, + ty: Option>) { let associated_type = Rc::new(ty::AssociatedType { name: ident.name, vis: vis, + ty: ty, def_id: local_def(id), container: container }); @@ -876,21 +878,14 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) { if let ast::TypeImplItem(ref ty) = impl_item.node { if opt_trait_ref.is_none() { span_err!(tcx.sess, impl_item.span, E0202, - "associated items are not allowed in inherent impls"); + "associated types are not allowed in inherent impls"); } - as_refsociated_type(ccx, ImplContainer(local_def(it.id)), - impl_item.ident, impl_item.id, impl_item.vis); - let typ = ccx.icx(&ty_predicates).to_ty(&ExplicitRscope, ty); - tcx.tcache.borrow_mut().insert(local_def(impl_item.id), - TypeScheme { - generics: ty::Generics::empty(), - ty: typ, - }); - tcx.predicates.borrow_mut().insert(local_def(impl_item.id), - ty::GenericPredicates::empty()); - write_ty_to_tcx(tcx, impl_item.id, typ); + + convert_associated_type(ccx, ImplContainer(local_def(it.id)), + impl_item.ident, impl_item.id, impl_item.vis, + Some(typ)); } } @@ -973,9 +968,14 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) { // Convert all the associated types. for trait_item in trait_items { match trait_item.node { - ast::TypeTraitItem(..) => { - as_refsociated_type(ccx, TraitContainer(local_def(it.id)), - trait_item.ident, trait_item.id, ast::Public); + ast::TypeTraitItem(_, ref opt_ty) => { + let typ = opt_ty.as_ref().map({ + |ty| ccx.icx(&trait_predicates).to_ty(&ExplicitRscope, &ty) + }); + + convert_associated_type(ccx, TraitContainer(local_def(it.id)), + trait_item.ident, trait_item.id, ast::Public, + typ); } _ => {} } @@ -2292,10 +2292,10 @@ fn enforce_impl_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>, let lifetimes_in_associated_types: HashSet<_> = impl_items.iter() - .filter_map(|item| match item.node { - ast::TypeImplItem(..) => Some(ty::node_id_to_type(tcx, item.id)), - ast::ConstImplItem(..) | ast::MethodImplItem(..) | - ast::MacImplItem(..) => None, + .map(|item| ty::impl_or_trait_item(tcx, local_def(item.id))) + .filter_map(|item| match item { + ty::TypeTraitItem(ref assoc_ty) => assoc_ty.ty, + ty::ConstTraitItem(..) | ty::MethodTraitItem(..) => None }) .flat_map(|ty| ctp::parameters_for_type(ty).into_iter()) .filter_map(|p| match p { diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 9c64b7b4ab6..fcc4e5bb96c 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -362,11 +362,13 @@ pub fn build_impl(cx: &DocContext, } ty::TypeTraitItem(ref assoc_ty) => { let did = assoc_ty.def_id; - let type_scheme = ty::lookup_item_type(tcx, did); - let predicates = ty::lookup_predicates(tcx, did); + let type_scheme = ty::TypeScheme { + ty: assoc_ty.ty.unwrap(), + generics: ty::Generics::empty() + }; // Not sure the choice of ParamSpace actually matters here, // because an associated type won't have generics on the LHS - let typedef = (type_scheme, predicates, + let typedef = (type_scheme, ty::GenericPredicates::empty(), subst::ParamSpace::TypeSpace).clean(cx); Some(clean::Item { name: Some(assoc_ty.name.clean(cx)), diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 95444bb9158..b5aa27d0b03 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2724,43 +2724,40 @@ impl<'tcx> Clean for ty::AssociatedConst<'tcx> { } } -impl Clean for ty::AssociatedType { +impl<'tcx> Clean for ty::AssociatedType<'tcx> { fn clean(&self, cx: &DocContext) -> Item { - // When loading a cross-crate associated type, the bounds for this type - // are actually located on the trait/impl itself, so we need to load - // all of the generics from there and then look for bounds that are - // applied to this associated type in question. - let predicates = ty::lookup_predicates(cx.tcx(), self.container.id()); - let generics = match self.container { - ty::TraitContainer(did) => { - let def = ty::lookup_trait_def(cx.tcx(), did); - (&def.generics, &predicates, subst::TypeSpace).clean(cx) - } - ty::ImplContainer(did) => { - let ty = ty::lookup_item_type(cx.tcx(), did); - (&ty.generics, &predicates, subst::TypeSpace).clean(cx) - } - }; let my_name = self.name.clean(cx); - let mut bounds = generics.where_predicates.iter().filter_map(|pred| { - let (name, self_type, trait_, bounds) = match *pred { - WherePredicate::BoundPredicate { - ty: QPath { ref name, ref self_type, ref trait_ }, - ref bounds - } => (name, self_type, trait_, bounds), - _ => return None, - }; - if *name != my_name { return None } - match **trait_ { - ResolvedPath { did, .. } if did == self.container.id() => {} - _ => return None, - } - match **self_type { - Generic(ref s) if *s == "Self" => {} - _ => return None, - } - Some(bounds) - }).flat_map(|i| i.iter().cloned()).collect::>(); + + let mut bounds = if let ty::TraitContainer(did) = self.container { + // When loading a cross-crate associated type, the bounds for this type + // are actually located on the trait/impl itself, so we need to load + // all of the generics from there and then look for bounds that are + // applied to this associated type in question. + let def = ty::lookup_trait_def(cx.tcx(), did); + let predicates = ty::lookup_predicates(cx.tcx(), did); + let generics = (&def.generics, &predicates, subst::TypeSpace).clean(cx); + generics.where_predicates.iter().filter_map(|pred| { + let (name, self_type, trait_, bounds) = match *pred { + WherePredicate::BoundPredicate { + ty: QPath { ref name, ref self_type, ref trait_ }, + ref bounds + } => (name, self_type, trait_, bounds), + _ => return None, + }; + if *name != my_name { return None } + match **trait_ { + ResolvedPath { did, .. } if did == self.container.id() => {} + _ => return None, + } + match **self_type { + Generic(ref s) if *s == "Self" => {} + _ => return None, + } + Some(bounds) + }).flat_map(|i| i.iter().cloned()).collect::>() + } else { + vec![] + }; // Our Sized/?Sized bound didn't get handled when creating the generics // because we didn't actually get our whole set of bounds until just now @@ -2776,7 +2773,7 @@ impl Clean for ty::AssociatedType { source: DUMMY_SP.clean(cx), name: Some(self.name.clean(cx)), attrs: inline::load_attrs(cx, cx.tcx(), self.def_id), - inner: AssociatedTypeItem(bounds, None), + inner: AssociatedTypeItem(bounds, self.ty.clean(cx)), visibility: self.vis.clean(cx), def_id: self.def_id, stability: stability::lookup(cx.tcx(), self.def_id).clean(cx), diff --git a/src/test/run-pass/default-associated-types.rs b/src/test/run-pass/default-associated-types.rs new file mode 100644 index 00000000000..b3def429b9b --- /dev/null +++ b/src/test/run-pass/default-associated-types.rs @@ -0,0 +1,30 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo { + type Out = T; + fn foo(&self) -> Self::Out; +} + +impl Foo for () { + fn foo(&self) -> u32 { + 4u32 + } +} + +impl Foo for bool { + type Out = (); + fn foo(&self) {} +} + +fn main() { + assert_eq!(<() as Foo>::foo(&()), 4u32); + assert_eq!(>::foo(&true), ()); +}