From b5499775d6bee080b3f46539d59d238de2c1726f Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos Date: Sat, 14 Mar 2015 12:05:00 -0600 Subject: Structural changes for associated constants Introduces new variants and types in syntax::ast, middle::ty, and middle::def. --- src/libsyntax/ast.rs | 2 ++ src/libsyntax/ast_map/blocks.rs | 3 +-- src/libsyntax/ast_map/mod.rs | 8 +++++++- src/libsyntax/fold.rs | 7 +++++++ src/libsyntax/print/pprust.rs | 2 ++ src/libsyntax/visit.rs | 10 ++++++++++ 6 files changed, 29 insertions(+), 3 deletions(-) (limited to 'src/libsyntax') diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 399810cb7f5..3f2e7c765a5 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1236,6 +1236,7 @@ pub struct TraitItem { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum TraitItem_ { + ConstTraitItem(P, Option>), MethodTraitItem(MethodSig, Option>), TypeTraitItem(TyParamBounds, Option>), } @@ -1252,6 +1253,7 @@ pub struct ImplItem { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum ImplItem_ { + ConstImplItem(P, P), MethodImplItem(MethodSig, P), TypeImplItem(P), MacImplItem(Mac), diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs index 1505d1e91b8..36a7f3a9381 100644 --- a/src/libsyntax/ast_map/blocks.rs +++ b/src/libsyntax/ast_map/blocks.rs @@ -222,8 +222,7 @@ impl<'a> FnLikeNode<'a> { ast::MethodImplItem(ref sig, ref body) => { method(ii.id, ii.ident, sig, Some(ii.vis), body, ii.span) } - ast::TypeImplItem(_) | - ast::MacImplItem(_) => { + _ => { panic!("impl method FnLikeNode that is not fn-like") } } diff --git a/src/libsyntax/ast_map/mod.rs b/src/libsyntax/ast_map/mod.rs index 2a9a609ecd1..795391d4009 100644 --- a/src/libsyntax/ast_map/mod.rs +++ b/src/libsyntax/ast_map/mod.rs @@ -940,6 +940,12 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String { } Some(NodeImplItem(ii)) => { match ii.node { + ConstImplItem(..) => { + format!("assoc const {} in {}{}", + token::get_ident(ii.ident), + map.path_to_string(id), + id_str) + } MethodImplItem(..) => { format!("method {} in {}{}", token::get_ident(ii.ident), @@ -959,9 +965,9 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String { } Some(NodeTraitItem(ti)) => { let kind = match ti.node { + ConstTraitItem(..) => "assoc constant", MethodTraitItem(..) => "trait method", TypeTraitItem(..) => "assoc type", -// ConstTraitItem(..) => "assoc constant" }; format!("{} {} in {}{}", diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 8ba36cefc65..22bc3a198e2 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -973,6 +973,10 @@ pub fn noop_fold_trait_item(i: P, folder: &mut T) ident: folder.fold_ident(ident), attrs: fold_attrs(attrs, folder), node: match node { + ConstTraitItem(ty, default) => { + ConstTraitItem(folder.fold_ty(ty), + default.map(|x| folder.fold_expr(x))) + } MethodTraitItem(sig, body) => { MethodTraitItem(noop_fold_method_sig(sig, folder), body.map(|x| folder.fold_block(x))) @@ -994,6 +998,9 @@ pub fn noop_fold_impl_item(i: P, folder: &mut T) attrs: fold_attrs(attrs, folder), vis: vis, node: match node { + ConstImplItem(ty, expr) => { + ConstImplItem(folder.fold_ty(ty), folder.fold_expr(expr)) + } MethodImplItem(sig, body) => { MethodImplItem(noop_fold_method_sig(sig, folder), folder.fold_block(body)) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 5a57e09fcff..6fd2a8b1815 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1269,6 +1269,7 @@ 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::MethodTraitItem(ref sig, ref body) => { if body.is_some() { try!(self.head("")); @@ -1295,6 +1296,7 @@ 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::MethodImplItem(ref sig, ref body) => { try!(self.head("")); try!(self.print_method_sig(ii.ident, sig, ii.vis)); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 4c70fc9f81f..2ab35367625 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -619,6 +619,12 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai visitor.visit_attribute(attr); } match trait_item.node { + ConstTraitItem(ref ty, ref default) => { + visitor.visit_ty(ty); + if let Some(ref expr) = *default { + visitor.visit_expr(expr); + } + } MethodTraitItem(ref sig, None) => { visitor.visit_explicit_self(&sig.explicit_self); visitor.visit_generics(&sig.generics); @@ -641,6 +647,10 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt visitor.visit_attribute(attr); } match impl_item.node { + ConstImplItem(ref ty, ref expr) => { + visitor.visit_ty(ty); + visitor.visit_expr(expr); + } MethodImplItem(ref sig, ref body) => { visitor.visit_fn(FkMethod(impl_item.ident, sig, Some(impl_item.vis)), &sig.decl, body, impl_item.span, impl_item.id); -- cgit 1.4.1-3-g733a5 From 7129e8815e3203ceae5bb85b0faa8e8753e520e3 Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos Date: Sun, 15 Mar 2015 19:35:25 -0600 Subject: Functional changes for associated constants. Cross-crate usage of associated constants is not yet working. --- src/grammar/parser-lalr.y | 23 +- src/librustc/metadata/csearch.rs | 7 + src/librustc/metadata/decoder.rs | 62 ++++- src/librustc/metadata/encoder.rs | 83 ++++++- src/librustc/middle/check_const.rs | 33 ++- src/librustc/middle/check_match.rs | 15 +- src/librustc/middle/check_static_recursion.rs | 112 ++++++--- src/librustc/middle/const_eval.rs | 161 +++++++++++- src/librustc/middle/ty.rs | 49 +++- src/librustc_borrowck/borrowck/mod.rs | 14 ++ src/librustc_driver/pretty.rs | 24 ++ src/librustc_lint/builtin.rs | 21 ++ src/librustc_privacy/lib.rs | 45 +++- src/librustc_resolve/lib.rs | 42 +++- src/librustc_trans/save/mod.rs | 33 ++- src/librustc_trans/trans/consts.rs | 16 +- src/librustc_typeck/check/compare_method.rs | 82 ++++++ src/librustc_typeck/check/method/confirm.rs | 23 +- src/librustc_typeck/check/method/mod.rs | 17 +- src/librustc_typeck/check/method/probe.rs | 276 +++++++++++---------- src/librustc_typeck/check/mod.rs | 123 +++++++-- src/librustc_typeck/collect.rs | 70 +++++- src/librustc_typeck/diagnostics.rs | 5 +- src/librustdoc/clean/inline.rs | 29 ++- src/librustdoc/html/render.rs | 24 +- src/libsyntax/parse/parser.rs | 26 +- src/libsyntax/print/pprust.rs | 30 ++- .../compile-fail/associated-const-private-impl.rs | 27 ++ .../impl-type-where-trait-has-method.rs | 21 ++ src/test/parse-fail/issue-20711-2.rs | 2 +- src/test/parse-fail/issue-20711.rs | 2 +- src/test/parse-fail/issue-21153.rs | 2 +- src/test/parse-fail/removed-syntax-static-fn.rs | 2 +- src/test/parse-fail/trait-pub-assoc-const.rs | 16 ++ src/test/parse-fail/trait-pub-assoc-ty.rs | 3 +- src/test/parse-fail/trait-pub-method.rs | 3 +- .../run-pass/associated-const-inherent-impl.rs | 19 ++ .../run-pass/associated-const-overwrite-default.rs | 23 ++ src/test/run-pass/associated-const-public-impl.rs | 26 ++ src/test/run-pass/associated-const-self-type.rs | 23 ++ .../run-pass/associated-const-ufcs-infer-trait.rs | 23 ++ src/test/run-pass/associated-const-use-default.rs | 21 ++ src/test/run-pass/associated-const.rs | 23 ++ 43 files changed, 1387 insertions(+), 294 deletions(-) create mode 100644 src/test/compile-fail/associated-const-private-impl.rs create mode 100644 src/test/compile-fail/impl-type-where-trait-has-method.rs create mode 100644 src/test/parse-fail/trait-pub-assoc-const.rs create mode 100644 src/test/run-pass/associated-const-inherent-impl.rs create mode 100644 src/test/run-pass/associated-const-overwrite-default.rs create mode 100644 src/test/run-pass/associated-const-public-impl.rs create mode 100644 src/test/run-pass/associated-const-self-type.rs create mode 100644 src/test/run-pass/associated-const-ufcs-infer-trait.rs create mode 100644 src/test/run-pass/associated-const-use-default.rs create mode 100644 src/test/run-pass/associated-const.rs (limited to 'src/libsyntax') diff --git a/src/grammar/parser-lalr.y b/src/grammar/parser-lalr.y index 6c3fd186cd4..27e15bc2f0e 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 d528e38d341..93056d949db 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 8e3f77f949b..00a7fe68f2f 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 5f31e24a063..bcbb350fc34 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 ee9d1e01553..80326229618 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 912854a6d7d..13be6d0cb7d 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 b97978fc03f..a521c4531c9 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 2c6ffb3281f..b5a173a569f 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 3f5f40ec0b5..1b5e31f61d8 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 502321d0759..19cdc194153 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 00c3450ebb9..b8032bda8d0 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 f4761f95505..902e9ffca1f 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 832a33e3fe0..128e29ee76e 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 f43d951aaaa..41a6f4adfe0 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 237270da562..dc14ef3696f 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 38136b03a21..9932899ed8f 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 532277d75b2..9f0a03878be 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 7eb15a14796..cf1323e71bd 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 f1a4dbf7cd5..c5d8e2758ba 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 08e2f47c5a6..7ff1355184b 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 47388e0e558..d04205666f2 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 d04e447a460..8bb0596753c 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 b17a7f4f318..a4814b36fe5 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 0bc3da416cb..0d59577a6d8 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 447cf7eab45..3d1e47345d4 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 dc577f603f6..9a1c963b8eb 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 6fd2a8b1815..4cfb9e4147a 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 00000000000..1d74873a5d5 --- /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 00000000000..eb75b82424d --- /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 2c993b7654e..be6bd516d6f 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 8462bd8fd01..d1d8d3acf91 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 44d979ba979..76a4687f544 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 be1e89dd717..7b6caad86b6 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 00000000000..adce0d7bbf4 --- /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 02d76234d4e..dab6c433aba 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 e76802d2ea0..7cb9363830c 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 00000000000..71f7a925d55 --- /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 00000000000..26ece859e14 --- /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 00000000000..08676425a51 --- /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 00000000000..b4fb452e020 --- /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 00000000000..21e1159366d --- /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 00000000000..59c83e267db --- /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 00000000000..5e7cc12cf48 --- /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); +} -- cgit 1.4.1-3-g733a5 From 29eb550ee6a9fd6961bb00e2680a5735aab95de1 Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos Date: Wed, 25 Mar 2015 10:53:28 -0600 Subject: Get associated consts working in match patterns. --- src/librustc/middle/cfg/construct.rs | 1 + src/librustc/middle/check_match.rs | 10 +- src/librustc/middle/expr_use_visitor.rs | 3 +- src/librustc/middle/mem_categorization.rs | 4 + src/librustc/middle/pat_util.rs | 20 +- src/librustc_lint/builtin.rs | 1 - src/librustc_resolve/lib.rs | 225 +++++++++++++++------ src/librustc_trans/save/mod.rs | 3 +- src/librustc_trans/trans/_match.rs | 3 +- src/librustc_trans/trans/debuginfo.rs | 4 + src/librustc_typeck/check/_match.rs | 87 +++++++- src/librustc_typeck/check/mod.rs | 89 ++++---- src/librustc_typeck/diagnostics.rs | 1 + src/librustdoc/clean/mod.rs | 2 + src/libsyntax/ast.rs | 6 + src/libsyntax/ast_util.rs | 2 +- src/libsyntax/fold.rs | 4 + src/libsyntax/parse/parser.rs | 189 +++++++++-------- src/libsyntax/print/pprust.rs | 3 + src/libsyntax/visit.rs | 4 + src/test/compile-fail/method-path-in-pattern.rs | 35 ++++ .../method-resolvable-path-in-pattern.rs | 26 +++ .../brace-after-qualified-path-in-match.rs | 17 ++ .../paren-after-qualified-path-in-match.rs | 17 ++ .../run-pass/associated-const-match-patterns.rs | 52 +++++ 25 files changed, 610 insertions(+), 198 deletions(-) create mode 100644 src/test/compile-fail/method-path-in-pattern.rs create mode 100644 src/test/compile-fail/method-resolvable-path-in-pattern.rs create mode 100644 src/test/parse-fail/brace-after-qualified-path-in-match.rs create mode 100644 src/test/parse-fail/paren-after-qualified-path-in-match.rs create mode 100644 src/test/run-pass/associated-const-match-patterns.rs (limited to 'src/libsyntax') diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 359a1a486c9..a7950a701f8 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -105,6 +105,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { match pat.node { ast::PatIdent(_, _, None) | ast::PatEnum(_, None) | + ast::PatQPath(..) | ast::PatLit(..) | ast::PatRange(..) | ast::PatWild(_) => { diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 13be6d0cb7d..a5ea3629abc 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -439,7 +439,7 @@ impl<'map> ast_util::IdVisitingOperation for RenamingRecorder<'map> { impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> { fn fold_pat(&mut self, pat: P) -> P { return match pat.node { - ast::PatIdent(..) | ast::PatEnum(..) => { + ast::PatIdent(..) | ast::PatEnum(..) | ast::PatQPath(..) => { let def = self.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()); match def { Some(DefAssociatedConst(did, _)) | @@ -762,6 +762,9 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat, Some(DefVariant(_, id, _)) => vec!(Variant(id)), _ => vec!(Single) }, + ast::PatQPath(..) => + cx.tcx.sess.span_bug(pat.span, "const pattern should've \ + been rewritten"), ast::PatStruct(..) => match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) { Some(DefConst(..)) | Some(DefAssociatedConst(..)) => @@ -891,6 +894,11 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], } } + ast::PatQPath(_, _) => { + cx.tcx.sess.span_bug(pat_span, "const pattern should've \ + been rewritten") + } + ast::PatStruct(_, ref pattern_fields, _) => { // Is this a struct or an enum variant? let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def(); diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 87379bd48f0..d740d24e236 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -1147,7 +1147,8 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { let tcx = typer.tcx(); match pat.node { - ast::PatEnum(_, _) | ast::PatIdent(_, _, None) | ast::PatStruct(..) => { + ast::PatEnum(_, _) | ast::PatQPath(..) | + ast::PatIdent(_, _, None) | ast::PatStruct(..) => { match def_map.get(&pat.id).map(|d| d.full_def()) { None => { // no definition found: pat is not a diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 6db55baf483..587194bafad 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -1299,6 +1299,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { } } + ast::PatQPath(..) => { + // Lone constant: ignore + } + ast::PatIdent(_, _, Some(ref subpat)) => { try!(self.cat_pattern_(cmt, &**subpat, op)); } diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs index 7a0f58947fe..27a30f5cf25 100644 --- a/src/librustc/middle/pat_util.rs +++ b/src/librustc/middle/pat_util.rs @@ -30,7 +30,7 @@ pub fn pat_id_map(dm: &DefMap, pat: &ast::Pat) -> PatIdMap { pub fn pat_is_refutable(dm: &DefMap, pat: &ast::Pat) -> bool { match pat.node { - ast::PatLit(_) | ast::PatRange(_, _) => true, + ast::PatLit(_) | ast::PatRange(_, _) | ast::PatQPath(..) => true, ast::PatEnum(_, _) | ast::PatIdent(_, _, None) | ast::PatStruct(..) => { @@ -60,7 +60,7 @@ pub fn pat_is_variant_or_struct(dm: &DefMap, pat: &ast::Pat) -> bool { pub fn pat_is_const(dm: &DefMap, pat: &ast::Pat) -> bool { match pat.node { - ast::PatIdent(_, _, None) | ast::PatEnum(..) => { + ast::PatIdent(_, _, None) | ast::PatEnum(..) | ast::PatQPath(..) => { match dm.borrow().get(&pat.id).map(|d| d.full_def()) { Some(DefConst(..)) | Some(DefAssociatedConst(..)) => true, _ => false @@ -70,6 +70,22 @@ pub fn pat_is_const(dm: &DefMap, pat: &ast::Pat) -> bool { } } +// Same as above, except that partially-resolved defs cause `false` to be +// returned instead of a panic. +pub fn pat_is_resolved_const(dm: &DefMap, pat: &ast::Pat) -> bool { + match pat.node { + ast::PatIdent(_, _, None) | ast::PatEnum(..) | ast::PatQPath(..) => { + match dm.borrow().get(&pat.id) + .and_then(|d| if d.depth == 0 { Some(d.base_def) } + else { None } ) { + Some(DefConst(..)) | Some(DefAssociatedConst(..)) => true, + _ => false + } + } + _ => false + } +} + pub fn pat_is_binding(dm: &DefMap, pat: &ast::Pat) -> bool { match pat.node { ast::PatIdent(..) => { diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 902e9ffca1f..1d5c5fb86cb 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1091,7 +1091,6 @@ impl LintPass for NonUpperCaseGlobals { 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_resolve/lib.rs b/src/librustc_resolve/lib.rs index 41a6f4adfe0..61eab4ce9b2 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -41,6 +41,7 @@ use self::TypeParameters::*; use self::RibKind::*; use self::UseLexicalScopeFlag::*; use self::ModulePrefixResult::*; +use self::AssocItemResolveResult::*; use self::NameSearchType::*; use self::BareIdentifierPatternResolution::*; use self::ParentLink::*; @@ -70,7 +71,7 @@ use syntax::ast::{Ident, ImplItem, Item, ItemConst, ItemEnum, ItemExternCrate}; use syntax::ast::{ItemFn, ItemForeignMod, ItemImpl, ItemMac, ItemMod, ItemStatic, ItemDefaultImpl}; use syntax::ast::{ItemStruct, ItemTrait, ItemTy, ItemUse}; use syntax::ast::{Local, MethodImplItem, Name, NodeId}; -use syntax::ast::{Pat, PatEnum, PatIdent, PatLit}; +use syntax::ast::{Pat, PatEnum, PatIdent, PatLit, PatQPath}; use syntax::ast::{PatRange, PatStruct, Path, PrimTy}; use syntax::ast::{TraitRef, Ty, TyBool, TyChar, TyF32}; use syntax::ast::{TyF64, TyFloat, TyIs, TyI8, TyI16, TyI32, TyI64, TyInt}; @@ -331,6 +332,15 @@ enum ModulePrefixResult { PrefixFound(Rc, usize) } +#[derive(Copy, Clone)] +enum AssocItemResolveResult { + /// Syntax such as `::item`, which can't be resolved until type + /// checking. + TypecheckRequired, + /// We should have been able to resolve the associated item. + ResolveAttempt(Option), +} + #[derive(Copy, Clone, PartialEq)] enum NameSearchType { /// We're doing a name search in order to resolve a `use` directive. @@ -2305,31 +2315,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { fn resolve_type(&mut self, ty: &Ty) { match ty.node { - // `::a::b::c` is resolved by typeck alone. - TyPath(Some(ast::QSelf { position: 0, .. }), _) => {} - TyPath(ref maybe_qself, ref path) => { - let max_assoc_types = if let Some(ref qself) = *maybe_qself { - // Make sure the trait is valid. - let _ = self.resolve_trait_reference(ty.id, path, 1); - path.segments.len() - qself.position - } else { - path.segments.len() - }; - - let mut resolution = None; - for depth in 0..max_assoc_types { - self.with_no_errors(|this| { - resolution = this.resolve_path(ty.id, path, depth, TypeNS, true); - }); - if resolution.is_some() { - break; - } - } - if let Some(DefMod(_)) = resolution.map(|r| r.base_def) { - // A module is not a valid type. - resolution = None; - } + let resolution = + match self.resolve_possibly_assoc_item(ty.id, + maybe_qself.as_ref(), + path, + TypeNS, + true) { + // `::a::b::c` is resolved by typeck alone. + TypecheckRequired => { + // Resolve embedded types. + visit::walk_ty(self, ty); + return; + } + ResolveAttempt(resolution) => resolution, + }; // This is a path in the type namespace. Walk through scopes // looking for it. @@ -2489,10 +2489,24 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { PatEnum(ref path, _) => { // This must be an enum variant, struct or const. - if let Some(path_res) = self.resolve_path(pat_id, path, 0, ValueNS, false) { + let resolution = + match self.resolve_possibly_assoc_item(pat_id, None, + path, ValueNS, + false) { + // The below shouldn't happen because all + // qualified paths should be in PatQPath. + TypecheckRequired => + self.session.span_bug( + path.span, + "resolve_possibly_assoc_item claimed + that a path in PatEnum requires typecheck + to resolve, but qualified paths should be + PatQPath"), + ResolveAttempt(resolution) => resolution, + }; + if let Some(path_res) = resolution { match path_res.base_def { - DefVariant(..) | DefStruct(..) | DefConst(..) | - DefAssociatedConst(..) => { + DefVariant(..) | DefStruct(..) | DefConst(..) => { self.record_def(pattern.id, path_res); } DefStatic(..) => { @@ -2501,19 +2515,70 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { referenced in a pattern, \ use a `const` instead"); } + _ => { + // If anything ends up here entirely resolved, + // it's an error. If anything ends up here + // partially resolved, that's OK, because it may + // be a `T::CONST` that typeck will resolve to + // an inherent impl. + if path_res.depth == 0 { + self.resolve_error( + path.span, + &format!("`{}` is not an enum variant, struct or const", + token::get_ident( + path.segments.last().unwrap().identifier))); + } else { + self.record_def(pattern.id, path_res); + } + } + } + } else { + self.resolve_error(path.span, + &format!("unresolved enum variant, struct or const `{}`", + token::get_ident(path.segments.last().unwrap().identifier))); + } + visit::walk_path(self, path); + } + + PatQPath(ref qself, ref path) => { + // Associated constants only. + let resolution = + match self.resolve_possibly_assoc_item(pat_id, Some(qself), + path, ValueNS, + false) { + TypecheckRequired => { + // All `::CONST` should end up here, and will + // require use of the trait map to resolve + // during typechecking. + let const_name = path.segments.last().unwrap() + .identifier.name; + let traits = self.get_traits_containing_item(const_name); + self.trait_map.insert(pattern.id, traits); + visit::walk_pat(self, pattern); + return true; + } + ResolveAttempt(resolution) => resolution, + }; + if let Some(path_res) = resolution { + match path_res.base_def { + // All `::CONST` should end up here, and + // have the trait already selected. + DefAssociatedConst(..) => { + self.record_def(pattern.id, path_res); + } _ => { self.resolve_error(path.span, - &format!("`{}` is not an enum variant, struct or const", + &format!("`{}` is not an associated const", token::get_ident( path.segments.last().unwrap().identifier))); } } } else { self.resolve_error(path.span, - &format!("unresolved enum variant, struct or const `{}`", + &format!("unresolved associated const `{}`", token::get_ident(path.segments.last().unwrap().identifier))); } - visit::walk_path(self, path); + visit::walk_pat(self, pattern); } PatStruct(ref path, _, _) => { @@ -2605,6 +2670,47 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } + /// Handles paths that may refer to associated items + fn resolve_possibly_assoc_item(&mut self, + id: NodeId, + maybe_qself: Option<&ast::QSelf>, + path: &Path, + namespace: Namespace, + check_ribs: bool) + -> AssocItemResolveResult + { + match maybe_qself { + Some(&ast::QSelf { position: 0, .. }) => + return TypecheckRequired, + _ => {} + } + let max_assoc_types = if let Some(qself) = maybe_qself { + // Make sure the trait is valid. + let _ = self.resolve_trait_reference(id, path, 1); + path.segments.len() - qself.position + } else { + path.segments.len() + }; + + let mut resolution = self.with_no_errors(|this| { + this.resolve_path(id, path, 0, namespace, check_ribs) + }); + for depth in 1..max_assoc_types { + if resolution.is_some() { + break; + } + self.with_no_errors(|this| { + resolution = this.resolve_path(id, path, depth, + TypeNS, true); + }); + } + if let Some(DefMod(_)) = resolution.map(|r| r.base_def) { + // A module is not a valid type or value. + resolution = None; + } + ResolveAttempt(resolution) + } + /// If `check_ribs` is true, checks the local definitions first; i.e. /// doesn't skip straight to the containing module. /// Skips `path_depth` trailing segments, which is also reflected in the @@ -3119,38 +3225,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Next, resolve the node. match expr.node { - // `::a::b::c` is resolved by typeck alone. - ExprPath(Some(ast::QSelf { position: 0, .. }), ref path) => { - let method_name = path.segments.last().unwrap().identifier.name; - let traits = self.search_for_traits_containing_method(method_name); - self.trait_map.insert(expr.id, traits); - visit::walk_expr(self, expr); - } - ExprPath(ref maybe_qself, ref path) => { - let max_assoc_types = if let Some(ref qself) = *maybe_qself { - // Make sure the trait is valid. - let _ = self.resolve_trait_reference(expr.id, path, 1); - path.segments.len() - qself.position - } else { - path.segments.len() - }; - - let mut resolution = self.with_no_errors(|this| { - this.resolve_path(expr.id, path, 0, ValueNS, true) - }); - for depth in 1..max_assoc_types { - if resolution.is_some() { - break; - } - self.with_no_errors(|this| { - resolution = this.resolve_path(expr.id, path, depth, TypeNS, true); - }); - } - if let Some(DefMod(_)) = resolution.map(|r| r.base_def) { - // A module is not a valid type or value. - resolution = None; - } + let resolution = + match self.resolve_possibly_assoc_item(expr.id, + maybe_qself.as_ref(), + path, + ValueNS, + true) { + // `::a::b::c` is resolved by typeck alone. + TypecheckRequired => { + let method_name = path.segments.last().unwrap().identifier.name; + let traits = self.get_traits_containing_item(method_name); + self.trait_map.insert(expr.id, traits); + visit::walk_expr(self, expr); + return; + } + ResolveAttempt(resolution) => resolution, + }; // This is a local path in the value namespace. Walk through // scopes looking for it. @@ -3181,7 +3272,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // so they can be completed during typeck. if path_res.depth != 0 { let method_name = path.segments.last().unwrap().identifier.name; - let traits = self.search_for_traits_containing_method(method_name); + let traits = self.get_traits_containing_item(method_name); self.trait_map.insert(expr.id, traits); } @@ -3339,14 +3430,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // field, we need to add any trait methods we find that match // the field name so that we can do some nice error reporting // later on in typeck. - let traits = self.search_for_traits_containing_method(ident.node.name); + let traits = self.get_traits_containing_item(ident.node.name); self.trait_map.insert(expr.id, traits); } ExprMethodCall(ident, _, _) => { debug!("(recording candidate traits for expr) recording \ traits for {}", expr.id); - let traits = self.search_for_traits_containing_method(ident.node.name); + let traits = self.get_traits_containing_item(ident.node.name); self.trait_map.insert(expr.id, traits); } _ => { @@ -3355,8 +3446,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - fn search_for_traits_containing_method(&mut self, name: Name) -> Vec { - debug!("(searching for traits containing method) looking for '{}'", + fn get_traits_containing_item(&mut self, name: Name) -> Vec { + debug!("(getting traits containing item) looking for '{}'", token::get_name(name)); fn add_trait_info(found_traits: &mut Vec, diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index dc14ef3696f..39cfac42011 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -1044,7 +1044,8 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> { } } } - ast::PatEnum(ref path, _) => { + ast::PatEnum(ref path, _) | + ast::PatQPath(_, ref path) => { self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef)); visit::walk_pat(self, p); } diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 744ec5a6168..b93068c88c8 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -1809,7 +1809,8 @@ fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ast::PatMac(..) => { bcx.sess().span_bug(pat.span, "unexpanded macro"); } - ast::PatWild(_) | ast::PatLit(_) | ast::PatRange(_, _) => () + ast::PatQPath(..) | ast::PatWild(_) | ast::PatLit(_) | + ast::PatRange(_, _) => () } return bcx; } diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs index 69cd57d1bab..516ff443dac 100644 --- a/src/librustc_trans/trans/debuginfo.rs +++ b/src/librustc_trans/trans/debuginfo.rs @@ -3437,6 +3437,10 @@ fn create_scope_map(cx: &CrateContext, } } + ast::PatQPath(..) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + } + ast::PatStruct(_, ref field_pats, _) => { scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 0959f9d1b91..1f4d6cc2fd4 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -11,12 +11,14 @@ use middle::const_eval; use middle::def; use middle::infer; -use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding, pat_is_const}; +use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding}; +use middle::pat_util::pat_is_resolved_const; +use middle::privacy::{AllPublic, LastMod}; use middle::subst::Substs; use middle::ty::{self, Ty}; use check::{check_expr, check_expr_has_type, check_expr_with_expectation}; use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation}; -use check::{instantiate_path, structurally_resolved_type}; +use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type}; use require_same_types; use util::nodemap::FnvHashMap; use util::ppaux::Repr; @@ -118,7 +120,7 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, // subtyping doesn't matter here, as the value is some kind of scalar demand::eqtype(fcx, pat.span, expected, lhs_ty); } - ast::PatEnum(..) | ast::PatIdent(..) if pat_is_const(&tcx.def_map, pat) => { + ast::PatEnum(..) | ast::PatIdent(..) if pat_is_resolved_const(&tcx.def_map, pat) => { let const_did = tcx.def_map.borrow().get(&pat.id).unwrap().def_id(); let const_scheme = ty::lookup_item_type(tcx, const_did); assert!(const_scheme.generics.is_empty()); @@ -181,6 +183,37 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, let subpats = subpats.as_ref().map(|v| &v[..]); check_pat_enum(pcx, pat, path, subpats, expected); } + ast::PatQPath(ref qself, ref path) => { + let self_ty = fcx.to_ty(&qself.ty); + let path_res = if let Some(&d) = tcx.def_map.borrow().get(&pat.id) { + d + } else if qself.position == 0 { + def::PathResolution { + // This is just a sentinel for finish_resolving_def_to_ty. + base_def: def::DefMod(ast_util::local_def(ast::CRATE_NODE_ID)), + last_private: LastMod(AllPublic), + depth: path.segments.len() + } + } else { + tcx.sess.span_bug(pat.span, + &format!("unbound path {}", pat.repr(tcx))) + }; + if let Some((opt_ty, segments, def)) = + resolve_ty_and_def_ufcs(fcx, path_res, Some(self_ty), + path, pat.span, pat.id) { + if check_assoc_item_is_const(pcx, def, pat.span) { + let scheme = ty::lookup_item_type(tcx, def.def_id()); + let predicates = ty::lookup_predicates(tcx, def.def_id()); + instantiate_path(fcx, segments, + scheme, &predicates, + opt_ty, def, pat.span, pat.id); + let const_ty = fcx.node_ty(pat.id); + demand::suptype(fcx, pat.span, expected, const_ty); + } else { + fcx.write_error(pat.id) + } + } + } ast::PatStruct(ref path, ref fields, etc) => { check_pat_struct(pcx, pat, path, fields, etc, expected); } @@ -331,6 +364,21 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, // subtyping. } +fn check_assoc_item_is_const(pcx: &pat_ctxt, def: def::Def, span: Span) -> bool { + match def { + def::DefAssociatedConst(..) => true, + def::DefMethod(..) => { + span_err!(pcx.fcx.ccx.tcx.sess, span, E0327, + "associated items in match patterns must be constants"); + false + } + _ => { + pcx.fcx.ccx.tcx.sess.span_bug(span, "non-associated item in + check_assoc_item_is_const"); + } + } +} + pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, span: Span, expected: Ty<'tcx>, inner: &ast::Pat) -> bool { @@ -532,7 +580,24 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, let fcx = pcx.fcx; let tcx = pcx.fcx.ccx.tcx; - let def = tcx.def_map.borrow().get(&pat.id).unwrap().full_def(); + let path_res = *tcx.def_map.borrow().get(&pat.id).unwrap(); + + let (opt_ty, segments, def) = match resolve_ty_and_def_ufcs(fcx, path_res, + None, path, + pat.span, pat.id) { + Some(resolution) => resolution, + // Error handling done inside resolve_ty_and_def_ufcs, so if + // resolution fails just return. + None => {return;} + }; + + // Items that were partially resolved before should have been resolved to + // associated constants (i.e. not methods). + if path_res.depth != 0 && !check_assoc_item_is_const(pcx, def, pat.span) { + fcx.write_error(pat.id); + return; + } + let enum_def = def.variant_def_ids() .map_or_else(|| def.def_id(), |(enum_def, _)| enum_def); @@ -547,13 +612,23 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, } else { ctor_scheme }; - instantiate_path(pcx.fcx, &path.segments, + instantiate_path(pcx.fcx, segments, path_scheme, &ctor_predicates, - None, def, pat.span, pat.id); + opt_ty, def, pat.span, pat.id); + + // If we didn't have a fully resolved path to start with, we had an + // associated const, and we should quit now, since the rest of this + // function uses checks specific to structs and enums. + if path_res.depth != 0 { + let pat_ty = fcx.node_ty(pat.id); + demand::suptype(fcx, pat.span, expected, pat_ty); + return; + } let pat_ty = fcx.node_ty(pat.id); demand::eqtype(fcx, pat.span, expected, pat_ty); + let real_path_ty = fcx.node_ty(pat.id); let (arg_tys, kind_name): (Vec<_>, &'static str) = match real_path_ty.sty { ty::ty_enum(enum_def_id, expected_substs) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d04205666f2..6ba341f62f5 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3230,53 +3230,20 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, &format!("unbound path {}", expr.repr(tcx))) }; - let def = path_res.base_def; - if path_res.depth == 0 { + if let Some((opt_ty, segments, def)) = + resolve_ty_and_def_ufcs(fcx, path_res, opt_self_ty, path, + expr.span, expr.id) { let (scheme, predicates) = type_scheme_and_predicates_for_def(fcx, expr.span, def); instantiate_path(fcx, - &path.segments, + segments, scheme, &predicates, - opt_self_ty, + opt_ty, def, expr.span, id); - } else { - let ty_segments = path.segments.init(); - let base_ty_end = path.segments.len() - path_res.depth; - let ty = astconv::finish_resolving_def_to_ty(fcx, - fcx, - expr.span, - PathParamMode::Optional, - &def, - opt_self_ty, - &ty_segments[..base_ty_end], - &ty_segments[base_ty_end..]); - let method_segment = path.segments.last().unwrap(); - let method_name = method_segment.identifier.name; - match method::resolve_ufcs(fcx, expr.span, method_name, ty, id) { - Ok((def, lp)) => { - // Write back the new resolution. - tcx.def_map.borrow_mut().insert(id, def::PathResolution { - base_def: def, - last_private: path_res.last_private.or(lp), - depth: 0 - }); - - let (scheme, predicates) = - type_scheme_and_predicates_for_def(fcx, expr.span, def); - instantiate_path(fcx, slice::ref_slice(method_segment), - scheme, &predicates, - Some(ty), def, expr.span, id); - } - Err(error) => { - method::report_error(fcx, expr.span, ty, - method_name, None, error); - fcx.write_error(id); - } - } } // We always require that the type provided as the value for @@ -3738,6 +3705,52 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, unifier(); } +pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>, + path_res: def::PathResolution, + opt_self_ty: Option>, + path: &'a ast::Path, + span: Span, + node_id: ast::NodeId) + -> Option<(Option>, + &'a [ast::PathSegment], + def::Def)> +{ + // If fully resolved already, we don't have to do anything. + if path_res.depth == 0 { + Some((opt_self_ty, &path.segments, path_res.base_def)) + } else { + let mut def = path_res.base_def; + let ty_segments = path.segments.init(); + let base_ty_end = path.segments.len() - path_res.depth; + let ty = astconv::finish_resolving_def_to_ty(fcx, fcx, span, + PathParamMode::Optional, + &mut def, + opt_self_ty, + &ty_segments[..base_ty_end], + &ty_segments[base_ty_end..]); + let item_segment = path.segments.last().unwrap(); + let item_name = item_segment.identifier.name; + match method::resolve_ufcs(fcx, span, item_name, ty, node_id) { + Ok((def, lp)) => { + // Write back the new resolution. + fcx.ccx.tcx.def_map.borrow_mut() + .insert(node_id, def::PathResolution { + base_def: def, + last_private: path_res.last_private.or(lp), + depth: 0 + }); + Some((Some(ty), slice::ref_slice(item_segment), def)) + } + Err(error) => { + method::report_error(fcx, span, ty, + item_name, None, error); + fcx.write_error(node_id); + None + } + } + } +} + fn constrain_path_type_parameters(fcx: &FnCtxt, expr: &ast::Expr) { diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index a4814b36fe5..46cc4628e2e 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -180,6 +180,7 @@ register_diagnostics! { 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 + E0327, // referred to method instead of constant in match pattern 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/mod.rs b/src/librustdoc/clean/mod.rs index 73fbfe29224..1e6e9a7562a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2522,6 +2522,8 @@ fn name_from_pat(p: &ast::Pat) -> String { PatWild(PatWildMulti) => "..".to_string(), PatIdent(_, ref p, _) => token::get_ident(p.node).to_string(), PatEnum(ref p, _) => path_to_string(p), + PatQPath(..) => panic!("tried to get argument name from PatQPath, \ + which is not allowed in function arguments"), PatStruct(ref name, ref fields, etc) => { format!("{} {{ {}{} }}", path_to_string(name), fields.iter().map(|&Spanned { node: ref fp, .. }| diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 3f2e7c765a5..4307abe4174 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -599,6 +599,12 @@ pub enum Pat_ { /// "None" means a * pattern where we don't bind the fields to names. PatEnum(Path, Option>>), + /// An associated const named using the qualified path `::CONST` or + /// `::CONST`. Associated consts from inherent impls can be + /// refered to as simply `T::CONST`, in which case they will end up as + /// PatEnum, and the resolver will have to sort that out. + PatQPath(QSelf, Path), + /// Destructuring of a struct, e.g. `Foo {x, y, ..}` /// The `bool` is `true` in the presence of a `..` PatStruct(Path, Vec>, bool), diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 78f06ce5fd5..fc4d73210ea 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -579,7 +579,7 @@ pub fn walk_pat(pat: &Pat, mut it: F) -> bool where F: FnMut(&Pat) -> bool { } PatMac(_) => panic!("attempted to analyze unexpanded pattern"), PatWild(_) | PatLit(_) | PatRange(_, _) | PatIdent(_, _, _) | - PatEnum(_, _) => { + PatEnum(_, _) | PatQPath(_, _) => { true } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 22bc3a198e2..8898dc7e359 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1134,6 +1134,10 @@ pub fn noop_fold_pat(p: P, folder: &mut T) -> P { PatEnum(folder.fold_path(pth), pats.map(|pats| pats.move_map(|x| folder.fold_pat(x)))) } + PatQPath(qself, pth) => { + let qself = QSelf {ty: folder.fold_ty(qself.ty), .. qself}; + PatQPath(qself, folder.fold_path(pth)) + } PatStruct(pth, fields, etc) => { let pth = folder.fold_path(pth); let fs = fields.move_map(|f| { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 9a1c963b8eb..abeee2a1c74 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -40,8 +40,9 @@ use ast::{MacStmtWithBraces, MacStmtWithSemicolon, MacStmtWithoutBraces}; use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, MatchSource}; use ast::{MutTy, BiMul, Mutability}; use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, UnNot}; -use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatMac, PatRange, PatRegion}; -use ast::{PatStruct, PatTup, PatVec, PatWild, PatWildMulti, PatWildSingle}; +use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatQPath, PatMac, PatRange}; +use ast::{PatRegion, PatStruct, PatTup, PatVec, PatWild, PatWildMulti}; +use ast::PatWildSingle; use ast::{PolyTraitRef, QSelf}; use ast::{Return, BiShl, BiShr, Stmt, StmtDecl}; use ast::{StmtExpr, StmtSemi, StmtMac, StructDef, StructField}; @@ -109,6 +110,15 @@ pub enum PathParsingMode { LifetimeAndTypesWithColons, } +/// How to parse a qualified path, whether to allow trailing parameters. +#[derive(Copy, Clone, PartialEq)] +pub enum QPathParsingMode { + /// No trailing parameters, e.g. `::Item` + NoParameters, + /// Optional parameters, e.g. `::item::<'a, U>` + MaybeParameters, +} + /// How to parse a bound, whether to allow bound modifiers such as `?`. #[derive(Copy, Clone, PartialEq)] pub enum BoundParsingMode { @@ -1345,36 +1355,9 @@ impl<'a> Parser<'a> { try!(self.expect(&token::CloseDelim(token::Paren))); TyTypeof(e) } else if try!(self.eat_lt()) { - // QUALIFIED PATH `::item` - let self_type = try!(self.parse_ty_sum()); - - let mut path = if try!(self.eat_keyword(keywords::As) ){ - try!(self.parse_path(LifetimeAndTypesWithoutColons)) - } else { - ast::Path { - span: self.span, - global: false, - segments: vec![] - } - }; - let qself = QSelf { - ty: self_type, - position: path.segments.len() - }; - - try!(self.expect(&token::Gt)); - try!(self.expect(&token::ModSep)); - - path.segments.push(ast::PathSegment { - identifier: try!(self.parse_ident()), - parameters: ast::PathParameters::none() - }); - - if path.segments.len() == 1 { - path.span.lo = self.last_span.lo; - } - path.span.hi = self.last_span.hi; + let (qself, path) = + try!(self.parse_qualified_path(QPathParsingMode::NoParameters)); TyPath(Some(qself), path) } else if self.check(&token::ModSep) || @@ -1591,6 +1574,61 @@ impl<'a> Parser<'a> { } } + // QUALIFIED PATH `::IDENT[::]` + // Assumes that the leading `<` has been parsed already. + pub fn parse_qualified_path(&mut self, mode: QPathParsingMode) + -> PResult<(QSelf, ast::Path)> { + let self_type = try!(self.parse_ty_sum()); + let mut path = if try!(self.eat_keyword(keywords::As)) { + try!(self.parse_path(LifetimeAndTypesWithoutColons)) + } else { + ast::Path { + span: self.span, + global: false, + segments: vec![] + } + }; + + let qself = QSelf { + ty: self_type, + position: path.segments.len() + }; + + try!(self.expect(&token::Gt)); + try!(self.expect(&token::ModSep)); + + let item_name = try!(self.parse_ident()); + let parameters = match mode { + QPathParsingMode::NoParameters => ast::PathParameters::none(), + QPathParsingMode::MaybeParameters => { + if try!(self.eat(&token::ModSep)) { + try!(self.expect_lt()); + // Consumed `item::<`, go look for types + let (lifetimes, types, bindings) = + try!(self.parse_generic_values_after_lt()); + ast::AngleBracketedParameters(ast::AngleBracketedParameterData { + lifetimes: lifetimes, + types: OwnedSlice::from_vec(types), + bindings: OwnedSlice::from_vec(bindings), + }) + } else { + ast::PathParameters::none() + } + } + }; + path.segments.push(ast::PathSegment { + identifier: item_name, + parameters: parameters + }); + + if path.segments.len() == 1 { + path.span.lo = self.last_span.lo; + } + path.span.hi = self.last_span.hi; + + Ok((qself, path)) + } + /// Parses a path and optional type parameter bounds, depending on the /// mode. The `mode` parameter determines whether lifetimes, types, and/or /// bounds are permitted and whether `::` must precede type parameter @@ -2054,49 +2092,10 @@ impl<'a> Parser<'a> { } _ => { if try!(self.eat_lt()){ - // QUALIFIED PATH `::item::<'a, T>` - let self_type = try!(self.parse_ty_sum()); - let mut path = if try!(self.eat_keyword(keywords::As) ){ - try!(self.parse_path(LifetimeAndTypesWithoutColons)) - } else { - ast::Path { - span: self.span, - global: false, - segments: vec![] - } - }; - let qself = QSelf { - ty: self_type, - position: path.segments.len() - }; - try!(self.expect(&token::Gt)); - try!(self.expect(&token::ModSep)); - - let item_name = try!(self.parse_ident()); - let parameters = if try!(self.eat(&token::ModSep) ){ - try!(self.expect_lt()); - // Consumed `item::<`, go look for types - let (lifetimes, types, bindings) = - try!(self.parse_generic_values_after_lt()); - ast::AngleBracketedParameters(ast::AngleBracketedParameterData { - lifetimes: lifetimes, - types: OwnedSlice::from_vec(types), - bindings: OwnedSlice::from_vec(bindings), - }) - } else { - ast::PathParameters::none() - }; - path.segments.push(ast::PathSegment { - identifier: item_name, - parameters: parameters - }); - if path.segments.len() == 1 { - path.span.lo = self.last_span.lo; - } - path.span.hi = self.last_span.hi; + let (qself, path) = + try!(self.parse_qualified_path(QPathParsingMode::MaybeParameters)); - let hi = self.span.hi; return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path))); } if try!(self.eat_keyword(keywords::Move) ){ @@ -3167,16 +3166,25 @@ impl<'a> Parser<'a> { fn parse_pat_range_end(&mut self) -> PResult> { if self.is_path_start() { let lo = self.span.lo; - let path = try!(self.parse_path(LifetimeAndTypesWithColons)); + let (qself, path) = if try!(self.eat_lt()) { + // Parse a qualified path + let (qself, path) = + try!(self.parse_qualified_path(QPathParsingMode::NoParameters)); + (Some(qself), path) + } else { + // Parse an unqualified path + (None, try!(self.parse_path(LifetimeAndTypesWithColons))) + }; let hi = self.last_span.hi; - Ok(self.mk_expr(lo, hi, ExprPath(None, path))) + Ok(self.mk_expr(lo, hi, ExprPath(qself, path))) } else { self.parse_literal_maybe_minus() } } fn is_path_start(&self) -> bool { - (self.token == token::ModSep || self.token.is_ident() || self.token.is_path()) + (self.token == token::Lt || self.token == token::ModSep + || self.token.is_ident() || self.token.is_path()) && !self.token.is_keyword(keywords::True) && !self.token.is_keyword(keywords::False) } @@ -3252,25 +3260,44 @@ impl<'a> Parser<'a> { pat = try!(self.parse_pat_ident(BindByValue(MutImmutable))); } } else { - // Parse as a general path - let path = try!(self.parse_path(LifetimeAndTypesWithColons)); + let (qself, path) = if try!(self.eat_lt()) { + // Parse a qualified path + let (qself, path) = + try!(self.parse_qualified_path(QPathParsingMode::NoParameters)); + (Some(qself), path) + } else { + // Parse an unqualified path + (None, try!(self.parse_path(LifetimeAndTypesWithColons))) + }; match self.token { token::DotDotDot => { // Parse range let hi = self.last_span.hi; - let begin = self.mk_expr(lo, hi, ExprPath(None, path)); + let begin = self.mk_expr(lo, hi, ExprPath(qself, path)); try!(self.bump()); let end = try!(self.parse_pat_range_end()); pat = PatRange(begin, end); } token::OpenDelim(token::Brace) => { - // Parse struct pattern + if qself.is_some() { + let span = self.span; + self.span_err(span, + "unexpected `{` after qualified path"); + self.abort_if_errors(); + } + // Parse struct pattern try!(self.bump()); let (fields, etc) = try!(self.parse_pat_fields()); try!(self.bump()); pat = PatStruct(path, fields, etc); } token::OpenDelim(token::Paren) => { + if qself.is_some() { + let span = self.span; + self.span_err(span, + "unexpected `(` after qualified path"); + self.abort_if_errors(); + } // Parse tuple struct or enum pattern if self.look_ahead(1, |t| *t == token::DotDot) { // This is a "top constructor only" pat @@ -3287,6 +3314,10 @@ impl<'a> Parser<'a> { pat = PatEnum(path, Some(args)); } } + _ if qself.is_some() => { + // Parse qualified path + pat = PatQPath(qself.unwrap(), path); + } _ => { // Parse nullary enum pat = PatEnum(path, Some(vec![])); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 4cfb9e4147a..27682bc6fec 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2220,6 +2220,9 @@ impl<'a> State<'a> { } } } + ast::PatQPath(ref qself, ref path) => { + try!(self.print_qpath(path, qself, false)); + } ast::PatStruct(ref path, ref fields, etc) => { try!(self.print_path(path, true, 0)); try!(self.nbsp()); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 2ab35367625..6cf791b10be 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -464,6 +464,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) { } } } + PatQPath(ref qself, ref path) => { + visitor.visit_ty(&qself.ty); + visitor.visit_path(path, pattern.id) + } PatStruct(ref path, ref fields, _) => { visitor.visit_path(path, pattern.id); for field in fields { diff --git a/src/test/compile-fail/method-path-in-pattern.rs b/src/test/compile-fail/method-path-in-pattern.rs new file mode 100644 index 00000000000..1d83f901cdb --- /dev/null +++ b/src/test/compile-fail/method-path-in-pattern.rs @@ -0,0 +1,35 @@ +// 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; + +struct Foo; + +impl Foo { + fn bar(&self) {} +} + +trait MyTrait: MarkerTrait { + fn trait_bar() {} +} + +impl MyTrait for Foo {} + +fn main() { + match 0u32 { + Foo::bar => {} //~ ERROR E0327 + } + match 0u32 { + ::bar => {} //~ ERROR E0327 + } + match 0u32 { + ::trait_bar => {} //~ ERROR E0327 + } +} diff --git a/src/test/compile-fail/method-resolvable-path-in-pattern.rs b/src/test/compile-fail/method-resolvable-path-in-pattern.rs new file mode 100644 index 00000000000..f3e93537203 --- /dev/null +++ b/src/test/compile-fail/method-resolvable-path-in-pattern.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; + +struct Foo; + +trait MyTrait: MarkerTrait { + fn trait_bar() {} +} + +impl MyTrait for Foo {} + +fn main() { + match 0u32 { + ::trait_bar => {} + //~^ ERROR `trait_bar` is not an associated const + } +} diff --git a/src/test/parse-fail/brace-after-qualified-path-in-match.rs b/src/test/parse-fail/brace-after-qualified-path-in-match.rs new file mode 100644 index 00000000000..66f462df05a --- /dev/null +++ b/src/test/parse-fail/brace-after-qualified-path-in-match.rs @@ -0,0 +1,17 @@ +// 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. + +fn foo() { + match x { + ::Type{key: value} => (), + //~^ ERROR unexpected `{` after qualified path + _ => (), + } +} diff --git a/src/test/parse-fail/paren-after-qualified-path-in-match.rs b/src/test/parse-fail/paren-after-qualified-path-in-match.rs new file mode 100644 index 00000000000..d06fd2bb4e7 --- /dev/null +++ b/src/test/parse-fail/paren-after-qualified-path-in-match.rs @@ -0,0 +1,17 @@ +// 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. + +fn foo() { + match x { + ::Type(2) => (), + //~^ ERROR unexpected `(` after qualified path + _ => (), + } +} diff --git a/src/test/run-pass/associated-const-match-patterns.rs b/src/test/run-pass/associated-const-match-patterns.rs new file mode 100644 index 00000000000..0085f89822e --- /dev/null +++ b/src/test/run-pass/associated-const-match-patterns.rs @@ -0,0 +1,52 @@ +// 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; + +struct Foo; + +enum Bar { + Var1, + Var2, +} + +// Use inherent and trait impls to test UFCS syntax. +impl Foo { + const MYBAR: Bar = Bar::Var2; +} + +trait HasBar: MarkerTrait { + const THEBAR: Bar; +} + +impl HasBar for Foo { + const THEBAR: Bar = Bar::Var1; +} + +fn main() { + // Inherent impl + assert!(match Bar::Var2 { + Foo::MYBAR => true, + _ => false, + }); + assert!(match Bar::Var2 { + ::MYBAR => true, + _ => false, + }); + // Trait impl + assert!(match Bar::Var1 { + ::THEBAR => true, + _ => false, + }); + assert!(match Bar::Var1 { + ::THEBAR => true, + _ => false, + }); +} -- cgit 1.4.1-3-g733a5 From b1db4ec3d0a96a1e83d74fbc7f99dc3be054f4d8 Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos Date: Thu, 26 Mar 2015 13:06:26 -0600 Subject: Feature-gate associated constants. --- src/doc/reference.md | 5 +++- src/libsyntax/feature_gate.rs | 28 ++++++++++++++++++++++ src/test/auxiliary/associated-const-cc-lib.rs | 2 ++ .../compile-fail/associated-const-dead-code.rs | 1 + .../associated-const-impl-wrong-type.rs | 2 ++ .../compile-fail/associated-const-private-impl.rs | 2 ++ .../associated-const-upper-case-lint.rs | 1 + src/test/compile-fail/gated-associated_consts.rs | 25 +++++++++++++++++++ src/test/compile-fail/impl-wrong-item-for-trait.rs | 2 ++ .../associated-const-cross-crate-defaults.rs | 2 ++ src/test/run-pass/associated-const-cross-crate.rs | 2 ++ .../run-pass/associated-const-in-global-const.rs | 2 ++ .../run-pass/associated-const-inherent-impl.rs | 2 ++ .../run-pass/associated-const-marks-live-code.rs | 2 ++ .../run-pass/associated-const-match-patterns.rs | 2 ++ .../run-pass/associated-const-overwrite-default.rs | 2 ++ src/test/run-pass/associated-const-public-impl.rs | 2 ++ .../run-pass/associated-const-resolution-order.rs | 2 ++ src/test/run-pass/associated-const-self-type.rs | 2 ++ .../run-pass/associated-const-ufcs-infer-trait.rs | 2 ++ src/test/run-pass/associated-const-use-default.rs | 2 ++ .../associated-const-use-impl-of-same-trait.rs | 2 ++ src/test/run-pass/associated-const.rs | 2 ++ 23 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/test/compile-fail/gated-associated_consts.rs (limited to 'src/libsyntax') diff --git a/src/doc/reference.md b/src/doc/reference.md index d918a320e63..2acd491c657 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2340,7 +2340,10 @@ The currently implemented features of the reference compiler are: semantics are likely to change, so this macro usage must be opted into. -* `associated_types` - Allows type aliases in traits. Experimental. +* `associated_consts` - Allows constants to be defined in `impl` and `trait` + blocks, so that they can be associated with a type or + trait in a similar manner to methods and associated + types. * `box_patterns` - Allows `box` patterns, the exact semantics of which is subject to change. diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index d0975c76e93..495bbd412ab 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -155,6 +155,10 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[ // Allows use of unary negate on unsigned integers, e.g. -e for e: u8 ("negate_unsigned", "1.0.0", Active), + + // Allows the definition of associated constants in `trait` or `impl` + // blocks. + ("associated_consts", "1.0.0", Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -659,6 +663,30 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { } visit::walk_fn(self, fn_kind, fn_decl, block, span); } + + fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) { + match ti.node { + ast::ConstTraitItem(..) => { + self.gate_feature("associated_consts", + ti.span, + "associated constants are experimental") + } + _ => {} + } + visit::walk_trait_item(self, ti); + } + + fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) { + match ii.node { + ast::ConstImplItem(..) => { + self.gate_feature("associated_consts", + ii.span, + "associated constants are experimental") + } + _ => {} + } + visit::walk_impl_item(self, ii); + } } fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, diff --git a/src/test/auxiliary/associated-const-cc-lib.rs b/src/test/auxiliary/associated-const-cc-lib.rs index 9735c6cb54d..3508fcd54af 100644 --- a/src/test/auxiliary/associated-const-cc-lib.rs +++ b/src/test/auxiliary/associated-const-cc-lib.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + #![crate_type="lib"] use std::marker::MarkerTrait; diff --git a/src/test/compile-fail/associated-const-dead-code.rs b/src/test/compile-fail/associated-const-dead-code.rs index 42db13f4f3b..1ed156d45f5 100644 --- a/src/test/compile-fail/associated-const-dead-code.rs +++ b/src/test/compile-fail/associated-const-dead-code.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] #![deny(dead_code)] struct MyFoo; diff --git a/src/test/compile-fail/associated-const-impl-wrong-type.rs b/src/test/compile-fail/associated-const-impl-wrong-type.rs index a7b2abc99e5..d76147de3db 100644 --- a/src/test/compile-fail/associated-const-impl-wrong-type.rs +++ b/src/test/compile-fail/associated-const-impl-wrong-type.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + use std::marker::MarkerTrait; trait Foo: MarkerTrait { diff --git a/src/test/compile-fail/associated-const-private-impl.rs b/src/test/compile-fail/associated-const-private-impl.rs index 1d74873a5d5..4dfe7ea78c5 100644 --- a/src/test/compile-fail/associated-const-private-impl.rs +++ b/src/test/compile-fail/associated-const-private-impl.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + use std::marker::MarkerTrait; mod bar1 { diff --git a/src/test/compile-fail/associated-const-upper-case-lint.rs b/src/test/compile-fail/associated-const-upper-case-lint.rs index 497ff426b2f..752691fa1c5 100644 --- a/src/test/compile-fail/associated-const-upper-case-lint.rs +++ b/src/test/compile-fail/associated-const-upper-case-lint.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] #![deny(non_upper_case_globals)] #![allow(dead_code)] diff --git a/src/test/compile-fail/gated-associated_consts.rs b/src/test/compile-fail/gated-associated_consts.rs new file mode 100644 index 00000000000..d508016357c --- /dev/null +++ b/src/test/compile-fail/gated-associated_consts.rs @@ -0,0 +1,25 @@ +// 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 MyTrait: MarkerTrait { + const C: bool; + //~^ associated constants are experimental + //~| add #![feature(associated_consts)] to the crate attributes to enable +} + +struct Foo; + +impl Foo { + const C: bool = true; + //~^ associated constants are experimental + //~| add #![feature(associated_consts)] to the crate attributes to enable +} diff --git a/src/test/compile-fail/impl-wrong-item-for-trait.rs b/src/test/compile-fail/impl-wrong-item-for-trait.rs index 3757005b91f..9b3e28cbc01 100644 --- a/src/test/compile-fail/impl-wrong-item-for-trait.rs +++ b/src/test/compile-fail/impl-wrong-item-for-trait.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + trait Foo { fn bar(&self); const MY_CONST: u32; diff --git a/src/test/run-pass/associated-const-cross-crate-defaults.rs b/src/test/run-pass/associated-const-cross-crate-defaults.rs index 944466f359d..92d2aae61c8 100644 --- a/src/test/run-pass/associated-const-cross-crate-defaults.rs +++ b/src/test/run-pass/associated-const-cross-crate-defaults.rs @@ -10,6 +10,8 @@ // aux-build:associated-const-cc-lib.rs +#![feature(associated_consts)] + extern crate associated_const_cc_lib as foolib; pub struct LocalFooUseDefault; diff --git a/src/test/run-pass/associated-const-cross-crate.rs b/src/test/run-pass/associated-const-cross-crate.rs index c18cda018d8..73d5dc5df96 100644 --- a/src/test/run-pass/associated-const-cross-crate.rs +++ b/src/test/run-pass/associated-const-cross-crate.rs @@ -10,6 +10,8 @@ // aux-build:associated-const-cc-lib.rs +#![feature(associated_consts)] + extern crate associated_const_cc_lib as foolib; pub struct LocalFoo; diff --git a/src/test/run-pass/associated-const-in-global-const.rs b/src/test/run-pass/associated-const-in-global-const.rs index b9fb067d4fa..e3a1e29d20f 100644 --- a/src/test/run-pass/associated-const-in-global-const.rs +++ b/src/test/run-pass/associated-const-in-global-const.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + struct Foo; impl Foo { diff --git a/src/test/run-pass/associated-const-inherent-impl.rs b/src/test/run-pass/associated-const-inherent-impl.rs index 71f7a925d55..5c9abf982b1 100644 --- a/src/test/run-pass/associated-const-inherent-impl.rs +++ b/src/test/run-pass/associated-const-inherent-impl.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + struct Foo; impl Foo { diff --git a/src/test/run-pass/associated-const-marks-live-code.rs b/src/test/run-pass/associated-const-marks-live-code.rs index e86ff0199a1..ea91a953129 100644 --- a/src/test/run-pass/associated-const-marks-live-code.rs +++ b/src/test/run-pass/associated-const-marks-live-code.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + #![deny(dead_code)] const GLOBAL_BAR: u32 = 1; diff --git a/src/test/run-pass/associated-const-match-patterns.rs b/src/test/run-pass/associated-const-match-patterns.rs index 0085f89822e..63f77b35170 100644 --- a/src/test/run-pass/associated-const-match-patterns.rs +++ b/src/test/run-pass/associated-const-match-patterns.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + use std::marker::MarkerTrait; struct Foo; diff --git a/src/test/run-pass/associated-const-overwrite-default.rs b/src/test/run-pass/associated-const-overwrite-default.rs index 26ece859e14..5134ad35659 100644 --- a/src/test/run-pass/associated-const-overwrite-default.rs +++ b/src/test/run-pass/associated-const-overwrite-default.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + use std::marker::MarkerTrait; trait Foo: MarkerTrait { diff --git a/src/test/run-pass/associated-const-public-impl.rs b/src/test/run-pass/associated-const-public-impl.rs index 08676425a51..686ac19dad1 100644 --- a/src/test/run-pass/associated-const-public-impl.rs +++ b/src/test/run-pass/associated-const-public-impl.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + use std::marker::MarkerTrait; mod bar1 { diff --git a/src/test/run-pass/associated-const-resolution-order.rs b/src/test/run-pass/associated-const-resolution-order.rs index e42dd25022b..ad20c084ff4 100644 --- a/src/test/run-pass/associated-const-resolution-order.rs +++ b/src/test/run-pass/associated-const-resolution-order.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + use std::marker::MarkerTrait; struct MyType; diff --git a/src/test/run-pass/associated-const-self-type.rs b/src/test/run-pass/associated-const-self-type.rs index b4fb452e020..dc8b1307f76 100644 --- a/src/test/run-pass/associated-const-self-type.rs +++ b/src/test/run-pass/associated-const-self-type.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + use std::marker::MarkerTrait; trait MyInt: MarkerTrait { diff --git a/src/test/run-pass/associated-const-ufcs-infer-trait.rs b/src/test/run-pass/associated-const-ufcs-infer-trait.rs index 21e1159366d..4cee76eb5aa 100644 --- a/src/test/run-pass/associated-const-ufcs-infer-trait.rs +++ b/src/test/run-pass/associated-const-ufcs-infer-trait.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + use std::marker::MarkerTrait; trait Foo: MarkerTrait { diff --git a/src/test/run-pass/associated-const-use-default.rs b/src/test/run-pass/associated-const-use-default.rs index 59c83e267db..59df2fc41ed 100644 --- a/src/test/run-pass/associated-const-use-default.rs +++ b/src/test/run-pass/associated-const-use-default.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + use std::marker::MarkerTrait; trait Foo: MarkerTrait { diff --git a/src/test/run-pass/associated-const-use-impl-of-same-trait.rs b/src/test/run-pass/associated-const-use-impl-of-same-trait.rs index 2f95d4275c5..6c9c6767cbd 100644 --- a/src/test/run-pass/associated-const-use-impl-of-same-trait.rs +++ b/src/test/run-pass/associated-const-use-impl-of-same-trait.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + use std::marker::MarkerTrait; // The main purpose of this test is to ensure that different impls of the same diff --git a/src/test/run-pass/associated-const.rs b/src/test/run-pass/associated-const.rs index 5e7cc12cf48..9214467275f 100644 --- a/src/test/run-pass/associated-const.rs +++ b/src/test/run-pass/associated-const.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(associated_consts)] + use std::marker::MarkerTrait; trait Foo: MarkerTrait { -- cgit 1.4.1-3-g733a5