diff --git a/src/grammar/parser-lalr.y b/src/grammar/parser-lalr.y index 6c3fd186cd423..27e15bc2f0eea 100644 --- a/src/grammar/parser-lalr.y +++ b/src/grammar/parser-lalr.y @@ -505,10 +505,20 @@ trait_items ; trait_item -: trait_type +: trait_const +| trait_type | trait_method ; +trait_const +: maybe_outer_attrs CONST ident maybe_const_default ';' { $$ = mk_node("ConstTraitItem", 3, $1, $3, $4); } +; + +maybe_const_default +: '=' expr { $$ = mk_node("ConstDefault", 1, $2); } +| %empty { $$ = mk_none(); } +; + trait_type : maybe_outer_attrs TYPE ty_param ';' { $$ = mk_node("TypeTraitItem", 2, $1, $3); } ; @@ -611,7 +621,16 @@ impl_items impl_item : impl_method | item_macro -| trait_type +| impl_const +| impl_type +; + +impl_const +: attrs_and_vis item_const { $$ = mk_node("ImplConst", 1, $1, $2); } +; + +impl_type +: attrs_and_vis TYPE ident generic_params '=' ty_sum ';' { $$ = mk_node("ImplType", 4, $1, $3, $4, $6); } ; item_fn diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs index d528e38d341cf..93056d949dbb0 100644 --- a/src/librustc/metadata/csearch.rs +++ b/src/librustc/metadata/csearch.rs @@ -175,6 +175,13 @@ pub fn get_provided_trait_methods<'tcx>(tcx: &ty::ctxt<'tcx>, decoder::get_provided_trait_methods(cstore.intr.clone(), &*cdata, def.node, tcx) } +pub fn get_associated_consts<'tcx>(tcx: &ty::ctxt<'tcx>, def: ast::DefId) + -> Vec>> { + let cstore = &tcx.sess.cstore; + let cdata = cstore.get_crate_data(def.krate); + decoder::get_associated_consts(cstore.intr.clone(), &*cdata, def.node, tcx) +} + pub fn get_type_name_if_impl(cstore: &cstore::CStore, def: ast::DefId) -> Option { let cdata = cstore.get_crate_data(def.krate); diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 8e3f77f949b6b..00a7fe68f2fb0 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -305,7 +305,25 @@ fn item_to_def_like(item: rbml::Doc, did: ast::DefId, cnum: ast::CrateNum) -> DefLike { let fam = item_family(item); match fam { - Constant => DlDef(def::DefConst(did)), + Constant => { + // Check whether we have an associated const item. + if item_sort(item) == Some('C') { + // Check whether the associated const is from a trait or impl. + // See the comment for methods below. + let provenance = if reader::maybe_get_doc( + item, tag_item_trait_parent_sort).is_some() { + def::FromTrait(item_reqd_and_translated_parent_item(cnum, + item)) + } else { + def::FromImpl(item_reqd_and_translated_parent_item(cnum, + item)) + }; + DlDef(def::DefAssociatedConst(did, provenance)) + } else { + // Regular const item. + DlDef(def::DefConst(did)) + } + } ImmStatic => DlDef(def::DefStatic(did, false)), MutStatic => DlDef(def::DefStatic(did, true)), Struct => DlDef(def::DefStruct(did)), @@ -826,6 +844,7 @@ pub fn get_impl_items(cdata: Cmd, impl_id: ast::NodeId) tag_item_impl_item, |doc| { let def_id = item_def_id(doc, cdata); match item_sort(doc) { + Some('C') => impl_items.push(ty::ConstTraitItemId(def_id)), Some('r') | Some('p') => { impl_items.push(ty::MethodTraitItemId(def_id)) } @@ -877,6 +896,18 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc, let vis = item_visibility(method_doc); match item_sort(method_doc) { + Some('C') => { + let ty = doc_type(method_doc, tcx, cdata); + let default = get_provided_source(method_doc, cdata); + ty::ConstTraitItem(Rc::new(ty::AssociatedConst { + name: name, + ty: ty, + vis: vis, + def_id: def_id, + container: container, + default: default, + })) + } 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); @@ -914,6 +945,7 @@ pub fn get_trait_item_def_ids(cdata: Cmd, id: ast::NodeId) reader::tagged_docs(item, tag_item_trait_item, |mth| { let def_id = item_def_id(mth, cdata); match item_sort(mth) { + Some('C') => result.push(ty::ConstTraitItemId(def_id)), Some('r') | Some('p') => { result.push(ty::MethodTraitItemId(def_id)); } @@ -961,6 +993,34 @@ pub fn get_provided_trait_methods<'tcx>(intr: Rc, return result; } +pub fn get_associated_consts<'tcx>(intr: Rc, + cdata: Cmd, + id: ast::NodeId, + tcx: &ty::ctxt<'tcx>) + -> Vec>> { + let data = cdata.data(); + let item = lookup_item(id, data); + let mut result = Vec::new(); + + reader::tagged_docs(item, tag_item_trait_item, |ac_id| { + let did = item_def_id(ac_id, cdata); + let ac_doc = lookup_item(did.node, data); + + if item_sort(ac_doc) == Some('C') { + let trait_item = get_impl_or_trait_item(intr.clone(), + cdata, + did.node, + tcx); + if let ty::ConstTraitItem(ref ac) = trait_item { + result.push((*ac).clone()) + } + } + true + }); + + return result; +} + pub fn get_type_name_if_impl(cdata: Cmd, node_id: ast::NodeId) -> Option { let item = lookup_item(node_id, cdata.data()); diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 5f31e24a0637f..bcbb350fc34ee 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -799,6 +799,43 @@ fn encode_method_ty_fields<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_provided_source(rbml_w, method_ty.provided_source); } +fn encode_info_for_associated_const(ecx: &EncodeContext, + rbml_w: &mut Encoder, + associated_const: &ty::AssociatedConst, + impl_path: PathElems, + parent_id: NodeId, + impl_item_opt: Option<&ast::ImplItem>) { + debug!("encode_info_for_associated_const({:?},{:?})", + associated_const.def_id, + token::get_name(associated_const.name)); + + rbml_w.start_tag(tag_items_data_item); + + encode_def_id(rbml_w, associated_const.def_id); + encode_name(rbml_w, associated_const.name); + encode_visibility(rbml_w, associated_const.vis); + encode_family(rbml_w, 'C'); + encode_provided_source(rbml_w, associated_const.default); + + encode_parent_item(rbml_w, local_def(parent_id)); + encode_item_sort(rbml_w, 'C'); + + encode_bounds_and_type_for_item(rbml_w, ecx, associated_const.def_id.local_id()); + + let stab = stability::lookup(ecx.tcx, associated_const.def_id); + encode_stability(rbml_w, stab); + + let elem = ast_map::PathName(associated_const.name); + encode_path(rbml_w, impl_path.chain(Some(elem).into_iter())); + + if let Some(ii) = impl_item_opt { + encode_attributes(rbml_w, &ii.attrs); + encode_inlined_item(ecx, rbml_w, IIImplItemRef(local_def(parent_id), ii)); + } + + rbml_w.end_tag(); +} + fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, rbml_w: &mut Encoder, m: &ty::Method<'tcx>, @@ -1192,7 +1229,10 @@ fn encode_info_for_item(ecx: &EncodeContext, for &item_def_id in items { rbml_w.start_tag(tag_item_impl_item); match item_def_id { - ty::ConstTraitItemId(_) => {} + ty::ConstTraitItemId(item_def_id) => { + encode_def_id(rbml_w, item_def_id); + encode_item_sort(rbml_w, 'C'); + } ty::MethodTraitItemId(item_def_id) => { encode_def_id(rbml_w, item_def_id); encode_item_sort(rbml_w, 'r'); @@ -1230,7 +1270,14 @@ fn encode_info_for_item(ecx: &EncodeContext, }); match ty::impl_or_trait_item(tcx, trait_item_def_id.def_id()) { - ty::ConstTraitItem(_) => {} + ty::ConstTraitItem(ref associated_const) => { + encode_info_for_associated_const(ecx, + rbml_w, + &*associated_const, + path.clone(), + item.id, + ast_item) + } ty::MethodTraitItem(ref method_type) => { encode_info_for_method(ecx, rbml_w, @@ -1275,7 +1322,10 @@ fn encode_info_for_item(ecx: &EncodeContext, for &method_def_id in &*ty::trait_item_def_ids(tcx, def_id) { rbml_w.start_tag(tag_item_trait_item); match method_def_id { - ty::ConstTraitItemId(_) => {} + ty::ConstTraitItemId(const_def_id) => { + encode_def_id(rbml_w, const_def_id); + encode_item_sort(rbml_w, 'C'); + } ty::MethodTraitItemId(method_def_id) => { encode_def_id(rbml_w, method_def_id); encode_item_sort(rbml_w, 'r'); @@ -1321,7 +1371,23 @@ fn encode_info_for_item(ecx: &EncodeContext, ty::impl_or_trait_item(tcx, item_def_id.def_id()); let is_nonstatic_method; match trait_item_type { - ty::ConstTraitItem(_) => { + ty::ConstTraitItem(associated_const) => { + encode_name(rbml_w, associated_const.name); + encode_def_id(rbml_w, associated_const.def_id); + encode_visibility(rbml_w, associated_const.vis); + + encode_provided_source(rbml_w, associated_const.default); + + let elem = ast_map::PathName(associated_const.name); + encode_path(rbml_w, + path.clone().chain(Some(elem).into_iter())); + + encode_item_sort(rbml_w, 'C'); + encode_family(rbml_w, 'C'); + + encode_bounds_and_type_for_item(rbml_w, ecx, + associated_const.def_id.local_id()); + is_nonstatic_method = false; } ty::MethodTraitItem(method_ty) => { @@ -1368,7 +1434,10 @@ fn encode_info_for_item(ecx: &EncodeContext, let trait_item = &*ms[i]; encode_attributes(rbml_w, &trait_item.attrs); match trait_item.node { - ast::ConstTraitItem(_, _) => {} + ast::ConstTraitItem(_, _) => { + encode_inlined_item(ecx, rbml_w, + IITraitItemRef(def_id, trait_item)); + } ast::MethodTraitItem(ref sig, ref body) => { // If this is a static method, we've already // encoded this. @@ -1388,9 +1457,7 @@ fn encode_info_for_item(ecx: &EncodeContext, encode_method_argument_names(rbml_w, &sig.decl); } - ast::TypeTraitItem(..) => { - encode_item_sort(rbml_w, 't'); - } + ast::TypeTraitItem(..) => {} } rbml_w.end_tag(); diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index ee9d1e015539e..80326229618c2 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -223,6 +223,28 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { } } + fn visit_trait_item(&mut self, t: &'v ast::TraitItem) { + match t.node { + ast::ConstTraitItem(_, ref default) => { + if let Some(ref expr) = *default { + self.global_expr(Mode::Const, &*expr); + } else { + visit::walk_trait_item(self, t); + } + } + _ => self.with_mode(Mode::Var, |v| visit::walk_trait_item(v, t)), + } + } + + fn visit_impl_item(&mut self, i: &'v ast::ImplItem) { + match i.node { + ast::ConstImplItem(_, ref expr) => { + self.global_expr(Mode::Const, &*expr); + } + _ => self.with_mode(Mode::Var, |v| visit::walk_impl_item(v, i)), + } + } + fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl, @@ -468,13 +490,16 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, Mode::Var => v.add_qualif(NOT_CONST) } } - Some(def::DefConst(did)) => { - if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did) { + Some(def::DefConst(did)) | + Some(def::DefAssociatedConst(did, _)) => { + if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did, + Some(e.id)) { let inner = v.global_expr(Mode::Const, expr); v.add_qualif(inner); } else { - v.tcx.sess.span_bug(e.span, "DefConst doesn't point \ - to an ItemConst"); + v.tcx.sess.span_bug(e.span, + "DefConst or DefAssociatedConst \ + doesn't point to a constant"); } } def => { diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 912854a6d7dfb..13be6d0cb7d9b 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -442,7 +442,8 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> { ast::PatIdent(..) | ast::PatEnum(..) => { let def = self.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()); match def { - Some(DefConst(did)) => match lookup_const_by_id(self.tcx, did) { + Some(DefAssociatedConst(did, _)) | + Some(DefConst(did)) => match lookup_const_by_id(self.tcx, did, Some(pat.id)) { Some(const_expr) => { const_expr_to_pat(self.tcx, const_expr, pat.span).map(|new_pat| { @@ -746,7 +747,7 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat, match pat.node { ast::PatIdent(..) => match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) { - Some(DefConst(..)) => + Some(DefConst(..)) | Some(DefAssociatedConst(..)) => cx.tcx.sess.span_bug(pat.span, "const pattern should've \ been rewritten"), Some(DefStruct(_)) => vec!(Single), @@ -755,7 +756,7 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat, }, ast::PatEnum(..) => match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) { - Some(DefConst(..)) => + Some(DefConst(..)) | Some(DefAssociatedConst(..)) => cx.tcx.sess.span_bug(pat.span, "const pattern should've \ been rewritten"), Some(DefVariant(_, id, _)) => vec!(Variant(id)), @@ -763,7 +764,7 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat, }, ast::PatStruct(..) => match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) { - Some(DefConst(..)) => + Some(DefConst(..)) | Some(DefAssociatedConst(..)) => cx.tcx.sess.span_bug(pat.span, "const pattern should've \ been rewritten"), Some(DefVariant(_, id, _)) => vec!(Variant(id)), @@ -861,7 +862,7 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], ast::PatIdent(_, _, _) => { let opt_def = cx.tcx.def_map.borrow().get(&pat_id).map(|d| d.full_def()); match opt_def { - Some(DefConst(..)) => + Some(DefConst(..)) | Some(DefAssociatedConst(..)) => cx.tcx.sess.span_bug(pat_span, "const pattern should've \ been rewritten"), Some(DefVariant(_, id, _)) => if *constructor == Variant(id) { @@ -876,7 +877,7 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], ast::PatEnum(_, ref args) => { let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def(); match def { - DefConst(..) => + DefConst(..) | DefAssociatedConst(..) => cx.tcx.sess.span_bug(pat_span, "const pattern should've \ been rewritten"), DefVariant(_, id, _) if *constructor != Variant(id) => None, @@ -894,7 +895,7 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], // Is this a struct or an enum variant? let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def(); let class_id = match def { - DefConst(..) => + DefConst(..) | DefAssociatedConst(..) => cx.tcx.sess.span_bug(pat_span, "const pattern should've \ been rewritten"), DefVariant(_, variant_id, _) => if *constructor == Variant(variant_id) { diff --git a/src/librustc/middle/check_static_recursion.rs b/src/librustc/middle/check_static_recursion.rs index b97978fc03fff..a521c4531c9a7 100644 --- a/src/librustc/middle/check_static_recursion.rs +++ b/src/librustc/middle/check_static_recursion.rs @@ -12,10 +12,11 @@ // recursively. use session::Session; -use middle::def::{DefStatic, DefConst, DefMap}; +use middle::def::{DefStatic, DefConst, DefAssociatedConst, DefMap}; use syntax::ast; use syntax::{ast_util, ast_map}; +use syntax::codemap::Span; use syntax::visit::Visitor; use syntax::visit; @@ -26,8 +27,43 @@ struct CheckCrateVisitor<'a, 'ast: 'a> { } impl<'v, 'a, 'ast> Visitor<'v> for CheckCrateVisitor<'a, 'ast> { - fn visit_item(&mut self, i: &ast::Item) { - check_item(self, i); + fn visit_item(&mut self, it: &ast::Item) { + match it.node { + ast::ItemStatic(_, _, ref expr) | + ast::ItemConst(_, ref expr) => { + let mut recursion_visitor = + CheckItemRecursionVisitor::new(self, &it.span); + recursion_visitor.visit_item(it); + visit::walk_expr(self, &*expr) + }, + _ => visit::walk_item(self, it) + } + } + + fn visit_trait_item(&mut self, ti: &ast::TraitItem) { + match ti.node { + ast::ConstTraitItem(_, ref default) => { + if let Some(ref expr) = *default { + let mut recursion_visitor = + CheckItemRecursionVisitor::new(self, &ti.span); + recursion_visitor.visit_trait_item(ti); + visit::walk_expr(self, &*expr) + } + } + _ => visit::walk_trait_item(self, ti) + } + } + + fn visit_impl_item(&mut self, ii: &ast::ImplItem) { + match ii.node { + ast::ConstImplItem(_, ref expr) => { + let mut recursion_visitor = + CheckItemRecursionVisitor::new(self, &ii.span); + recursion_visitor.visit_impl_item(ii); + visit::walk_expr(self, &*expr) + } + _ => visit::walk_impl_item(self, ii) + } } } @@ -44,51 +80,48 @@ pub fn check_crate<'ast>(sess: &Session, sess.abort_if_errors(); } -fn check_item(v: &mut CheckCrateVisitor, it: &ast::Item) { - match it.node { - ast::ItemStatic(_, _, ref ex) | - ast::ItemConst(_, ref ex) => { - check_item_recursion(v.sess, v.ast_map, v.def_map, it); - visit::walk_expr(v, &**ex) - }, - _ => visit::walk_item(v, it) - } -} - struct CheckItemRecursionVisitor<'a, 'ast: 'a> { - root_it: &'a ast::Item, + root_span: &'a Span, sess: &'a Session, ast_map: &'a ast_map::Map<'ast>, def_map: &'a DefMap, idstack: Vec } -// Make sure a const item doesn't recursively refer to itself -// FIXME: Should use the dependency graph when it's available (#1356) -pub fn check_item_recursion<'a>(sess: &'a Session, - ast_map: &'a ast_map::Map, - def_map: &'a DefMap, - it: &'a ast::Item) { - - let mut visitor = CheckItemRecursionVisitor { - root_it: it, - sess: sess, - ast_map: ast_map, - def_map: def_map, - idstack: Vec::new() - }; - visitor.visit_item(it); +impl<'a, 'ast: 'a> CheckItemRecursionVisitor<'a, 'ast> { + fn new(v: &CheckCrateVisitor<'a, 'ast>, span: &'a Span) + -> CheckItemRecursionVisitor<'a, 'ast> { + CheckItemRecursionVisitor { + root_span: span, + sess: v.sess, + ast_map: v.ast_map, + def_map: v.def_map, + idstack: Vec::new() + } + } + fn with_item_id_pushed(&mut self, id: ast::NodeId, f: F) + where F: Fn(&mut Self) { + if self.idstack.iter().any(|x| x == &(id)) { + span_err!(self.sess, *self.root_span, E0265, "recursive constant"); + return; + } + self.idstack.push(id); + f(self); + self.idstack.pop(); + } } impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> { fn visit_item(&mut self, it: &ast::Item) { - if self.idstack.iter().any(|x| x == &(it.id)) { - span_err!(self.sess, self.root_it.span, E0265, "recursive constant"); - return; - } - self.idstack.push(it.id); - visit::walk_item(self, it); - self.idstack.pop(); + self.with_item_id_pushed(it.id, |v| visit::walk_item(v, it)); + } + + fn visit_trait_item(&mut self, ti: &ast::TraitItem) { + self.with_item_id_pushed(ti.id, |v| visit::walk_trait_item(v, ti)); + } + + fn visit_impl_item(&mut self, ii: &ast::ImplItem) { + self.with_item_id_pushed(ii.id, |v| visit::walk_impl_item(v, ii)); } fn visit_expr(&mut self, e: &ast::Expr) { @@ -96,11 +129,16 @@ impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> { ast::ExprPath(..) => { match self.def_map.borrow().get(&e.id).map(|d| d.base_def) { Some(DefStatic(def_id, _)) | + Some(DefAssociatedConst(def_id, _)) | Some(DefConst(def_id)) if ast_util::is_local(def_id) => { match self.ast_map.get(def_id.node) { ast_map::NodeItem(item) => self.visit_item(item), + ast_map::NodeTraitItem(item) => + self.visit_trait_item(item), + ast_map::NodeImplItem(item) => + self.visit_impl_item(item), ast_map::NodeForeignItem(_) => {}, _ => { span_err!(self.sess, e.span, E0266, diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 2c6ffb3281fcb..b5a173a569f45 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -16,11 +16,12 @@ pub use self::const_val::*; use self::ErrKind::*; use metadata::csearch; -use middle::{astencode, def}; +use middle::{astencode, def, infer, subst, traits}; use middle::pat_util::def_to_path; use middle::ty::{self, Ty}; use middle::astconv_util::ast_ty_to_prim_ty; use util::num::ToPrimitive; +use util::ppaux::Repr; use syntax::ast::{self, Expr}; use syntax::codemap::Span; @@ -39,8 +40,9 @@ use std::rc::Rc; fn lookup_const<'a>(tcx: &'a ty::ctxt, e: &Expr) -> Option<&'a Expr> { let opt_def = tcx.def_map.borrow().get(&e.id).map(|d| d.full_def()); match opt_def { - Some(def::DefConst(def_id)) => { - lookup_const_by_id(tcx, def_id) + Some(def::DefConst(def_id)) | + Some(def::DefAssociatedConst(def_id, _)) => { + lookup_const_by_id(tcx, def_id, Some(e.id)) } Some(def::DefVariant(enum_def, variant_def, _)) => { lookup_variant_by_id(tcx, enum_def, variant_def) @@ -101,14 +103,36 @@ fn lookup_variant_by_id<'a>(tcx: &'a ty::ctxt, } } -pub fn lookup_const_by_id<'a>(tcx: &'a ty::ctxt, def_id: ast::DefId) - -> Option<&'a Expr> { +pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>, + def_id: ast::DefId, + maybe_ref_id: Option) + -> Option<&'tcx Expr> { if ast_util::is_local(def_id) { match tcx.map.find(def_id.node) { None => None, Some(ast_map::NodeItem(it)) => match it.node { ast::ItemConst(_, ref const_expr) => { - Some(&**const_expr) + Some(&*const_expr) + } + _ => None + }, + Some(ast_map::NodeTraitItem(ti)) => match ti.node { + ast::ConstTraitItem(_, ref default) => { + match maybe_ref_id { + Some(ref_id) => { + let trait_id = ty::trait_of_item(tcx, def_id) + .unwrap(); + resolve_trait_associated_const(tcx, ti, trait_id, + ref_id) + } + None => default.as_ref().map(|expr| &**expr), + } + } + _ => None + }, + Some(ast_map::NodeImplItem(ii)) => match ii.node { + ast::ConstImplItem(_, ref expr) => { + Some(&*expr) } _ => None }, @@ -122,16 +146,42 @@ pub fn lookup_const_by_id<'a>(tcx: &'a ty::ctxt, def_id: ast::DefId) } None => {} } + let mut used_ref_id = false; let expr_id = match csearch::maybe_get_item_ast(tcx, def_id, Box::new(|a, b, c, d| astencode::decode_inlined_item(a, b, c, d))) { csearch::FoundAst::Found(&ast::IIItem(ref item)) => match item.node { ast::ItemConst(_, ref const_expr) => Some(const_expr.id), _ => None }, + csearch::FoundAst::Found(&ast::IITraitItem(_, ref ti)) => match ti.node { + ast::ConstTraitItem(_, ref default) => { + used_ref_id = true; + match maybe_ref_id { + Some(ref_id) => { + let trait_id = ty::trait_of_item(tcx, def_id) + .unwrap(); + resolve_trait_associated_const(tcx, ti, trait_id, + ref_id).map(|e| e.id) + } + None => default.as_ref().map(|expr| expr.id), + } + } + _ => None + }, + csearch::FoundAst::Found(&ast::IIImplItem(_, ref ii)) => match ii.node { + ast::ConstImplItem(_, ref expr) => Some(expr.id), + _ => None + }, _ => None }; - tcx.extern_const_statics.borrow_mut().insert(def_id, - expr_id.unwrap_or(ast::DUMMY_NODE_ID)); + // If we used the reference expression, particularly to choose an impl + // of a trait-associated const, don't cache that, because the next + // lookup with the same def_id may yield a different result. + if used_ref_id { + tcx.extern_const_statics + .borrow_mut().insert(def_id, + expr_id.unwrap_or(ast::DUMMY_NODE_ID)); + } expr_id.map(|id| tcx.map.expect_expr(id)) } } @@ -755,7 +805,35 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, _ => (None, None) } } else { - (lookup_const_by_id(tcx, def_id), None) + (lookup_const_by_id(tcx, def_id, Some(e.id)), None) + } + } + Some(def::DefAssociatedConst(def_id, provenance)) => { + if ast_util::is_local(def_id) { + match provenance { + def::FromTrait(trait_id) => match tcx.map.find(def_id.node) { + Some(ast_map::NodeTraitItem(ti)) => match ti.node { + ast::ConstTraitItem(ref ty, _) => { + (resolve_trait_associated_const(tcx, ti, + trait_id, e.id), + Some(&**ty)) + } + _ => (None, None) + }, + _ => (None, None) + }, + def::FromImpl(_) => match tcx.map.find(def_id.node) { + Some(ast_map::NodeImplItem(ii)) => match ii.node { + ast::ConstImplItem(ref ty, ref expr) => { + (Some(&**expr), Some(&**ty)) + } + _ => (None, None) + }, + _ => (None, None) + }, + } + } else { + (lookup_const_by_id(tcx, def_id, Some(e.id)), None) } } Some(def::DefVariant(enum_def, variant_def, _)) => { @@ -833,6 +911,71 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, Ok(result) } +fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>, + ti: &'tcx ast::TraitItem, + trait_id: ast::DefId, + ref_id: ast::NodeId) + -> Option<&'tcx Expr> +{ + let rcvr_substs = ty::node_id_item_substs(tcx, ref_id).substs; + let subst::SeparateVecsPerParamSpace { + types: rcvr_type, + selfs: rcvr_self, + fns: _, + } = rcvr_substs.types.split(); + let trait_substs = + subst::Substs::erased(subst::VecPerParamSpace::new(rcvr_type, + rcvr_self, + Vec::new())); + let trait_substs = tcx.mk_substs(trait_substs); + debug!("resolve_trait_associated_const: trait_substs={}", + trait_substs.repr(tcx)); + let trait_ref = ty::Binder(Rc::new(ty::TraitRef { def_id: trait_id, + substs: trait_substs })); + + ty::populate_implementations_for_trait_if_necessary(tcx, trait_ref.def_id()); + let infcx = infer::new_infer_ctxt(tcx); + + let param_env = ty::empty_parameter_environment(tcx); + let mut selcx = traits::SelectionContext::new(&infcx, ¶m_env); + let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), + trait_ref.to_poly_trait_predicate()); + let selection = match selcx.select(&obligation) { + Ok(Some(vtable)) => vtable, + // Still ambiguous, so give up and let the caller decide whether this + // expression is really needed yet. Some associated constant values + // can't be evaluated until monomorphization is done in trans. + Ok(None) => { + return None + } + Err(e) => { + tcx.sess.span_bug(ti.span, + &format!("Encountered error `{}` when trying \ + to select an implementation for \ + constant trait item reference.", + e.repr(tcx))) + } + }; + + match selection { + traits::VtableImpl(ref impl_data) => { + match ty::associated_consts(tcx, impl_data.impl_def_id) + .iter().find(|ic| ic.name == ti.ident.name) { + Some(ic) => lookup_const_by_id(tcx, ic.def_id, None), + None => match ti.node { + ast::ConstTraitItem(_, Some(ref expr)) => Some(&*expr), + _ => None, + }, + } + } + _ => { + tcx.sess.span_bug( + ti.span, + &format!("resolve_trait_associated_const: unexpected vtable type")) + } + } +} + fn cast_const<'tcx>(tcx: &ty::ctxt<'tcx>, val: const_val, ty: Ty) -> CastResult { macro_rules! convert_val { ($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 3f5f40ec0b59b..1b5e31f61d8ae 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -78,7 +78,7 @@ use std::vec::IntoIter; use collections::enum_set::{EnumSet, CLike}; use std::collections::{HashMap, HashSet}; use syntax::abi; -use syntax::ast::{CrateNum, DefId, ItemTrait, LOCAL_CRATE}; +use syntax::ast::{CrateNum, DefId, ItemImpl, ItemTrait, LOCAL_CRATE}; use syntax::ast::{MutImmutable, MutMutable, Name, NamedField, NodeId}; use syntax::ast::{StmtExpr, StmtSemi, StructField, UnnamedField, Visibility}; use syntax::ast_util::{self, is_local, lit_is_str, local_def}; @@ -5125,6 +5125,53 @@ pub fn provided_trait_methods<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId) } } +pub fn associated_consts<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId) + -> Vec>> { + if is_local(id) { + match cx.map.expect_item(id.node).node { + ItemTrait(_, _, _, ref tis) => { + tis.iter().filter_map(|ti| { + if let ast::ConstTraitItem(_, _) = ti.node { + match impl_or_trait_item(cx, ast_util::local_def(ti.id)) { + ConstTraitItem(ac) => Some(ac), + _ => { + cx.sess.bug("associated_consts(): \ + non-const item found from \ + looking up a constant?!") + } + } + } else { + None + } + }).collect() + } + ItemImpl(_, _, _, _, _, ref iis) => { + iis.iter().filter_map(|ii| { + if let ast::ConstImplItem(_, _) = ii.node { + match impl_or_trait_item(cx, ast_util::local_def(ii.id)) { + ConstTraitItem(ac) => Some(ac), + _ => { + cx.sess.bug("associated_consts(): \ + non-const item found from \ + looking up a constant?!") + } + } + } else { + None + } + }).collect() + } + _ => { + cx.sess.bug(&format!("associated_consts: `{:?}` is not a trait \ + or impl", id)) + } + } + } else { + let acs = csearch::get_associated_consts(cx, id); + acs.iter().map(|ac| (*ac).clone()).collect() + } +} + /// Helper for looking things up in the various maps that are populated during /// typeck::collect (e.g., `cx.impl_or_trait_items`, `cx.tcache`, etc). All of /// these share the pattern that if the id is local, it should have been loaded diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 502321d07598c..19cdc19415337 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -62,6 +62,20 @@ impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> { fn visit_item(&mut self, item: &ast::Item) { borrowck_item(self, item); } + + fn visit_trait_item(&mut self, ti: &ast::TraitItem) { + if let ast::ConstTraitItem(_, Some(ref expr)) = ti.node { + gather_loans::gather_loans_in_static_initializer(self, &*expr); + } + visit::walk_trait_item(self, ti); + } + + fn visit_impl_item(&mut self, ii: &ast::ImplItem) { + if let ast::ConstImplItem(_, ref expr) = ii.node { + gather_loans::gather_loans_in_static_initializer(self, &*expr); + } + visit::walk_impl_item(self, ii); + } } pub fn check_crate(tcx: &ty::ctxt) { diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 00c3450ebb935..b8032bda8d070 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -35,6 +35,7 @@ use syntax::codemap; use syntax::fold::{self, Folder}; use syntax::print::{pp, pprust}; use syntax::ptr::P; +use syntax::util::small_vector::SmallVector; use graphviz as dot; @@ -475,6 +476,29 @@ impl fold::Folder for ReplaceBodyWithLoop { } } + fn fold_trait_item(&mut self, i: P) -> SmallVector> { + match i.node { + ast::ConstTraitItem(..) => { + self.within_static_or_const = true; + let ret = fold::noop_fold_trait_item(i, self); + self.within_static_or_const = false; + return ret; + } + _ => fold::noop_fold_trait_item(i, self), + } + } + + fn fold_impl_item(&mut self, i: P) -> SmallVector> { + match i.node { + ast::ConstImplItem(..) => { + self.within_static_or_const = true; + let ret = fold::noop_fold_impl_item(i, self); + self.within_static_or_const = false; + return ret; + } + _ => fold::noop_fold_impl_item(i, self), + } + } fn fold_block(&mut self, b: P) -> P { fn expr_to_block(rules: ast::BlockCheckMode, diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index f4761f9550564..902e9ffca1f64 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1068,9 +1068,30 @@ impl LintPass for NonUpperCaseGlobals { } } + fn check_trait_item(&mut self, cx: &Context, ti: &ast::TraitItem) { + match ti.node { + ast::ConstTraitItem(..) => { + NonUpperCaseGlobals::check_upper_case(cx, "associated constant", + ti.ident, ti.span); + } + _ => {} + } + } + + fn check_impl_item(&mut self, cx: &Context, ii: &ast::ImplItem) { + match ii.node { + ast::ConstImplItem(..) => { + NonUpperCaseGlobals::check_upper_case(cx, "associated constant", + ii.ident, ii.span); + } + _ => {} + } + } + fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { // Lint for constants that look like binding identifiers (#7526) match (&p.node, cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def())) { + (&ast::PatIdent(_, ref path1, _), Some(def::DefAssociatedConst(..))) | (&ast::PatIdent(_, ref path1, _), Some(def::DefConst(..))) => { NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern", path1.node, p.span); diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 832a33e3fe0cf..128e29ee76e7d 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -119,6 +119,15 @@ impl<'v> Visitor<'v> for ParentVisitor { visit::walk_fn(self, a, b, c, d); } + fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) { + // visit_fn handles methods, but associated consts have to be handled + // here. + if !self.parents.contains_key(&ii.id) { + self.parents.insert(ii.id, self.curparent); + } + visit::walk_impl_item(self, ii); + } + fn visit_struct_def(&mut self, s: &ast::StructDef, _: ast::Ident, _: &'v ast::Generics, n: ast::NodeId) { // Struct constructors are parented to their struct definitions because @@ -272,7 +281,12 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> { if public_ty || public_trait { for impl_item in impl_items { match impl_item.node { - ast::ConstImplItem(_, _) => {} + ast::ConstImplItem(..) => { + if (public_ty && impl_item.vis == ast::Public) + || tr.is_some() { + self.exported_items.insert(impl_item.id); + } + } ast::MethodImplItem(ref sig, _) => { let meth_public = match sig.explicit_self.node { ast::SelfStatic => public_ty, @@ -400,7 +414,33 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> { debug!("privacy - is {:?} a public method", did); return match self.tcx.impl_or_trait_items.borrow().get(&did) { - Some(&ty::ConstTraitItem(_)) => ExternallyDenied, + Some(&ty::ConstTraitItem(ref ac)) => { + debug!("privacy - it's a const: {:?}", *ac); + match ac.container { + ty::TraitContainer(id) => { + debug!("privacy - recursing on trait {:?}", id); + self.def_privacy(id) + } + ty::ImplContainer(id) => { + match ty::impl_trait_ref(self.tcx, id) { + Some(t) => { + debug!("privacy - impl of trait {:?}", id); + self.def_privacy(t.def_id) + } + None => { + debug!("privacy - found inherent \ + associated constant {:?}", + ac.vis); + if ac.vis == ast::Public { + Allowable + } else { + ExternallyDenied + } + } + } + } + } + } Some(&ty::MethodTraitItem(ref meth)) => { debug!("privacy - well at least it's a method: {:?}", *meth); @@ -794,6 +834,7 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> { def::DefFn(..) => ck("function"), def::DefStatic(..) => ck("static"), def::DefConst(..) => ck("const"), + def::DefAssociatedConst(..) => ck("associated const"), def::DefVariant(..) => ck("variant"), def::DefTy(_, false) => ck("type"), def::DefTy(_, true) => ck("enum"), diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index f43d951aaaa71..41a6f4adfe038 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1831,22 +1831,36 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // // FIXME #4951: Do we need a node ID here? - let type_parameters = match trait_item.node { - ast::ConstTraitItem(..) => NoTypeParameters, + match trait_item.node { + ast::ConstTraitItem(_, ref default) => { + // Only impose the restrictions of + // ConstRibKind if there's an actual constant + // expression in a provided default. + if default.is_some() { + this.with_constant_rib(|this| { + visit::walk_trait_item(this, trait_item) + }); + } else { + visit::walk_trait_item(this, trait_item) + } + } ast::MethodTraitItem(ref sig, _) => { - HasTypeParameters(&sig.generics, - FnSpace, - MethodRibKind) + let type_parameters = + HasTypeParameters(&sig.generics, + FnSpace, + MethodRibKind); + this.with_type_parameter_rib(type_parameters, |this| { + visit::walk_trait_item(this, trait_item) + }); } ast::TypeTraitItem(..) => { this.check_if_primitive_type_name(trait_item.ident.name, trait_item.span); - NoTypeParameters + this.with_type_parameter_rib(NoTypeParameters, |this| { + visit::walk_trait_item(this, trait_item) + }); } }; - this.with_type_parameter_rib(type_parameters, |this| { - visit::walk_trait_item(this, trait_item) - }); } }); }); @@ -2096,7 +2110,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { this.with_current_self_type(self_type, |this| { for impl_item in impl_items { match impl_item.node { - ConstImplItem(_, _) => {} + ConstImplItem(..) => { + // If this is a trait impl, ensure the method + // exists in trait + this.check_trait_item(impl_item.ident.name, + impl_item.span); + this.with_constant_rib(|this| { + visit::walk_impl_item(this, impl_item); + }); + } MethodImplItem(ref sig, _) => { // If this is a trait impl, ensure the method // exists in trait diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 237270da5628b..dc14ef3696f5f 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -539,25 +539,27 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> { } fn process_const(&mut self, - item: &ast::Item, - typ: &ast::Ty, - expr: &ast::Expr) + id: ast::NodeId, + ident: &ast::Ident, + span: Span, + typ: &ast::Ty, + expr: &ast::Expr) { - let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); + let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(id)); - let sub_span = self.span.sub_span_after_keyword(item.span, + let sub_span = self.span.sub_span_after_keyword(span, keywords::Const); - self.fmt.static_str(item.span, + self.fmt.static_str(span, sub_span, - item.id, - &get_ident(item.ident), + id, + &get_ident((*ident).clone()), &qualname[..], "", &ty_to_string(&*typ), self.cur_scope); // walk type and init value - self.visit_ty(&*typ); + self.visit_ty(typ); self.visit_expr(expr); } @@ -1188,7 +1190,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> { ast::ItemStatic(ref typ, mt, ref expr) => self.process_static(item, &**typ, mt, &**expr), ast::ItemConst(ref typ, ref expr) => - self.process_const(item, &**typ, &**expr), + self.process_const(item.id, &item.ident, item.span, &*typ, &*expr), ast::ItemStruct(ref def, ref ty_params) => self.process_struct(item, &**def, ty_params), ast::ItemEnum(ref def, ref ty_params) => self.process_enum(item, def, ty_params), ast::ItemImpl(_, _, @@ -1238,18 +1240,25 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> { fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) { match trait_item.node { - ast::ConstTraitItem(..) => {} + ast::ConstTraitItem(ref ty, Some(ref expr)) => { + self.process_const(trait_item.id, &trait_item.ident, + trait_item.span, &*ty, &*expr); + } ast::MethodTraitItem(ref sig, ref body) => { self.process_method(sig, body.as_ref().map(|x| &**x), trait_item.id, trait_item.ident.name, trait_item.span); } + ast::ConstTraitItem(_, None) | ast::TypeTraitItem(..) => {} } } fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) { match impl_item.node { - ast::ConstImplItem(..) => {} + ast::ConstImplItem(ref ty, ref expr) => { + self.process_const(impl_item.id, &impl_item.ident, + impl_item.span, &*ty, &*expr); + } ast::MethodImplItem(ref sig, ref body) => { self.process_method(sig, Some(body), impl_item.id, impl_item.ident.name, impl_item.span); diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 38136b03a2141..9932899ed8f0f 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -173,13 +173,11 @@ pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, "cross crate constant could not be inlined"); } - let item = ccx.tcx().map.expect_item(def_id.node); - if let ast::ItemConst(_, ref expr) = item.node { - &**expr - } else { - ccx.sess().span_bug(ref_expr.span, - &format!("get_const_expr given non-constant item {}", - item.repr(ccx.tcx()))); + match const_eval::lookup_const_by_id(ccx.tcx(), def_id, Some(ref_expr.id)) { + Some(ref expr) => expr, + None => { + ccx.sess().span_bug(ref_expr.span, "constant item not found") + } } } @@ -201,7 +199,7 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ast::ExprPath(..) => { let def = ccx.tcx().def_map.borrow().get(&expr.id).unwrap().full_def(); match def { - def::DefConst(def_id) => { + def::DefConst(def_id) | def::DefAssociatedConst(def_id, _) => { if !ccx.tcx().adjustments.borrow().contains_key(&expr.id) { return get_const_val(ccx, def_id, expr); } @@ -774,7 +772,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, def::DefFn(..) | def::DefMethod(..) => { expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val } - def::DefConst(def_id) => { + def::DefConst(def_id) | def::DefAssociatedConst(def_id, _) => { const_deref_ptr(cx, get_const_val(cx, def_id, e)) } def::DefVariant(enum_did, variant_did, _) => { diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 532277d75b2e0..9f0a03878be79 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -401,3 +401,85 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, return true; } } + +pub fn compare_const_impl<'tcx>(tcx: &ty::ctxt<'tcx>, + impl_c: &ty::AssociatedConst<'tcx>, + impl_c_span: Span, + trait_c: &ty::AssociatedConst<'tcx>, + impl_trait_ref: &ty::TraitRef<'tcx>) { + debug!("compare_const_impl(impl_trait_ref={})", + impl_trait_ref.repr(tcx)); + + let infcx = infer::new_infer_ctxt(tcx); + let mut fulfillment_cx = traits::FulfillmentContext::new(); + + // The below is for the most part highly similar to the procedure + // for methods above. It is simpler in many respects, especially + // because we shouldn't really have to deal with lifetimes or + // predicates. In fact some of this should probably be put into + // shared functions because of DRY violations... + let trait_to_impl_substs = &impl_trait_ref.substs; + + // Create a parameter environment that represents the implementation's + // method. + let impl_param_env = + ty::ParameterEnvironment::for_item(tcx, impl_c.def_id.node); + + // Create mapping from impl to skolemized. + let impl_to_skol_substs = &impl_param_env.free_substs; + + // Create mapping from trait to skolemized. + let trait_to_skol_substs = + trait_to_impl_substs + .subst(tcx, impl_to_skol_substs) + .with_method(impl_to_skol_substs.types.get_slice(subst::FnSpace).to_vec(), + impl_to_skol_substs.regions().get_slice(subst::FnSpace).to_vec()); + debug!("compare_const_impl: trait_to_skol_substs={}", + trait_to_skol_substs.repr(tcx)); + + // Compute skolemized form of impl and trait const tys. + let impl_ty = impl_c.ty.subst(tcx, impl_to_skol_substs); + let trait_ty = trait_c.ty.subst(tcx, &trait_to_skol_substs); + + let err = infcx.commit_if_ok(|_| { + let origin = infer::Misc(impl_c_span); + + // There is no "body" here, so just pass dummy id. + let impl_ty = + assoc::normalize_associated_types_in(&infcx, + &impl_param_env, + &mut fulfillment_cx, + impl_c_span, + 0, + &impl_ty); + debug!("compare_const_impl: impl_ty={}", + impl_ty.repr(tcx)); + + let trait_ty = + assoc::normalize_associated_types_in(&infcx, + &impl_param_env, + &mut fulfillment_cx, + impl_c_span, + 0, + &trait_ty); + debug!("compare_const_impl: trait_ty={}", + trait_ty.repr(tcx)); + + infer::mk_subty(&infcx, false, origin, impl_ty, trait_ty) + }); + + match err { + Ok(()) => { } + Err(terr) => { + debug!("checking associated const for compatibility: impl ty {}, trait ty {}", + impl_ty.repr(tcx), + trait_ty.repr(tcx)); + span_err!(tcx.sess, impl_c_span, E0326, + "implemented const `{}` has an incompatible type for \ + trait: {}", + token::get_name(trait_c.name), + ty::type_err_to_str(tcx, &terr)); + return; + } + } +} diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 7eb15a1479634..cf1323e71bd0e 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -109,10 +109,11 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { self.add_obligations(&pick, &all_substs, &method_predicates); // Create the final `MethodCallee`. + let method_ty = pick.item.as_opt_method().unwrap(); let fty = ty::mk_bare_fn(self.tcx(), None, self.tcx().mk_bare_fn(ty::BareFnTy { sig: ty::Binder(method_sig), - unsafety: pick.method_ty.fty.unsafety, - abi: pick.method_ty.fty.abi.clone(), + unsafety: method_ty.fty.unsafety, + abi: method_ty.fty.abi.clone(), })); let callee = MethodCallee { origin: method_origin, @@ -204,7 +205,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { "impl {:?} is not an inherent impl", impl_def_id); let impl_polytype = check::impl_self_ty(self.fcx, self.span, impl_def_id); - (impl_polytype.substs, MethodStatic(pick.method_ty.def_id)) + (impl_polytype.substs, MethodStatic(pick.item.def_id())) } probe::ObjectPick(trait_def_id, method_num, vtable_index) => { @@ -336,7 +337,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { // If they were not explicitly supplied, just construct fresh // variables. let num_supplied_types = supplied_method_types.len(); - let num_method_types = pick.method_ty.generics.types.len(subst::FnSpace); + let num_method_types = pick.item.as_opt_method().unwrap() + .generics.types.len(subst::FnSpace); let method_types = { if num_supplied_types == 0 { self.fcx.infcx().next_ty_vars(num_method_types) @@ -360,7 +362,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { let method_regions = self.fcx.infcx().region_vars_for_defs( self.span, - pick.method_ty.generics.regions.get_slice(subst::FnSpace)); + pick.item.as_opt_method().unwrap() + .generics.regions.get_slice(subst::FnSpace)); (method_types, method_regions) } @@ -397,7 +400,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { // Instantiate the bounds on the method with the // type/early-bound-regions substitutions performed. There can // be no late-bound regions appearing here. - let method_predicates = pick.method_ty.predicates.instantiate(self.tcx(), &all_substs); + let method_predicates = pick.item.as_opt_method().unwrap() + .predicates.instantiate(self.tcx(), &all_substs); let method_predicates = self.fcx.normalize_associated_types_in(self.span, &method_predicates); @@ -410,7 +414,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { // NB: Instantiate late-bound regions first so that // `instantiate_type_scheme` can normalize associated types that // may reference those regions. - let method_sig = self.replace_late_bound_regions_with_fresh_var(&pick.method_ty.fty.sig); + let method_sig = self.replace_late_bound_regions_with_fresh_var( + &pick.item.as_opt_method().unwrap().fty.sig); debug!("late-bound lifetimes from method instantiated, method_sig={}", method_sig.repr(self.tcx())); @@ -616,7 +621,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { fn enforce_illegal_method_limitations(&self, pick: &probe::Pick) { // Disallow calls to the method `drop` defined in the `Drop` trait. - match pick.method_ty.container { + match pick.item.container() { ty::TraitContainer(trait_def_id) => { callee::check_legal_trait_for_method_call(self.fcx.ccx, self.span, trait_def_id) } @@ -625,7 +630,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { // potential calls to it will wind up in the other // arm. But just to be sure, check that the method id // does not appear in the list of destructors. - assert!(!self.tcx().destructors.borrow().contains(&pick.method_ty.def_id)); + assert!(!self.tcx().destructors.borrow().contains(&pick.item.def_id())); } } } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index f1a4dbf7cd528..c5d8e2758ba5c 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -58,7 +58,7 @@ pub enum CandidateSource { TraitSource(/* trait id */ ast::DefId), } -type MethodIndex = usize; // just for doc purposes +type ItemIndex = usize; // just for doc purposes /// Determines whether the type `self_ty` supports a method name `method_name` or not. pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, @@ -312,18 +312,25 @@ pub fn resolve_ufcs<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, { let mode = probe::Mode::Path; let pick = try!(probe::probe(fcx, span, mode, method_name, self_ty, expr_id)); - let def_id = pick.method_ty.def_id; + let def_id = pick.item.def_id(); let mut lp = LastMod(AllPublic); let provenance = match pick.kind { probe::InherentImplPick(impl_def_id) => { - if pick.method_ty.vis != ast::Public { + if pick.item.vis() != ast::Public { lp = LastMod(DependsOn(def_id)); } def::FromImpl(impl_def_id) } - _ => def::FromTrait(pick.method_ty.container.id()) + _ => def::FromTrait(pick.item.container().id()) }; - Ok((def::DefMethod(def_id, provenance), lp)) + let def_result = match pick.item { + ImplOrTraitItem::MethodTraitItem(..) => def::DefMethod(def_id, provenance), + ImplOrTraitItem::ConstTraitItem(..) => def::DefAssociatedConst(def_id, provenance), + ImplOrTraitItem::TypeTraitItem(..) => { + fcx.tcx().sess.span_bug(span, "resolve_ufcs: probe picked associated type"); + } + }; + Ok((def_result, lp)) } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 08e2f47c5a675..7ff1355184b56 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -9,7 +9,7 @@ // except according to those terms. use super::MethodError; -use super::MethodIndex; +use super::ItemIndex; use super::{CandidateSource,ImplSource,TraitSource}; use super::suggest; @@ -37,7 +37,7 @@ struct ProbeContext<'a, 'tcx:'a> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, mode: Mode, - method_name: ast::Name, + item_name: ast::Name, steps: Rc>>, opt_simplified_steps: Option>, inherent_candidates: Vec>, @@ -54,7 +54,7 @@ struct CandidateStep<'tcx> { struct Candidate<'tcx> { xform_self_ty: Ty<'tcx>, - method_ty: Rc>, + item: ty::ImplOrTraitItem<'tcx>, kind: CandidateKind<'tcx>, } @@ -62,14 +62,14 @@ enum CandidateKind<'tcx> { InherentImplCandidate(/* Impl */ ast::DefId, subst::Substs<'tcx>), ObjectCandidate(/* Trait */ ast::DefId, /* method_num */ usize, /* vtable index */ usize), ExtensionImplCandidate(/* Impl */ ast::DefId, Rc>, - subst::Substs<'tcx>, MethodIndex), - ClosureCandidate(/* Trait */ ast::DefId, MethodIndex), - WhereClauseCandidate(ty::PolyTraitRef<'tcx>, MethodIndex), - ProjectionCandidate(ast::DefId, MethodIndex), + subst::Substs<'tcx>, ItemIndex), + ClosureCandidate(/* Trait */ ast::DefId, ItemIndex), + WhereClauseCandidate(ty::PolyTraitRef<'tcx>, ItemIndex), + ProjectionCandidate(ast::DefId, ItemIndex), } pub struct Pick<'tcx> { - pub method_ty: Rc>, + pub item: ty::ImplOrTraitItem<'tcx>, pub kind: PickKind<'tcx>, // Indicates that the source expression should be autoderef'd N times @@ -94,20 +94,20 @@ pub struct Pick<'tcx> { pub enum PickKind<'tcx> { InherentImplPick(/* Impl */ ast::DefId), ObjectPick(/* Trait */ ast::DefId, /* method_num */ usize, /* real_index */ usize), - ExtensionImplPick(/* Impl */ ast::DefId, MethodIndex), - TraitPick(/* Trait */ ast::DefId, MethodIndex), - WhereClausePick(/* Trait */ ty::PolyTraitRef<'tcx>, MethodIndex), + ExtensionImplPick(/* Impl */ ast::DefId, ItemIndex), + TraitPick(/* Trait */ ast::DefId, ItemIndex), + WhereClausePick(/* Trait */ ty::PolyTraitRef<'tcx>, ItemIndex), } pub type PickResult<'tcx> = Result, MethodError>; -#[derive(PartialEq, Eq, Copy, Clone)] +#[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum Mode { // An expression of the form `receiver.method_name(...)`. // Autoderefs are performed on `receiver`, lookup is done based on the // `self` argument of the method, and static methods aren't considered. MethodCall, - // An expression of the form `Type::method` or `::method`. + // An expression of the form `Type::item` or `::item`. // No autoderefs are performed, lookup is done based on the type each // implementation is for, and static methods are included. Path @@ -116,14 +116,14 @@ pub enum Mode { pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, span: Span, mode: Mode, - method_name: ast::Name, + item_name: ast::Name, self_ty: Ty<'tcx>, scope_expr_id: ast::NodeId) -> PickResult<'tcx> { - debug!("probe(self_ty={}, method_name={}, scope_expr_id={})", + debug!("probe(self_ty={}, item_name={}, scope_expr_id={})", self_ty.repr(fcx.tcx()), - method_name, + item_name, scope_expr_id); // FIXME(#18741) -- right now, creating the steps involves evaluating the @@ -171,7 +171,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, let mut probe_cx = ProbeContext::new(fcx, span, mode, - method_name, + item_name, steps, opt_simplified_steps); probe_cx.assemble_inherent_candidates(); @@ -221,7 +221,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { fn new(fcx: &'a FnCtxt<'a,'tcx>, span: Span, mode: Mode, - method_name: ast::Name, + item_name: ast::Name, steps: Vec>, opt_simplified_steps: Option>) -> ProbeContext<'a,'tcx> @@ -230,7 +230,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { fcx: fcx, span: span, mode: mode, - method_name: method_name, + item_name: item_name, inherent_candidates: Vec::new(), extension_candidates: Vec::new(), impl_dups: HashSet::new(), @@ -387,12 +387,12 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { debug!("assemble_inherent_impl_probe {:?}", impl_def_id); - let method = match impl_method(self.tcx(), impl_def_id, self.method_name) { + let item = match impl_item(self.tcx(), impl_def_id, self.item_name) { Some(m) => m, None => { return; } // No method with correct name on this impl }; - if !self.has_applicable_self(&*method) { + if !self.has_applicable_self(&item) { // No receiver declared. Not a candidate. return self.record_static_candidate(ImplSource(impl_def_id)); } @@ -402,11 +402,11 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { // Determine the receiver type that the method itself expects. let xform_self_ty = - self.xform_self_ty(&method, impl_ty, &impl_substs); + self.xform_self_ty(&item, impl_ty, &impl_substs); self.inherent_candidates.push(Candidate { xform_self_ty: xform_self_ty, - method_ty: method, + item: item, kind: InherentImplCandidate(impl_def_id, impl_substs) }); } @@ -427,23 +427,23 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { // itself. Hence, a `&self` method will wind up with an // argument type like `&Trait`. let trait_ref = data.principal_trait_ref_with_self_ty(self.tcx(), self_ty); - self.elaborate_bounds(&[trait_ref.clone()], |this, new_trait_ref, m, method_num| { + self.elaborate_bounds(&[trait_ref.clone()], |this, new_trait_ref, item, item_num| { let new_trait_ref = this.erase_late_bound_regions(&new_trait_ref); let vtable_index = traits::get_vtable_index_of_object_method(tcx, trait_ref.clone(), new_trait_ref.def_id, - method_num); + item_num); - let xform_self_ty = this.xform_self_ty(&m, + let xform_self_ty = this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs); this.inherent_candidates.push(Candidate { xform_self_ty: xform_self_ty, - method_ty: m, - kind: ObjectCandidate(new_trait_ref.def_id, method_num, vtable_index) + item: item, + kind: ObjectCandidate(new_trait_ref.def_id, item_num, vtable_index) }); }); } @@ -476,27 +476,29 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { }) .collect(); - self.elaborate_bounds(&bounds, |this, poly_trait_ref, m, method_num| { + self.elaborate_bounds(&bounds, |this, poly_trait_ref, item, item_num| { let trait_ref = this.erase_late_bound_regions(&poly_trait_ref); let xform_self_ty = - this.xform_self_ty(&m, + this.xform_self_ty(&item, trait_ref.self_ty(), trait_ref.substs); - debug!("found match: trait_ref={} substs={} m={}", - trait_ref.repr(this.tcx()), - trait_ref.substs.repr(this.tcx()), - m.repr(this.tcx())); - assert_eq!(m.generics.types.get_slice(subst::TypeSpace).len(), - trait_ref.substs.types.get_slice(subst::TypeSpace).len()); - assert_eq!(m.generics.regions.get_slice(subst::TypeSpace).len(), - trait_ref.substs.regions().get_slice(subst::TypeSpace).len()); - assert_eq!(m.generics.types.get_slice(subst::SelfSpace).len(), - trait_ref.substs.types.get_slice(subst::SelfSpace).len()); - assert_eq!(m.generics.regions.get_slice(subst::SelfSpace).len(), - trait_ref.substs.regions().get_slice(subst::SelfSpace).len()); + if let Some(ref m) = item.as_opt_method() { + debug!("found match: trait_ref={} substs={} m={}", + trait_ref.repr(this.tcx()), + trait_ref.substs.repr(this.tcx()), + m.repr(this.tcx())); + assert_eq!(m.generics.types.get_slice(subst::TypeSpace).len(), + trait_ref.substs.types.get_slice(subst::TypeSpace).len()); + assert_eq!(m.generics.regions.get_slice(subst::TypeSpace).len(), + trait_ref.substs.regions().get_slice(subst::TypeSpace).len()); + assert_eq!(m.generics.types.get_slice(subst::SelfSpace).len(), + trait_ref.substs.types.get_slice(subst::SelfSpace).len()); + assert_eq!(m.generics.regions.get_slice(subst::SelfSpace).len(), + trait_ref.substs.regions().get_slice(subst::SelfSpace).len()); + } // Because this trait derives from a where-clause, it // should not contain any inference variables or other @@ -507,8 +509,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { this.inherent_candidates.push(Candidate { xform_self_ty: xform_self_ty, - method_ty: m, - kind: WhereClauseCandidate(poly_trait_ref, method_num) + item: item, + kind: WhereClauseCandidate(poly_trait_ref, item_num) }); }); } @@ -523,7 +525,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { F: for<'b> FnMut( &mut ProbeContext<'b, 'tcx>, ty::PolyTraitRef<'tcx>, - Rc>, + ty::ImplOrTraitItem<'tcx>, usize, ), { @@ -531,17 +533,17 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { let tcx = self.tcx(); for bound_trait_ref in traits::transitive_bounds(tcx, bounds) { - let (pos, method) = match trait_method(tcx, - bound_trait_ref.def_id(), - self.method_name) { + let (pos, item) = match trait_item(tcx, + bound_trait_ref.def_id(), + self.item_name) { Some(v) => v, None => { continue; } }; - if !self.has_applicable_self(&*method) { + if !self.has_applicable_self(&item) { self.record_static_candidate(TraitSource(bound_trait_ref.def_id())); } else { - mk_cand(self, bound_trait_ref, method, pos); + mk_cand(self, bound_trait_ref, item, pos); } } } @@ -584,37 +586,34 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { ty::trait_items(self.tcx(), trait_def_id); let matching_index = trait_items.iter() - .position(|item| item.name() == self.method_name); + .position(|item| item.name() == self.item_name); let matching_index = match matching_index { Some(i) => i, None => { return Ok(()); } }; - let method = match (&*trait_items)[matching_index].as_opt_method() { - Some(m) => m, - None => { return Ok(()); } - }; + let ref item = (&*trait_items)[matching_index]; // Check whether `trait_def_id` defines a method with suitable name: - if !self.has_applicable_self(&*method) { + if !self.has_applicable_self(item) { debug!("method has inapplicable self"); self.record_static_candidate(TraitSource(trait_def_id)); return Ok(()); } self.assemble_extension_candidates_for_trait_impls(trait_def_id, - method.clone(), + item.clone(), matching_index); try!(self.assemble_closure_candidates(trait_def_id, - method.clone(), + item.clone(), matching_index)); self.assemble_projection_candidates(trait_def_id, - method.clone(), + item.clone(), matching_index); self.assemble_where_clause_candidates(trait_def_id, - method, + item.clone(), matching_index); Ok(()) @@ -622,8 +621,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { fn assemble_extension_candidates_for_trait_impls(&mut self, trait_def_id: ast::DefId, - method: Rc>, - method_index: usize) + item: ty::ImplOrTraitItem<'tcx>, + item_index: usize) { ty::populate_implementations_for_trait_if_necessary(self.tcx(), trait_def_id); @@ -657,7 +656,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { // Determine the receiver type that the method itself expects. let xform_self_ty = - self.xform_self_ty(&method, + self.xform_self_ty(&item, impl_trait_ref.self_ty(), impl_trait_ref.substs); @@ -665,8 +664,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { self.extension_candidates.push(Candidate { xform_self_ty: xform_self_ty, - method_ty: method.clone(), - kind: ExtensionImplCandidate(impl_def_id, impl_trait_ref, impl_substs, method_index) + item: item.clone(), + kind: ExtensionImplCandidate(impl_def_id, impl_trait_ref, impl_substs, item_index) }); } } @@ -689,8 +688,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { fn assemble_closure_candidates(&mut self, trait_def_id: ast::DefId, - method_ty: Rc>, - method_index: usize) + item: ty::ImplOrTraitItem<'tcx>, + item_index: usize) -> Result<(),MethodError> { // Check if this is one of the Fn,FnMut,FnOnce traits. @@ -736,13 +735,13 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { &trait_def.generics, step.self_ty); - let xform_self_ty = self.xform_self_ty(&method_ty, + let xform_self_ty = self.xform_self_ty(&item, step.self_ty, &substs); self.inherent_candidates.push(Candidate { xform_self_ty: xform_self_ty, - method_ty: method_ty.clone(), - kind: ClosureCandidate(trait_def_id, method_index) + item: item.clone(), + kind: ClosureCandidate(trait_def_id, item_index) }); } @@ -751,16 +750,16 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { fn assemble_projection_candidates(&mut self, trait_def_id: ast::DefId, - method: Rc>, - method_index: usize) + item: ty::ImplOrTraitItem<'tcx>, + item_index: usize) { debug!("assemble_projection_candidates(\ trait_def_id={}, \ - method={}, \ - method_index={})", + item={}, \ + item_index={})", trait_def_id.repr(self.tcx()), - method.repr(self.tcx()), - method_index); + item.repr(self.tcx()), + item_index); for step in &*self.steps { debug!("assemble_projection_candidates: step={}", @@ -792,7 +791,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { bound.repr(self.tcx())); if self.infcx().can_equate(&step.self_ty, &bound.self_ty()).is_ok() { - let xform_self_ty = self.xform_self_ty(&method, + let xform_self_ty = self.xform_self_ty(&item, bound.self_ty(), bound.substs); @@ -802,8 +801,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { self.extension_candidates.push(Candidate { xform_self_ty: xform_self_ty, - method_ty: method.clone(), - kind: ProjectionCandidate(trait_def_id, method_index) + item: item.clone(), + kind: ProjectionCandidate(trait_def_id, item_index) }); } } @@ -812,8 +811,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { fn assemble_where_clause_candidates(&mut self, trait_def_id: ast::DefId, - method_ty: Rc>, - method_index: usize) + item: ty::ImplOrTraitItem<'tcx>, + item_index: usize) { debug!("assemble_where_clause_candidates(trait_def_id={})", trait_def_id.repr(self.tcx())); @@ -824,7 +823,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { .filter(|b| b.def_id() == trait_def_id) { let bound = self.erase_late_bound_regions(&poly_bound); - let xform_self_ty = self.xform_self_ty(&method_ty, + let xform_self_ty = self.xform_self_ty(&item, bound.self_ty(), bound.substs); @@ -834,8 +833,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { self.extension_candidates.push(Candidate { xform_self_ty: xform_self_ty, - method_ty: method_ty.clone(), - kind: WhereClauseCandidate(poly_bound, method_index) + item: item.clone(), + kind: WhereClauseCandidate(poly_bound, item_index) }); } } @@ -860,7 +859,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { try!(self.assemble_extension_candidates_for_all_traits()); let out_of_scope_traits = match self.pick_core() { - Some(Ok(p)) => vec![p.method_ty.container.id()], + Some(Ok(p)) => vec![p.item.container().id()], Some(Err(MethodError::Ambiguity(v))) => v.into_iter().map(|source| { match source { TraitSource(id) => id, @@ -1099,11 +1098,11 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { } // If so, just use this trait and call it a day. - let (trait_def_id, method_num) = trait_data; - let method_ty = probes[0].method_ty.clone(); + let (trait_def_id, item_num) = trait_data; + let item = probes[0].item.clone(); Some(Pick { - method_ty: method_ty, - kind: TraitPick(trait_def_id, method_num), + item: item, + kind: TraitPick(trait_def_id, item_num), autoderefs: 0, autoref: None, unsize: None @@ -1117,28 +1116,25 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { self.infcx().sub_types(false, infer::Misc(DUMMY_SP), sub, sup) } - fn has_applicable_self(&self, method: &ty::Method) -> bool { + fn has_applicable_self(&self, item: &ty::ImplOrTraitItem) -> bool { // "fast track" -- check for usage of sugar - match method.explicit_self { - ty::StaticExplicitSelfCategory => { - if self.mode == Mode::Path { - return true; - } - } - ty::ByValueExplicitSelfCategory | - ty::ByReferenceExplicitSelfCategory(..) | - ty::ByBoxExplicitSelfCategory => { - return true; - } + match *item { + ty::ImplOrTraitItem::MethodTraitItem(ref method) => + match method.explicit_self { + ty::StaticExplicitSelfCategory => self.mode == Mode::Path, + ty::ByValueExplicitSelfCategory | + ty::ByReferenceExplicitSelfCategory(..) | + ty::ByBoxExplicitSelfCategory => true, + }, + ty::ImplOrTraitItem::ConstTraitItem(..) => self.mode == Mode::Path, + _ => false, } - // FIXME -- check for types that deref to `Self`, // like `Rc` and so on. // // Note also that the current code will break if this type // includes any of the type parameters defined on the method // -- but this could be overcome. - return false; } fn record_static_candidate(&mut self, source: CandidateSource) { @@ -1146,10 +1142,23 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { } fn xform_self_ty(&self, - method: &Rc>, + item: &ty::ImplOrTraitItem<'tcx>, impl_ty: Ty<'tcx>, substs: &subst::Substs<'tcx>) -> Ty<'tcx> + { + match item.as_opt_method() { + Some(ref method) => self.xform_method_self_ty(method, impl_ty, + substs), + None => impl_ty, + } + } + + fn xform_method_self_ty(&self, + method: &Rc>, + impl_ty: Ty<'tcx>, + substs: &subst::Substs<'tcx>) + -> Ty<'tcx> { debug!("xform_self_ty(impl_ty={}, self_ty={}, substs={})", impl_ty.repr(self.tcx()), @@ -1245,46 +1254,45 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { } } -fn impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, - impl_def_id: ast::DefId, - method_name: ast::Name) - -> Option>> +fn impl_item<'tcx>(tcx: &ty::ctxt<'tcx>, + impl_def_id: ast::DefId, + item_name: ast::Name) + -> Option> { let impl_items = tcx.impl_items.borrow(); let impl_items = impl_items.get(&impl_def_id).unwrap(); impl_items .iter() .map(|&did| ty::impl_or_trait_item(tcx, did.def_id())) - .find(|m| m.name() == method_name) - .and_then(|item| item.as_opt_method()) + .find(|item| item.name() == item_name) } -/// Find method with name `method_name` defined in `trait_def_id` and return it, along with its -/// index (or `None`, if no such method). -fn trait_method<'tcx>(tcx: &ty::ctxt<'tcx>, - trait_def_id: ast::DefId, - method_name: ast::Name) - -> Option<(usize, Rc>)> +/// Find item with name `item_name` defined in `trait_def_id` and return it, +/// along with its index (or `None`, if no such item). +fn trait_item<'tcx>(tcx: &ty::ctxt<'tcx>, + trait_def_id: ast::DefId, + item_name: ast::Name) + -> Option<(usize, ty::ImplOrTraitItem<'tcx>)> { let trait_items = ty::trait_items(tcx, trait_def_id); debug!("trait_method; items: {:?}", trait_items); trait_items .iter() .enumerate() - .find(|&(_, ref item)| item.name() == method_name) - .and_then(|(idx, item)| item.as_opt_method().map(|m| (idx, m))) + .find(|&(_, ref item)| item.name() == item_name) + .map(|(num, ref item)| (num, (*item).clone())) } impl<'tcx> Candidate<'tcx> { fn to_unadjusted_pick(&self) -> Pick<'tcx> { Pick { - method_ty: self.method_ty.clone(), + item: self.item.clone(), kind: match self.kind { InherentImplCandidate(def_id, _) => { InherentImplPick(def_id) } - ObjectCandidate(def_id, method_num, real_index) => { - ObjectPick(def_id, method_num, real_index) + ObjectCandidate(def_id, item_num, real_index) => { + ObjectPick(def_id, item_num, real_index) } ExtensionImplCandidate(def_id, _, _, index) => { ExtensionImplPick(def_id, index) @@ -1323,25 +1331,25 @@ impl<'tcx> Candidate<'tcx> { } } - fn to_trait_data(&self) -> Option<(ast::DefId,MethodIndex)> { + fn to_trait_data(&self) -> Option<(ast::DefId, ItemIndex)> { match self.kind { InherentImplCandidate(..) => { None } - ObjectCandidate(trait_def_id, method_num, _) => { - Some((trait_def_id, method_num)) + ObjectCandidate(trait_def_id, item_num, _) => { + Some((trait_def_id, item_num)) } - ClosureCandidate(trait_def_id, method_num) => { - Some((trait_def_id, method_num)) + ClosureCandidate(trait_def_id, item_num) => { + Some((trait_def_id, item_num)) } - ExtensionImplCandidate(_, ref trait_ref, _, method_num) => { - Some((trait_ref.def_id, method_num)) + ExtensionImplCandidate(_, ref trait_ref, _, item_num) => { + Some((trait_ref.def_id, item_num)) } - WhereClauseCandidate(ref trait_ref, method_num) => { - Some((trait_ref.def_id(), method_num)) + WhereClauseCandidate(ref trait_ref, item_num) => { + Some((trait_ref.def_id(), item_num)) } - ProjectionCandidate(trait_def_id, method_num) => { - Some((trait_def_id, method_num)) + ProjectionCandidate(trait_def_id, item_num) => { + Some((trait_def_id, item_num)) } } } @@ -1392,9 +1400,9 @@ impl<'tcx> Repr<'tcx> for PickKind<'tcx> { impl<'tcx> Repr<'tcx> for Pick<'tcx> { fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { - format!("Pick(method_ty={}, autoderefs={}, + format!("Pick(item={}, autoderefs={}, autoref={}, unsize={}, kind={:?})", - self.method_ty.repr(tcx), + self.item.repr(tcx), self.autoderefs, self.autoref.repr(tcx), self.unsize.repr(tcx), diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 47388e0e5583f..d04205666f2ea 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -78,7 +78,7 @@ type parameter). pub use self::LvaluePreference::*; pub use self::Expectation::*; -pub use self::compare_method::compare_impl_method; +pub use self::compare_method::{compare_impl_method, compare_const_impl}; use self::TupleArgumentsFlag::*; use astconv::{self, ast_region_to_region, ast_ty_to_ty, AstConv, PathParamMode}; @@ -808,7 +808,9 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) { for impl_item in impl_items { match impl_item.node { - ast::ConstImplItem(_, _) => {} + ast::ConstImplItem(_, ref expr) => { + check_const(ccx, impl_item.span, &*expr, impl_item.id) + } ast::MethodImplItem(ref sig, ref body) => { check_method_body(ccx, &impl_pty.generics, sig, body, impl_item.id, impl_item.span); @@ -824,15 +826,15 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) { let trait_def = ty::lookup_trait_def(ccx.tcx, local_def(it.id)); for trait_item in trait_items { match trait_item.node { - ast::ConstTraitItem(_, _) => {} - ast::MethodTraitItem(_, None) => { - // Nothing to do, since required methods don't have - // bodies to check. + ast::ConstTraitItem(_, Some(ref expr)) => { + check_const(ccx, trait_item.span, &*expr, trait_item.id) } ast::MethodTraitItem(ref sig, Some(ref body)) => { check_method_body(ccx, &trait_def.generics, sig, body, trait_item.id, trait_item.span); } + ast::ConstTraitItem(_, None) | + ast::MethodTraitItem(_, None) | ast::TypeTraitItem(..) => { // Nothing to do. } @@ -922,7 +924,48 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, // and compatible with trait signature for impl_item in impl_items { match impl_item.node { - ast::ConstImplItem(_, _) => {} + ast::ConstImplItem(..) => { + let impl_const_def_id = local_def(impl_item.id); + let impl_const_ty = ty::impl_or_trait_item(ccx.tcx, + impl_const_def_id); + + // Find associated const definition. + let opt_associated_const = + trait_items.iter() + .find(|ac| ac.name() == impl_const_ty.name()); + match opt_associated_const { + Some(associated_const) => { + match (associated_const, &impl_const_ty) { + (&ty::ConstTraitItem(ref const_trait), + &ty::ConstTraitItem(ref const_impl)) => { + compare_const_impl(ccx.tcx, + &const_impl, + impl_item.span, + &const_trait, + &*impl_trait_ref); + } + _ => { + span_err!(tcx.sess, impl_item.span, E0323, + "item `{}` is an associated const, \ + which doesn't match its trait `{}`", + token::get_name(impl_const_ty.name()), + impl_trait_ref.repr(tcx)) + } + } + } + None => { + // This is `span_bug` as it should have already been + // caught in resolve. + tcx.sess.span_bug( + impl_item.span, + &format!( + "associated const `{}` is not a member of \ + trait `{}`", + token::get_name(impl_const_ty.name()), + impl_trait_ref.repr(tcx))); + } + } + } ast::MethodImplItem(_, ref body) => { let impl_method_def_id = local_def(impl_item.id); let impl_item_ty = ty::impl_or_trait_item(ccx.tcx, @@ -946,13 +989,11 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, &*impl_trait_ref); } _ => { - // This is span_bug as it should have already been - // caught in resolve. - tcx.sess.span_bug( - impl_item.span, - &format!("item `{}` is of a different kind from its trait `{}`", - token::get_name(impl_item_ty.name()), - impl_trait_ref.repr(tcx))); + span_err!(tcx.sess, impl_item.span, E0324, + "item `{}` is an associated method, \ + which doesn't match its trait `{}`", + token::get_name(impl_item_ty.name()), + impl_trait_ref.repr(tcx)) } } } @@ -982,11 +1023,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, match (associated_type, &typedef_ty) { (&ty::TypeTraitItem(_), &ty::TypeTraitItem(_)) => {} _ => { - // Formerly `span_bug`, but it turns out that - // this is not checked in resolve, so this is - // the first place where we'll notice the - // mismatch. - span_err!(tcx.sess, impl_item.span, E0323, + span_err!(tcx.sess, impl_item.span, E0325, "item `{}` is an associated type, \ which doesn't match its trait `{}`", token::get_name(typedef_ty.name()), @@ -1014,10 +1051,27 @@ 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(); for trait_item in &*trait_items { match *trait_item { - ty::ConstTraitItem(_) => {} + ty::ConstTraitItem(ref associated_const) => { + let is_implemented = impl_items.iter().any(|ii| { + match ii.node { + ast::ConstImplItem(..) => { + ii.ident.name == associated_const.name + } + _ => false, + } + }); + let is_provided = + 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))); + } + } ty::MethodTraitItem(ref trait_method) => { let is_implemented = impl_items.iter().any(|ii| { @@ -4254,7 +4308,7 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // Luckily, we can (at least for now) deduce the intermediate steps // just from the end-point. // - // There are basically three cases to consider: + // There are basically four cases to consider: // // 1. Reference to a *type*, such as a struct or enum: // @@ -4304,6 +4358,16 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // `SomeStruct::`, contains parameters in TypeSpace, and the // final segment, `foo::` contains parameters in fn space. // + // 4. Reference to an *associated const*: + // + // impl AnotherStruct { + // const FOO: B = BAR; + // } + // + // The path in this case will look like + // `a::b::AnotherStruct::::FOO`, so the penultimate segment + // only will have parameters in TypeSpace. + // // The first step then is to categorize the segments appropriately. assert!(!segments.is_empty()); @@ -4355,8 +4419,21 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } } - def::DefAssociatedConst(..) => { - segment_spaces = repeat(None).take(segments.len()).collect(); + def::DefAssociatedConst(_, provenance) => { + match provenance { + def::FromTrait(trait_did) => { + callee::check_legal_trait_for_method_call(fcx.ccx, span, trait_did) + } + def::FromImpl(_) => {} + } + + if segments.len() >= 2 { + segment_spaces = repeat(None).take(segments.len() - 2).collect(); + segment_spaces.push(Some(subst::TypeSpace)); + segment_spaces.push(None); + } else { + segment_spaces = vec![None]; + } } // Other cases. Various nonsense that really shouldn't show up diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index d04e447a4608a..8bb0596753c7b 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -691,11 +691,37 @@ fn convert_field<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } } +fn convert_associated_const<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, + container: ImplOrTraitItemContainer, + ident: ast::Ident, + id: ast::NodeId, + vis: ast::Visibility, + ty: ty::Ty<'tcx>, + default: Option<&ast::Expr>) +{ + ccx.tcx.predicates.borrow_mut().insert(local_def(id), + ty::GenericPredicates::empty()); + + write_ty_to_tcx(ccx.tcx, id, ty); + let default_id = default.map(|expr| local_def(expr.id)); + + let associated_const = Rc::new(ty::AssociatedConst { + name: ident.name, + vis: vis, + def_id: local_def(id), + container: container, + ty: ty, + default: default_id, + }); + ccx.tcx.impl_or_trait_items.borrow_mut() + .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) + container: ImplOrTraitItemContainer, + ident: ast::Ident, + id: ast::NodeId, + vis: ast::Visibility) { let associated_type = Rc::new(ty::AssociatedType { name: ident.name, @@ -828,6 +854,23 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) { it.vis }; + // Convert all the associated consts. + for impl_item in impl_items { + if let ast::ConstImplItem(ref ty, ref expr) = impl_item.node { + let ty = ccx.icx(&ty_predicates) + .to_ty(&ExplicitRscope, &*ty); + tcx.tcache.borrow_mut().insert(local_def(impl_item.id), + TypeScheme { + generics: ty_generics.clone(), + ty: ty, + }); + convert_associated_const(ccx, ImplContainer(local_def(it.id)), + impl_item.ident, impl_item.id, + impl_item.vis.inherit_from(parent_visibility), + ty, Some(&*expr)); + } + } + // Convert all the associated types. for impl_item in impl_items { if let ast::TypeImplItem(ref ty) = impl_item.node { @@ -906,6 +949,25 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) { debug!("convert: trait_bounds={:?}", trait_predicates); + // Convert all the associated types. + for trait_item in trait_items { + match trait_item.node { + ast::ConstTraitItem(ref ty, ref default) => { + let ty = ccx.icx(&trait_predicates) + .to_ty(&ExplicitRscope, ty); + tcx.tcache.borrow_mut().insert(local_def(trait_item.id), + TypeScheme { + generics: trait_def.generics.clone(), + ty: ty, + }); + convert_associated_const(ccx, TraitContainer(local_def(it.id)), + trait_item.ident, trait_item.id, + ast::Public, ty, default.as_ref().map(|d| &**d)); + } + _ => {} + } + }; + // Convert all the associated types. for trait_item in trait_items { match trait_item.node { diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index b17a7f4f3189d..a4814b36fe5b1 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -176,7 +176,10 @@ register_diagnostics! { E0320, // recursive overflow during dropck E0321, // extended coherence rules for defaulted traits violated E0322, // cannot implement Sized explicitly - E0323, // implemented trait where method should have been provided + E0323, // implemented an associated const when another trait item expected + E0324, // implemented a method when another trait item expected + E0325, // implemented an associated type when another trait item expected + E0326, // associated const implemented with different type from trait E0366, // dropck forbid specialization to concrete type or region E0367, // dropck forbid specialization to predicate not in struct/enum E0368, // binary operation `=` cannot be applied to types diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 0bc3da416cb55..0d59577a6d802 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -22,12 +22,13 @@ use rustc::middle::def; use rustc::middle::ty; use rustc::middle::subst; use rustc::middle::stability; +use rustc::middle::const_eval; use core::DocContext; use doctree; use clean; -use super::Clean; +use super::{Clean, ToSource}; /// Attempt to inline the definition of a local node id into this AST. /// @@ -106,7 +107,7 @@ fn try_inline_def(cx: &DocContext, tcx: &ty::ctxt, record_extern_fqn(cx, did, clean::TypeStatic); clean::StaticItem(build_static(cx, tcx, did, mtbl)) } - def::DefConst(did) => { + def::DefConst(did) | def::DefAssociatedConst(did, _) => { record_extern_fqn(cx, did, clean::TypeConst); clean::ConstantItem(build_const(cx, tcx, did)) } @@ -312,7 +313,27 @@ pub fn build_impl(cx: &DocContext, let did = did.def_id(); let impl_item = ty::impl_or_trait_item(tcx, did); match impl_item { - ty::ConstTraitItem(_) => { return None } + ty::ConstTraitItem(ref assoc_const) => { + let did = assoc_const.def_id; + let type_scheme = ty::lookup_item_type(tcx, did); + let default = match assoc_const.default { + Some(_) => Some(const_eval::lookup_const_by_id(tcx, did, None) + .unwrap().span.to_src(cx)), + None => None, + }; + Some(clean::Item { + name: Some(assoc_const.name.clean(cx)), + inner: clean::AssociatedConstItem( + type_scheme.ty.clean(cx), + default, + ), + source: clean::Span::empty(), + attrs: vec![], + visibility: None, + stability: stability::lookup(tcx, did).clean(cx), + def_id: did + }) + } ty::MethodTraitItem(method) => { if method.vis != ast::Public && associated_trait.is_none() { return None @@ -444,7 +465,7 @@ fn build_const(cx: &DocContext, tcx: &ty::ctxt, use rustc::middle::const_eval; use syntax::print::pprust; - let expr = const_eval::lookup_const_by_id(tcx, did).unwrap_or_else(|| { + let expr = const_eval::lookup_const_by_id(tcx, did, None).unwrap_or_else(|| { panic!("expected lookup_const_by_id to succeed for {:?}", did); }); debug!("converting constant expr {:?} to snippet", expr); diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 447cf7eab458d..3d1e47345d42f 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1904,6 +1904,17 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, Ok(()) } +fn assoc_const(w: &mut fmt::Formatter, it: &clean::Item, + ty: &clean::Type, default: &Option) + -> fmt::Result { + try!(write!(w, "const {}", it.name.as_ref().unwrap())); + try!(write!(w, ": {}", ty)); + if let Some(ref default) = *default { + try!(write!(w, " = {}", default)); + } + Ok(()) +} + fn assoc_type(w: &mut fmt::Formatter, it: &clean::Item, bounds: &Vec, default: &Option) @@ -1959,7 +1970,9 @@ fn render_assoc_item(w: &mut fmt::Formatter, meth: &clean::Item, method(w, meth, m.unsafety, m.abi, &m.generics, &m.self_, &m.decl, link) } - clean::AssociatedConstItem(_, _) => Ok(()), + clean::AssociatedConstItem(ref ty, ref default) => { + assoc_const(w, meth, ty, default) + } clean::AssociatedTypeItem(ref bounds, ref default) => { assoc_type(w, meth, bounds, default) } @@ -2319,7 +2332,14 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: AssocItemLink, try!(write!(w, "type {} = {}", name, tydef.type_)); try!(write!(w, "\n")); } - clean::AssociatedConstItem(_, _) => {} + clean::AssociatedConstItem(ref ty, ref default) => { + let name = item.name.as_ref().unwrap(); + try!(write!(w, "

", + *name, + shortty(item))); + try!(assoc_const(w, item, ty, default)); + try!(write!(w, "

\n")); + } clean::AssociatedTypeItem(ref bounds, ref default) => { let name = item.name.as_ref().unwrap(); try!(write!(w, "

", diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index dc577f603f621..9a1c963b8eb0b 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -17,8 +17,8 @@ use ast::{Public, Unsafety}; use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue}; use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, BiGt, Block}; use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause}; -use ast::{Crate, CrateConfig, Decl, DeclItem}; -use ast::{DeclLocal, DefaultBlock, DefaultReturn}; +use ast::{ConstImplItem, ConstTraitItem, Crate, CrateConfig}; +use ast::{Decl, DeclItem, DeclLocal, DefaultBlock, DefaultReturn}; use ast::{UnDeref, BiDiv, EMPTY_CTXT, EnumDef, ExplicitSelf}; use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain}; use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprBox}; @@ -1158,6 +1158,20 @@ impl<'a> Parser<'a> { let TyParam {ident, bounds, default, ..} = try!(p.parse_ty_param()); try!(p.expect(&token::Semi)); (ident, TypeTraitItem(bounds, default)) + } else if try!(p.eat_keyword(keywords::Const)) { + let ident = try!(p.parse_ident()); + try!(p.expect(&token::Colon)); + let ty = try!(p.parse_ty_sum()); + let default = if p.check(&token::Eq) { + try!(p.bump()); + let expr = try!(p.parse_expr_nopanic()); + try!(p.commit_expr_expecting(&expr, token::Semi)); + Some(expr) + } else { + try!(p.expect(&token::Semi)); + None + }; + (ident, ConstTraitItem(ty, default)) } else { let style = try!(p.parse_unsafety()); let abi = if try!(p.eat_keyword(keywords::Extern)) { @@ -4313,6 +4327,14 @@ impl<'a> Parser<'a> { let typ = try!(self.parse_ty_sum()); try!(self.expect(&token::Semi)); (name, TypeImplItem(typ)) + } else if try!(self.eat_keyword(keywords::Const)) { + let name = try!(self.parse_ident()); + try!(self.expect(&token::Colon)); + let typ = try!(self.parse_ty_sum()); + try!(self.expect(&token::Eq)); + let expr = try!(self.parse_expr_nopanic()); + try!(self.commit_expr_expecting(&expr, token::Semi)); + (name, ConstImplItem(typ, expr)) } else { let (name, inner_attrs, node) = try!(self.parse_impl_method(vis)); attrs.extend(inner_attrs.into_iter()); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 6fd2a8b181518..4cfb9e4147a95 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -796,6 +796,26 @@ impl<'a> State<'a> { } } + fn print_associated_const(&mut self, + ident: ast::Ident, + ty: &ast::Ty, + default: Option<&ast::Expr>, + vis: ast::Visibility) + -> io::Result<()> + { + try!(word(&mut self.s, &visibility_qualified(vis, ""))); + try!(self.word_space("const")); + try!(self.print_ident(ident)); + try!(self.word_space(":")); + try!(self.print_type(ty)); + if let Some(expr) = default { + try!(space(&mut self.s)); + try!(self.word_space("=")); + try!(self.print_expr(expr)); + } + word(&mut self.s, ";") + } + fn print_associated_type(&mut self, ident: ast::Ident, bounds: Option<&ast::TyParamBounds>, @@ -1269,7 +1289,11 @@ impl<'a> State<'a> { try!(self.maybe_print_comment(ti.span.lo)); try!(self.print_outer_attributes(&ti.attrs)); match ti.node { - ast::ConstTraitItem(_, _) => Ok(()), + ast::ConstTraitItem(ref ty, ref default) => { + try!(self.print_associated_const(ti.ident, &ty, + default.as_ref().map(|expr| &**expr), + ast::Inherited)); + } ast::MethodTraitItem(ref sig, ref body) => { if body.is_some() { try!(self.head("")); @@ -1296,7 +1320,9 @@ impl<'a> State<'a> { try!(self.maybe_print_comment(ii.span.lo)); try!(self.print_outer_attributes(&ii.attrs)); match ii.node { - ast::ConstImplItem(_, _) => Ok(()), + ast::ConstImplItem(ref ty, ref expr) => { + try!(self.print_associated_const(ii.ident, &ty, Some(&expr), ii.vis)); + } ast::MethodImplItem(ref sig, ref body) => { try!(self.head("")); try!(self.print_method_sig(ii.ident, sig, ii.vis)); diff --git a/src/test/compile-fail/associated-const-private-impl.rs b/src/test/compile-fail/associated-const-private-impl.rs new file mode 100644 index 0000000000000..1d74873a5d50b --- /dev/null +++ b/src/test/compile-fail/associated-const-private-impl.rs @@ -0,0 +1,27 @@ +// 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. + +use std::marker::MarkerTrait; + +mod bar1 { + pub use self::bar2::Foo; + mod bar2 { + pub struct Foo; + + impl Foo { + const ID: i32 = 1; + } + } +} + +fn main() { + assert_eq!(1, bar1::Foo::ID); + //~^ERROR associated const `ID` is private +} diff --git a/src/test/compile-fail/impl-type-where-trait-has-method.rs b/src/test/compile-fail/impl-type-where-trait-has-method.rs new file mode 100644 index 0000000000000..eb75b82424d71 --- /dev/null +++ b/src/test/compile-fail/impl-type-where-trait-has-method.rs @@ -0,0 +1,21 @@ +// 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 { + fn bar(&self); +} + +impl Foo for u32 { + //~^ ERROR not all trait items implemented, missing: `bar` + type bar = u64; + //~^ ERROR item `bar` is an associated type, which doesn't match its trait `Foo` +} + +fn main () {} diff --git a/src/test/parse-fail/issue-20711-2.rs b/src/test/parse-fail/issue-20711-2.rs index 2c993b7654e67..be6bd516d6fe4 100644 --- a/src/test/parse-fail/issue-20711-2.rs +++ b/src/test/parse-fail/issue-20711-2.rs @@ -16,6 +16,6 @@ impl Foo { fn foo() {} #[stable(feature = "rust1", since = "1.0.0")] -} //~ ERROR expected one of `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}` +} //~ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}` fn main() {} diff --git a/src/test/parse-fail/issue-20711.rs b/src/test/parse-fail/issue-20711.rs index 8462bd8fd016a..d1d8d3acf9187 100644 --- a/src/test/parse-fail/issue-20711.rs +++ b/src/test/parse-fail/issue-20711.rs @@ -14,6 +14,6 @@ struct Foo; impl Foo { #[stable(feature = "rust1", since = "1.0.0")] -} //~ ERROR expected one of `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}` +} //~ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}` fn main() {} diff --git a/src/test/parse-fail/issue-21153.rs b/src/test/parse-fail/issue-21153.rs index 44d979ba9798b..76a4687f544da 100644 --- a/src/test/parse-fail/issue-21153.rs +++ b/src/test/parse-fail/issue-21153.rs @@ -11,5 +11,5 @@ // compile-flags: -Z parse-only trait MyTrait: Iterator { - Item = T; //~ ERROR expected one of `extern`, `fn`, `type`, or `unsafe`, found `Item` + Item = T; //~ ERROR expected one of `const`, `extern`, `fn`, `type`, or `unsafe`, found `Item` } diff --git a/src/test/parse-fail/removed-syntax-static-fn.rs b/src/test/parse-fail/removed-syntax-static-fn.rs index be1e89dd71799..7b6caad86b6cc 100644 --- a/src/test/parse-fail/removed-syntax-static-fn.rs +++ b/src/test/parse-fail/removed-syntax-static-fn.rs @@ -14,5 +14,5 @@ struct S; impl S { static fn f() {} - //~^ ERROR expected one of `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `static` } +//~^^ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `static` diff --git a/src/test/parse-fail/trait-pub-assoc-const.rs b/src/test/parse-fail/trait-pub-assoc-const.rs new file mode 100644 index 0000000000000..adce0d7bbf4b6 --- /dev/null +++ b/src/test/parse-fail/trait-pub-assoc-const.rs @@ -0,0 +1,16 @@ +// 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 { + pub const Foo: u32; + //~^ ERROR expected one of `const`, `extern`, `fn`, `type`, or `unsafe`, found `pub` +} + +fn main() {} diff --git a/src/test/parse-fail/trait-pub-assoc-ty.rs b/src/test/parse-fail/trait-pub-assoc-ty.rs index 02d76234d4e57..dab6c433aba4c 100644 --- a/src/test/parse-fail/trait-pub-assoc-ty.rs +++ b/src/test/parse-fail/trait-pub-assoc-ty.rs @@ -9,7 +9,8 @@ // except according to those terms. trait Foo { - pub type Foo; //~ ERROR expected one of `extern`, `fn`, `type`, or `unsafe`, found `pub` + pub type Foo; + //~^ ERROR expected one of `const`, `extern`, `fn`, `type`, or `unsafe`, found `pub` } fn main() {} diff --git a/src/test/parse-fail/trait-pub-method.rs b/src/test/parse-fail/trait-pub-method.rs index e76802d2ea0f7..7cb9363830c43 100644 --- a/src/test/parse-fail/trait-pub-method.rs +++ b/src/test/parse-fail/trait-pub-method.rs @@ -9,7 +9,8 @@ // except according to those terms. trait Foo { - pub fn foo(); //~ ERROR expected one of `extern`, `fn`, `type`, or `unsafe`, found `pub` + pub fn foo(); + //~^ ERROR expected one of `const`, `extern`, `fn`, `type`, or `unsafe`, found `pub` } fn main() {} diff --git a/src/test/run-pass/associated-const-inherent-impl.rs b/src/test/run-pass/associated-const-inherent-impl.rs new file mode 100644 index 0000000000000..71f7a925d55d9 --- /dev/null +++ b/src/test/run-pass/associated-const-inherent-impl.rs @@ -0,0 +1,19 @@ +// 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. + +struct Foo; + +impl Foo { + const ID: i32 = 1; +} + +fn main() { + assert_eq!(1, Foo::ID); +} diff --git a/src/test/run-pass/associated-const-overwrite-default.rs b/src/test/run-pass/associated-const-overwrite-default.rs new file mode 100644 index 0000000000000..26ece859e143d --- /dev/null +++ b/src/test/run-pass/associated-const-overwrite-default.rs @@ -0,0 +1,23 @@ +// 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. + +use std::marker::MarkerTrait; + +trait Foo: MarkerTrait { + const ID: i32 = 2; +} + +impl Foo for i32 { + const ID: i32 = 1; +} + +fn main() { + assert_eq!(1, ::ID); +} diff --git a/src/test/run-pass/associated-const-public-impl.rs b/src/test/run-pass/associated-const-public-impl.rs new file mode 100644 index 0000000000000..08676425a514d --- /dev/null +++ b/src/test/run-pass/associated-const-public-impl.rs @@ -0,0 +1,26 @@ +// 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. + +use std::marker::MarkerTrait; + +mod bar1 { + pub use self::bar2::Foo; + mod bar2 { + pub struct Foo; + + impl Foo { + pub const ID: i32 = 1; + } + } +} + +fn main() { + assert_eq!(1, bar1::Foo::ID); +} diff --git a/src/test/run-pass/associated-const-self-type.rs b/src/test/run-pass/associated-const-self-type.rs new file mode 100644 index 0000000000000..b4fb452e02003 --- /dev/null +++ b/src/test/run-pass/associated-const-self-type.rs @@ -0,0 +1,23 @@ +// 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. + +use std::marker::MarkerTrait; + +trait MyInt: MarkerTrait { + const ONE: Self; +} + +impl MyInt for i32 { + const ONE: i32 = 1; +} + +fn main() { + assert_eq!(1, ::ONE); +} diff --git a/src/test/run-pass/associated-const-ufcs-infer-trait.rs b/src/test/run-pass/associated-const-ufcs-infer-trait.rs new file mode 100644 index 0000000000000..21e1159366d5a --- /dev/null +++ b/src/test/run-pass/associated-const-ufcs-infer-trait.rs @@ -0,0 +1,23 @@ +// 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. + +use std::marker::MarkerTrait; + +trait Foo: MarkerTrait { + const ID: i32; +} + +impl Foo for i32 { + const ID: i32 = 1; +} + +fn main() { + assert_eq!(1, ::ID); +} diff --git a/src/test/run-pass/associated-const-use-default.rs b/src/test/run-pass/associated-const-use-default.rs new file mode 100644 index 0000000000000..59c83e267dbe1 --- /dev/null +++ b/src/test/run-pass/associated-const-use-default.rs @@ -0,0 +1,21 @@ +// 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. + +use std::marker::MarkerTrait; + +trait Foo: MarkerTrait { + const ID: i32 = 1; +} + +impl Foo for i32 {} + +fn main() { + assert_eq!(1, ::ID); +} diff --git a/src/test/run-pass/associated-const.rs b/src/test/run-pass/associated-const.rs new file mode 100644 index 0000000000000..5e7cc12cf485b --- /dev/null +++ b/src/test/run-pass/associated-const.rs @@ -0,0 +1,23 @@ +// 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. + +use std::marker::MarkerTrait; + +trait Foo: MarkerTrait { + const ID: i32; +} + +impl Foo for i32 { + const ID: i32 = 1; +} + +fn main() { + assert_eq!(1, ::ID); +}