1
Fork 0

Auto merge of #23606 - quantheory:associated_const, r=nikomatsakis

Closes #17841.

The majority of the work should be done, e.g. trait and inherent impls, different forms of UFCS syntax, defaults, and cross-crate usage. It's probably enough to replace the constants in `f32`, `i8`, and so on, or close to good enough.

There is still some significant functionality missing from this commit:

 - ~~Associated consts can't be used in match patterns at all. This is simply because I haven't updated the relevant bits in the parser or `resolve`, but it's *probably* not hard to get working.~~
 - Since you can't select an impl for trait-associated consts until partway through type-checking, there are some problems with code that assumes that you can check constants earlier. Associated consts that are not in inherent impls cause ICEs if you try to use them in array sizes or match ranges. For similar reasons, `check_static_recursion` doesn't check them properly, so the stack goes ka-blooey if you use an associated constant that's recursively defined. That's a bit trickier to solve; I'm not entirely sure what the best approach is yet.
 - Dealing with consts associated with type parameters will raise some new issues (e.g. if you have a `T: Int` type parameter and want to use `<T>::ZERO`). See rust-lang/rfcs#865.
 - ~~Unused associated consts don't seem to trigger the `dead_code` lint when they should. Probably easy to fix.~~

Also, this is the first time I've been spelunking in rustc to such a large extent, so I've probably done some silly things in a couple of places.
This commit is contained in:
bors 2015-04-27 16:45:21 +00:00
commit 857ef6e272
93 changed files with 2804 additions and 727 deletions

View file

@ -2135,7 +2135,10 @@ The currently implemented features of the reference compiler are:
semantics are likely to change, so this macro usage must be opted semantics are likely to change, so this macro usage must be opted
into. 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 * `box_patterns` - Allows `box` patterns, the exact semantics of which
is subject to change. is subject to change.

View file

@ -505,10 +505,20 @@ trait_items
; ;
trait_item trait_item
: trait_type : trait_const
| trait_type
| trait_method | 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 trait_type
: maybe_outer_attrs TYPE ty_param ';' { $$ = mk_node("TypeTraitItem", 2, $1, $3); } : maybe_outer_attrs TYPE ty_param ';' { $$ = mk_node("TypeTraitItem", 2, $1, $3); }
; ;
@ -611,7 +621,16 @@ impl_items
impl_item impl_item
: impl_method : impl_method
| item_macro | 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 item_fn

View file

@ -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) 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<Rc<ty::AssociatedConst<'tcx>>> {
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) pub fn get_type_name_if_impl(cstore: &cstore::CStore, def: ast::DefId)
-> Option<ast::Name> { -> Option<ast::Name> {
let cdata = cstore.get_crate_data(def.krate); let cdata = cstore.get_crate_data(def.krate);

View file

@ -305,7 +305,25 @@ fn item_to_def_like(item: rbml::Doc, did: ast::DefId, cnum: ast::CrateNum)
-> DefLike { -> DefLike {
let fam = item_family(item); let fam = item_family(item);
match fam { 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)), ImmStatic => DlDef(def::DefStatic(did, false)),
MutStatic => DlDef(def::DefStatic(did, true)), MutStatic => DlDef(def::DefStatic(did, true)),
Struct => DlDef(def::DefStruct(did)), 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| { tag_item_impl_item, |doc| {
let def_id = item_def_id(doc, cdata); let def_id = item_def_id(doc, cdata);
match item_sort(doc) { match item_sort(doc) {
Some('C') => impl_items.push(ty::ConstTraitItemId(def_id)),
Some('r') | Some('p') => { Some('r') | Some('p') => {
impl_items.push(ty::MethodTraitItemId(def_id)) impl_items.push(ty::MethodTraitItemId(def_id))
} }
@ -877,6 +896,18 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc<IdentInterner>,
let vis = item_visibility(method_doc); let vis = item_visibility(method_doc);
match item_sort(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') => { Some('r') | Some('p') => {
let generics = doc_generics(method_doc, tcx, cdata, tag_method_ty_generics); let generics = doc_generics(method_doc, tcx, cdata, tag_method_ty_generics);
let predicates = doc_predicates(method_doc, tcx, cdata, tag_method_ty_generics); let 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| { reader::tagged_docs(item, tag_item_trait_item, |mth| {
let def_id = item_def_id(mth, cdata); let def_id = item_def_id(mth, cdata);
match item_sort(mth) { match item_sort(mth) {
Some('C') => result.push(ty::ConstTraitItemId(def_id)),
Some('r') | Some('p') => { Some('r') | Some('p') => {
result.push(ty::MethodTraitItemId(def_id)); result.push(ty::MethodTraitItemId(def_id));
} }
@ -951,12 +983,9 @@ pub fn get_provided_trait_methods<'tcx>(intr: Rc<IdentInterner>,
cdata, cdata,
did.node, did.node,
tcx); tcx);
match trait_item { if let ty::MethodTraitItem(ref method) = trait_item {
ty::MethodTraitItem(ref method) => {
result.push((*method).clone()) result.push((*method).clone())
} }
ty::TypeTraitItem(_) => {}
}
} }
true true
}); });
@ -964,6 +993,36 @@ pub fn get_provided_trait_methods<'tcx>(intr: Rc<IdentInterner>,
return result; return result;
} }
pub fn get_associated_consts<'tcx>(intr: Rc<IdentInterner>,
cdata: Cmd,
id: ast::NodeId,
tcx: &ty::ctxt<'tcx>)
-> Vec<Rc<ty::AssociatedConst<'tcx>>> {
let data = cdata.data();
let item = lookup_item(id, data);
let mut result = Vec::new();
for &tag in &[tag_item_trait_item, tag_item_impl_item] {
reader::tagged_docs(item, tag, |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, pub fn get_type_name_if_impl(cdata: Cmd,
node_id: ast::NodeId) -> Option<ast::Name> { node_id: ast::NodeId) -> Option<ast::Name> {
let item = lookup_item(node_id, cdata.data()); let item = lookup_item(node_id, cdata.data());

View file

@ -378,15 +378,12 @@ fn encode_reexported_static_base_methods(ecx: &EncodeContext,
let impl_item = ty::impl_or_trait_item( let impl_item = ty::impl_or_trait_item(
ecx.tcx, ecx.tcx,
method_did.def_id()); method_did.def_id());
match impl_item { if let ty::MethodTraitItem(ref m) = impl_item {
ty::MethodTraitItem(ref m) => {
encode_reexported_static_method(rbml_w, encode_reexported_static_method(rbml_w,
exp, exp,
m.def_id, m.def_id,
m.name); m.name);
} }
ty::TypeTraitItem(_) => {}
}
} }
} }
@ -802,6 +799,43 @@ fn encode_method_ty_fields<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
encode_provided_source(rbml_w, method_ty.provided_source); 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>, fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
rbml_w: &mut Encoder, rbml_w: &mut Encoder,
m: &ty::Method<'tcx>, m: &ty::Method<'tcx>,
@ -1195,6 +1229,10 @@ fn encode_info_for_item(ecx: &EncodeContext,
for &item_def_id in items { for &item_def_id in items {
rbml_w.start_tag(tag_item_impl_item); rbml_w.start_tag(tag_item_impl_item);
match item_def_id { match item_def_id {
ty::ConstTraitItemId(item_def_id) => {
encode_def_id(rbml_w, item_def_id);
encode_item_sort(rbml_w, 'C');
}
ty::MethodTraitItemId(item_def_id) => { ty::MethodTraitItemId(item_def_id) => {
encode_def_id(rbml_w, item_def_id); encode_def_id(rbml_w, item_def_id);
encode_item_sort(rbml_w, 'r'); encode_item_sort(rbml_w, 'r');
@ -1232,6 +1270,14 @@ fn encode_info_for_item(ecx: &EncodeContext,
}); });
match ty::impl_or_trait_item(tcx, trait_item_def_id.def_id()) { match ty::impl_or_trait_item(tcx, trait_item_def_id.def_id()) {
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) => { ty::MethodTraitItem(ref method_type) => {
encode_info_for_method(ecx, encode_info_for_method(ecx,
rbml_w, rbml_w,
@ -1276,6 +1322,10 @@ fn encode_info_for_item(ecx: &EncodeContext,
for &method_def_id in &*ty::trait_item_def_ids(tcx, def_id) { for &method_def_id in &*ty::trait_item_def_ids(tcx, def_id) {
rbml_w.start_tag(tag_item_trait_item); rbml_w.start_tag(tag_item_trait_item);
match method_def_id { match method_def_id {
ty::ConstTraitItemId(const_def_id) => {
encode_def_id(rbml_w, const_def_id);
encode_item_sort(rbml_w, 'C');
}
ty::MethodTraitItemId(method_def_id) => { ty::MethodTraitItemId(method_def_id) => {
encode_def_id(rbml_w, method_def_id); encode_def_id(rbml_w, method_def_id);
encode_item_sort(rbml_w, 'r'); encode_item_sort(rbml_w, 'r');
@ -1321,6 +1371,25 @@ fn encode_info_for_item(ecx: &EncodeContext,
ty::impl_or_trait_item(tcx, item_def_id.def_id()); ty::impl_or_trait_item(tcx, item_def_id.def_id());
let is_nonstatic_method; let is_nonstatic_method;
match trait_item_type { match trait_item_type {
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) => { ty::MethodTraitItem(method_ty) => {
let method_def_id = item_def_id.def_id(); let method_def_id = item_def_id.def_id();
@ -1365,6 +1434,10 @@ fn encode_info_for_item(ecx: &EncodeContext,
let trait_item = &*ms[i]; let trait_item = &*ms[i];
encode_attributes(rbml_w, &trait_item.attrs); encode_attributes(rbml_w, &trait_item.attrs);
match trait_item.node { match trait_item.node {
ast::ConstTraitItem(_, _) => {
encode_inlined_item(ecx, rbml_w,
IITraitItemRef(def_id, trait_item));
}
ast::MethodTraitItem(ref sig, ref body) => { ast::MethodTraitItem(ref sig, ref body) => {
// If this is a static method, we've already // If this is a static method, we've already
// encoded this. // encoded this.
@ -1384,9 +1457,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
encode_method_argument_names(rbml_w, &sig.decl); encode_method_argument_names(rbml_w, &sig.decl);
} }
ast::TypeTraitItem(..) => { ast::TypeTraitItem(..) => {}
encode_item_sort(rbml_w, 't');
}
} }
rbml_w.end_tag(); rbml_w.end_tag();

View file

@ -465,6 +465,9 @@ impl tr for def::Def {
def::DefForeignMod(did) => { def::DefForeignMod(did.tr(dcx)) } def::DefForeignMod(did) => { def::DefForeignMod(did.tr(dcx)) }
def::DefStatic(did, m) => { def::DefStatic(did.tr(dcx), m) } def::DefStatic(did, m) => { def::DefStatic(did.tr(dcx), m) }
def::DefConst(did) => { def::DefConst(did.tr(dcx)) } def::DefConst(did) => { def::DefConst(did.tr(dcx)) }
def::DefAssociatedConst(did, p) => {
def::DefAssociatedConst(did.tr(dcx), p.map(|did2| did2.tr(dcx)))
}
def::DefLocal(nid) => { def::DefLocal(dcx.tr_id(nid)) } def::DefLocal(nid) => { def::DefLocal(dcx.tr_id(nid)) }
def::DefVariant(e_did, v_did, is_s) => { def::DefVariant(e_did, v_did, is_s) => {
def::DefVariant(e_did.tr(dcx), v_did.tr(dcx), is_s) def::DefVariant(e_did.tr(dcx), v_did.tr(dcx), is_s)

View file

@ -105,6 +105,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
match pat.node { match pat.node {
ast::PatIdent(_, _, None) | ast::PatIdent(_, _, None) |
ast::PatEnum(_, None) | ast::PatEnum(_, None) |
ast::PatQPath(..) |
ast::PatLit(..) | ast::PatLit(..) |
ast::PatRange(..) | ast::PatRange(..) |
ast::PatWild(_) => { ast::PatWild(_) => {

View file

@ -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, fn visit_fn(&mut self,
fk: visit::FnKind<'v>, fk: visit::FnKind<'v>,
fd: &'v ast::FnDecl, 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) Mode::Var => v.add_qualif(NOT_CONST)
} }
} }
Some(def::DefConst(did)) => { Some(def::DefConst(did)) |
if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, 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); let inner = v.global_expr(Mode::Const, expr);
v.add_qualif(inner); v.add_qualif(inner);
} else { } else {
v.tcx.sess.span_bug(e.span, "DefConst doesn't point \ v.tcx.sess.span_bug(e.span,
to an ItemConst"); "DefConst or DefAssociatedConst \
doesn't point to a constant");
} }
} }
def => { def => {

View file

@ -439,10 +439,11 @@ impl<'map> ast_util::IdVisitingOperation for RenamingRecorder<'map> {
impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> { impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> { fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
return match pat.node { 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()); let def = self.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def());
match 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) => { Some(const_expr) => {
const_expr_to_pat(self.tcx, const_expr, pat.span).map(|new_pat| { 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 { match pat.node {
ast::PatIdent(..) => ast::PatIdent(..) =>
match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) { 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 \ cx.tcx.sess.span_bug(pat.span, "const pattern should've \
been rewritten"), been rewritten"),
Some(DefStruct(_)) => vec!(Single), Some(DefStruct(_)) => vec!(Single),
@ -755,15 +756,18 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat,
}, },
ast::PatEnum(..) => ast::PatEnum(..) =>
match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) { 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 \ cx.tcx.sess.span_bug(pat.span, "const pattern should've \
been rewritten"), been rewritten"),
Some(DefVariant(_, id, _)) => vec!(Variant(id)), Some(DefVariant(_, id, _)) => vec!(Variant(id)),
_ => vec!(Single) _ => vec!(Single)
}, },
ast::PatQPath(..) =>
cx.tcx.sess.span_bug(pat.span, "const pattern should've \
been rewritten"),
ast::PatStruct(..) => ast::PatStruct(..) =>
match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) { 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 \ cx.tcx.sess.span_bug(pat.span, "const pattern should've \
been rewritten"), been rewritten"),
Some(DefVariant(_, id, _)) => vec!(Variant(id)), Some(DefVariant(_, id, _)) => vec!(Variant(id)),
@ -861,7 +865,7 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
ast::PatIdent(_, _, _) => { ast::PatIdent(_, _, _) => {
let opt_def = cx.tcx.def_map.borrow().get(&pat_id).map(|d| d.full_def()); let opt_def = cx.tcx.def_map.borrow().get(&pat_id).map(|d| d.full_def());
match opt_def { match opt_def {
Some(DefConst(..)) => Some(DefConst(..)) | Some(DefAssociatedConst(..)) =>
cx.tcx.sess.span_bug(pat_span, "const pattern should've \ cx.tcx.sess.span_bug(pat_span, "const pattern should've \
been rewritten"), been rewritten"),
Some(DefVariant(_, id, _)) => if *constructor == Variant(id) { Some(DefVariant(_, id, _)) => if *constructor == Variant(id) {
@ -876,7 +880,7 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
ast::PatEnum(_, ref args) => { ast::PatEnum(_, ref args) => {
let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def(); let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def();
match def { match def {
DefConst(..) => DefConst(..) | DefAssociatedConst(..) =>
cx.tcx.sess.span_bug(pat_span, "const pattern should've \ cx.tcx.sess.span_bug(pat_span, "const pattern should've \
been rewritten"), been rewritten"),
DefVariant(_, id, _) if *constructor != Variant(id) => None, DefVariant(_, id, _) if *constructor != Variant(id) => None,
@ -890,11 +894,16 @@ 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, _) => { ast::PatStruct(_, ref pattern_fields, _) => {
// Is this a struct or an enum variant? // Is this a struct or an enum variant?
let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def(); let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def();
let class_id = match def { let class_id = match def {
DefConst(..) => DefConst(..) | DefAssociatedConst(..) =>
cx.tcx.sess.span_bug(pat_span, "const pattern should've \ cx.tcx.sess.span_bug(pat_span, "const pattern should've \
been rewritten"), been rewritten"),
DefVariant(_, variant_id, _) => if *constructor == Variant(variant_id) { DefVariant(_, variant_id, _) => if *constructor == Variant(variant_id) {

View file

@ -12,10 +12,11 @@
// recursively. // recursively.
use session::Session; use session::Session;
use middle::def::{DefStatic, DefConst, DefMap}; use middle::def::{DefStatic, DefConst, DefAssociatedConst, DefMap};
use syntax::ast; use syntax::ast;
use syntax::{ast_util, ast_map}; use syntax::{ast_util, ast_map};
use syntax::codemap::Span;
use syntax::visit::Visitor; use syntax::visit::Visitor;
use syntax::visit; use syntax::visit;
@ -26,8 +27,43 @@ struct CheckCrateVisitor<'a, 'ast: 'a> {
} }
impl<'v, 'a, 'ast> Visitor<'v> for CheckCrateVisitor<'a, 'ast> { impl<'v, 'a, 'ast> Visitor<'v> for CheckCrateVisitor<'a, 'ast> {
fn visit_item(&mut self, i: &ast::Item) { fn visit_item(&mut self, it: &ast::Item) {
check_item(self, i); 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(); 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> { struct CheckItemRecursionVisitor<'a, 'ast: 'a> {
root_it: &'a ast::Item, root_span: &'a Span,
sess: &'a Session, sess: &'a Session,
ast_map: &'a ast_map::Map<'ast>, ast_map: &'a ast_map::Map<'ast>,
def_map: &'a DefMap, def_map: &'a DefMap,
idstack: Vec<ast::NodeId> idstack: Vec<ast::NodeId>
} }
// Make sure a const item doesn't recursively refer to itself impl<'a, 'ast: 'a> CheckItemRecursionVisitor<'a, 'ast> {
// FIXME: Should use the dependency graph when it's available (#1356) fn new(v: &CheckCrateVisitor<'a, 'ast>, span: &'a Span)
pub fn check_item_recursion<'a>(sess: &'a Session, -> CheckItemRecursionVisitor<'a, 'ast> {
ast_map: &'a ast_map::Map, CheckItemRecursionVisitor {
def_map: &'a DefMap, root_span: span,
it: &'a ast::Item) { sess: v.sess,
ast_map: v.ast_map,
let mut visitor = CheckItemRecursionVisitor { def_map: v.def_map,
root_it: it,
sess: sess,
ast_map: ast_map,
def_map: def_map,
idstack: Vec::new() idstack: Vec::new()
}; }
visitor.visit_item(it); }
fn with_item_id_pushed<F>(&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> { impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> {
fn visit_item(&mut self, it: &ast::Item) { fn visit_item(&mut self, it: &ast::Item) {
if self.idstack.iter().any(|x| x == &(it.id)) { self.with_item_id_pushed(it.id, |v| visit::walk_item(v, it));
span_err!(self.sess, self.root_it.span, E0265, "recursive constant");
return;
} }
self.idstack.push(it.id);
visit::walk_item(self, it); fn visit_trait_item(&mut self, ti: &ast::TraitItem) {
self.idstack.pop(); 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) { fn visit_expr(&mut self, e: &ast::Expr) {
@ -96,11 +129,16 @@ impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> {
ast::ExprPath(..) => { ast::ExprPath(..) => {
match self.def_map.borrow().get(&e.id).map(|d| d.base_def) { match self.def_map.borrow().get(&e.id).map(|d| d.base_def) {
Some(DefStatic(def_id, _)) | Some(DefStatic(def_id, _)) |
Some(DefAssociatedConst(def_id, _)) |
Some(DefConst(def_id)) if Some(DefConst(def_id)) if
ast_util::is_local(def_id) => { ast_util::is_local(def_id) => {
match self.ast_map.get(def_id.node) { match self.ast_map.get(def_id.node) {
ast_map::NodeItem(item) => ast_map::NodeItem(item) =>
self.visit_item(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(_) => {}, ast_map::NodeForeignItem(_) => {},
_ => { _ => {
span_err!(self.sess, e.span, E0266, span_err!(self.sess, e.span, E0266,

View file

@ -16,11 +16,12 @@ pub use self::const_val::*;
use self::ErrKind::*; use self::ErrKind::*;
use metadata::csearch; use metadata::csearch;
use middle::{astencode, def}; use middle::{astencode, def, infer, subst, traits};
use middle::pat_util::def_to_path; use middle::pat_util::def_to_path;
use middle::ty::{self, Ty}; use middle::ty::{self, Ty};
use middle::astconv_util::ast_ty_to_prim_ty; use middle::astconv_util::ast_ty_to_prim_ty;
use util::num::ToPrimitive; use util::num::ToPrimitive;
use util::ppaux::Repr;
use syntax::ast::{self, Expr}; use syntax::ast::{self, Expr};
use syntax::codemap::Span; 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> { 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()); let opt_def = tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
match opt_def { match opt_def {
Some(def::DefConst(def_id)) => { Some(def::DefConst(def_id)) |
lookup_const_by_id(tcx, def_id) Some(def::DefAssociatedConst(def_id, _)) => {
lookup_const_by_id(tcx, def_id, Some(e.id))
} }
Some(def::DefVariant(enum_def, variant_def, _)) => { Some(def::DefVariant(enum_def, variant_def, _)) => {
lookup_variant_by_id(tcx, enum_def, variant_def) lookup_variant_by_id(tcx, enum_def, variant_def)
@ -101,14 +103,46 @@ 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) pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
-> Option<&'a Expr> { def_id: ast::DefId,
maybe_ref_id: Option<ast::NodeId>)
-> Option<&'tcx Expr> {
if ast_util::is_local(def_id) { if ast_util::is_local(def_id) {
match tcx.map.find(def_id.node) { match tcx.map.find(def_id.node) {
None => None, None => None,
Some(ast_map::NodeItem(it)) => match it.node { Some(ast_map::NodeItem(it)) => match it.node {
ast::ItemConst(_, ref const_expr) => { ast::ItemConst(_, ref const_expr) => {
Some(&**const_expr) Some(&*const_expr)
}
_ => None
},
Some(ast_map::NodeTraitItem(ti)) => match ti.node {
ast::ConstTraitItem(_, _) => {
match maybe_ref_id {
// If we have a trait item, and we know the expression
// that's the source of the obligation to resolve it,
// `resolve_trait_associated_const` will select an impl
// or the default.
Some(ref_id) => {
let trait_id = ty::trait_of_item(tcx, def_id)
.unwrap();
resolve_trait_associated_const(tcx, ti, trait_id,
ref_id)
}
// Technically, without knowing anything about the
// expression that generates the obligation, we could
// still return the default if there is one. However,
// it's safer to return `None` than to return some value
// that may differ from what you would get from
// correctly selecting an impl.
None => None
}
}
_ => None
},
Some(ast_map::NodeImplItem(ii)) => match ii.node {
ast::ConstImplItem(_, ref expr) => {
Some(&*expr)
} }
_ => None _ => None
}, },
@ -122,16 +156,44 @@ pub fn lookup_const_by_id<'a>(tcx: &'a ty::ctxt, def_id: ast::DefId)
} }
None => {} None => {}
} }
let mut used_ref_id = false;
let expr_id = match csearch::maybe_get_item_ast(tcx, def_id, 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))) { Box::new(|a, b, c, d| astencode::decode_inlined_item(a, b, c, d))) {
csearch::FoundAst::Found(&ast::IIItem(ref item)) => match item.node { csearch::FoundAst::Found(&ast::IIItem(ref item)) => match item.node {
ast::ItemConst(_, ref const_expr) => Some(const_expr.id), ast::ItemConst(_, ref const_expr) => Some(const_expr.id),
_ => None _ => None
}, },
csearch::FoundAst::Found(&ast::IITraitItem(trait_id, ref ti)) => match ti.node {
ast::ConstTraitItem(_, _) => {
used_ref_id = true;
match maybe_ref_id {
// As mentioned in the comments above for in-crate
// constants, we only try to find the expression for
// a trait-associated const if the caller gives us
// the expression that refers to it.
Some(ref_id) => {
resolve_trait_associated_const(tcx, ti, trait_id,
ref_id).map(|e| e.id)
}
None => None
}
}
_ => None
},
csearch::FoundAst::Found(&ast::IIImplItem(_, ref ii)) => match ii.node {
ast::ConstImplItem(_, ref expr) => Some(expr.id),
_ => None
},
_ => None _ => None
}; };
tcx.extern_const_statics.borrow_mut().insert(def_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.unwrap_or(ast::DUMMY_NODE_ID));
}
expr_id.map(|id| tcx.map.expect_expr(id)) expr_id.map(|id| tcx.map.expect_expr(id))
} }
} }
@ -755,7 +817,35 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
_ => (None, None) _ => (None, None)
} }
} else { } 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, _)) => { Some(def::DefVariant(enum_def, variant_def, _)) => {
@ -833,6 +923,71 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
Ok(result) 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, &param_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 { fn cast_const<'tcx>(tcx: &ty::ctxt<'tcx>, val: const_val, ty: Ty) -> CastResult {
macro_rules! convert_val { macro_rules! convert_val {
($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => { ($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => {

View file

@ -72,7 +72,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
fn lookup_and_handle_definition(&mut self, id: &ast::NodeId) { fn lookup_and_handle_definition(&mut self, id: &ast::NodeId) {
self.tcx.def_map.borrow().get(id).map(|def| { self.tcx.def_map.borrow().get(id).map(|def| {
match def.full_def() { match def.full_def() {
def::DefConst(_) => { def::DefConst(_) | def::DefAssociatedConst(..) => {
self.check_def_id(def.def_id()) self.check_def_id(def.def_id())
} }
_ if self.ignore_non_const_paths => (), _ if self.ignore_non_const_paths => (),
@ -114,14 +114,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
let trait_item = ty::trait_item(self.tcx, let trait_item = ty::trait_item(self.tcx,
trait_ref.def_id, trait_ref.def_id,
index); index);
match trait_item { self.check_def_id(trait_item.def_id());
ty::MethodTraitItem(method) => {
self.check_def_id(method.def_id);
}
ty::TypeTraitItem(typedef) => {
self.check_def_id(typedef.def_id);
}
}
} }
} }
} }
@ -353,6 +346,7 @@ impl<'v> Visitor<'v> for LifeSeeder {
ast::ItemTrait(_, _, _, ref trait_items) => { ast::ItemTrait(_, _, _, ref trait_items) => {
for trait_item in trait_items { for trait_item in trait_items {
match trait_item.node { match trait_item.node {
ast::ConstTraitItem(_, Some(_)) |
ast::MethodTraitItem(_, Some(_)) => { ast::MethodTraitItem(_, Some(_)) => {
if has_allow_dead_code_or_lang_attr(&trait_item.attrs) { if has_allow_dead_code_or_lang_attr(&trait_item.attrs) {
self.worklist.push(trait_item.id); self.worklist.push(trait_item.id);
@ -365,6 +359,7 @@ impl<'v> Visitor<'v> for LifeSeeder {
ast::ItemImpl(_, _, _, ref opt_trait, _, ref impl_items) => { ast::ItemImpl(_, _, _, ref opt_trait, _, ref impl_items) => {
for impl_item in impl_items { for impl_item in impl_items {
match impl_item.node { match impl_item.node {
ast::ConstImplItem(..) |
ast::MethodImplItem(..) => { ast::MethodImplItem(..) => {
if opt_trait.is_some() || if opt_trait.is_some() ||
has_allow_dead_code_or_lang_attr(&impl_item.attrs) { has_allow_dead_code_or_lang_attr(&impl_item.attrs) {
@ -406,7 +401,7 @@ fn create_and_seed_worklist(tcx: &ty::ctxt,
None => () None => ()
} }
// Seed implemented trait methods // Seed implemented trait items
let mut life_seeder = LifeSeeder { let mut life_seeder = LifeSeeder {
worklist: worklist worklist: worklist
}; };
@ -487,7 +482,7 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
|ctor| self.live_symbols.contains(&ctor)) { |ctor| self.live_symbols.contains(&ctor)) {
return true; return true;
} }
// If it's a type whose methods are live, then it's live, too. // If it's a type whose items are live, then it's live, too.
// This is done to handle the case where, for example, the static // This is done to handle the case where, for example, the static
// method of a private type is used, but the type itself is never // method of a private type is used, but the type itself is never
// called directly. // called directly.
@ -557,21 +552,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for DeadVisitor<'a, 'tcx> {
visit::walk_foreign_item(self, fi); visit::walk_foreign_item(self, fi);
} }
fn visit_fn(&mut self, fk: visit::FnKind<'v>,
_: &'v ast::FnDecl, block: &'v ast::Block,
span: codemap::Span, id: ast::NodeId) {
// Have to warn method here because methods are not ast::Item
match fk {
visit::FkMethod(name, _, _) => {
if !self.symbol_is_live(id, None) {
self.warn_dead_code(id, span, name.name, "method");
}
}
_ => ()
}
visit::walk_block(self, block);
}
fn visit_struct_field(&mut self, field: &ast::StructField) { fn visit_struct_field(&mut self, field: &ast::StructField) {
if self.should_warn_about_field(&field.node) { if self.should_warn_about_field(&field.node) {
self.warn_dead_code(field.node.id, field.span, self.warn_dead_code(field.node.id, field.span,
@ -581,12 +561,37 @@ impl<'a, 'tcx, 'v> Visitor<'v> for DeadVisitor<'a, 'tcx> {
visit::walk_struct_field(self, field); visit::walk_struct_field(self, field);
} }
// Overwrite so that we don't warn the trait method itself. fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) {
fn visit_trait_item(&mut self, trait_method: &ast::TraitItem) { match impl_item.node {
match trait_method.node { ast::ConstImplItem(_, ref expr) => {
if !self.symbol_is_live(impl_item.id, None) {
self.warn_dead_code(impl_item.id, impl_item.span,
impl_item.ident.name, "associated const");
}
visit::walk_expr(self, expr)
}
ast::MethodImplItem(_, ref body) => {
if !self.symbol_is_live(impl_item.id, None) {
self.warn_dead_code(impl_item.id, impl_item.span,
impl_item.ident.name, "method");
}
visit::walk_block(self, body)
}
ast::TypeImplItem(..) |
ast::MacImplItem(..) => {}
}
}
// Overwrite so that we don't warn the trait item itself.
fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
match trait_item.node {
ast::ConstTraitItem(_, Some(ref expr)) => {
visit::walk_expr(self, expr)
}
ast::MethodTraitItem(_, Some(ref body)) => { ast::MethodTraitItem(_, Some(ref body)) => {
visit::walk_block(self, body) visit::walk_block(self, body)
} }
ast::ConstTraitItem(_, None) |
ast::MethodTraitItem(_, None) | ast::MethodTraitItem(_, None) |
ast::TypeTraitItem(..) => {} ast::TypeTraitItem(..) => {}
} }

View file

@ -28,6 +28,7 @@ pub enum Def {
DefForeignMod(ast::DefId), DefForeignMod(ast::DefId),
DefStatic(ast::DefId, bool /* is_mutbl */), DefStatic(ast::DefId, bool /* is_mutbl */),
DefConst(ast::DefId), DefConst(ast::DefId),
DefAssociatedConst(ast::DefId /* const */, MethodProvenance),
DefLocal(ast::NodeId), DefLocal(ast::NodeId),
DefVariant(ast::DefId /* enum */, ast::DefId /* variant */, bool /* is_structure */), DefVariant(ast::DefId /* enum */, ast::DefId /* variant */, bool /* is_structure */),
DefTy(ast::DefId, bool /* is_enum */), DefTy(ast::DefId, bool /* is_enum */),
@ -140,7 +141,8 @@ impl Def {
DefFn(id, _) | DefMod(id) | DefForeignMod(id) | DefStatic(id, _) | DefFn(id, _) | DefMod(id) | DefForeignMod(id) | DefStatic(id, _) |
DefVariant(_, id, _) | DefTy(id, _) | DefAssociatedTy(_, id) | DefVariant(_, id, _) | DefTy(id, _) | DefAssociatedTy(_, id) |
DefTyParam(_, _, id, _) | DefUse(id) | DefStruct(id) | DefTrait(id) | DefTyParam(_, _, id, _) | DefUse(id) | DefStruct(id) | DefTrait(id) |
DefMethod(id, _) | DefConst(id) | DefSelfTy(Some(id), None)=> { DefMethod(id, _) | DefConst(id) | DefAssociatedConst(id, _) |
DefSelfTy(Some(id), None)=> {
id id
} }
DefLocal(id) | DefLocal(id) |

View file

@ -234,7 +234,7 @@ impl OverloadedCallType {
ty::MethodTraitItem(ref method_descriptor) => { ty::MethodTraitItem(ref method_descriptor) => {
(*method_descriptor).clone() (*method_descriptor).clone()
} }
ty::TypeTraitItem(_) => { _ => {
tcx.sess.bug("overloaded call method wasn't in method map") tcx.sess.bug("overloaded call method wasn't in method map")
} }
}; };
@ -1147,7 +1147,8 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
let tcx = typer.tcx(); let tcx = typer.tcx();
match pat.node { 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()) { match def_map.get(&pat.id).map(|d| d.full_def()) {
None => { None => {
// no definition found: pat is not a // no definition found: pat is not a
@ -1183,6 +1184,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
} }
Some(def::DefConst(..)) | Some(def::DefConst(..)) |
Some(def::DefAssociatedConst(..)) |
Some(def::DefLocal(..)) => { Some(def::DefLocal(..)) => {
// This is a leaf (i.e. identifier binding // This is a leaf (i.e. identifier binding
// or constant value to match); thus no // or constant value to match); thus no

View file

@ -843,8 +843,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
Some(&sig.explicit_self.node), Some(&sig.explicit_self.node),
item.span)) item.span))
} }
ast::TypeImplItem(_) => None, ast::MacImplItem(_) => self.tcx.sess.bug("unexpanded macro"),
ast::MacImplItem(_) => self.tcx.sess.bug("unexpanded macro") _ => None,
} }
}, },
ast_map::NodeTraitItem(item) => { ast_map::NodeTraitItem(item) => {
@ -1723,8 +1723,8 @@ fn lifetimes_in_scope(tcx: &ty::ctxt,
taken.push_all(&sig.generics.lifetimes); taken.push_all(&sig.generics.lifetimes);
Some(ii.id) Some(ii.id)
} }
ast::TypeImplItem(_) => None, ast::MacImplItem(_) => tcx.sess.bug("unexpanded macro"),
ast::MacImplItem(_) => tcx.sess.bug("unexpanded macro") _ => None,
} }
} }
_ => None _ => None

View file

@ -589,7 +589,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
match def { match def {
def::DefStruct(..) | def::DefVariant(..) | def::DefConst(..) | def::DefStruct(..) | def::DefVariant(..) | def::DefConst(..) |
def::DefFn(..) | def::DefMethod(..) => { def::DefAssociatedConst(..) | def::DefFn(..) | def::DefMethod(..) => {
Ok(self.cat_rvalue_node(id, span, expr_ty)) Ok(self.cat_rvalue_node(id, span, expr_ty))
} }
def::DefMod(_) | def::DefForeignMod(_) | def::DefUse(_) | def::DefMod(_) | def::DefForeignMod(_) | def::DefUse(_) |
@ -1286,7 +1286,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
try!(self.cat_pattern_(cmt_field, &**subpat, op)); try!(self.cat_pattern_(cmt_field, &**subpat, op));
} }
} }
Some(def::DefConst(..)) => { Some(def::DefConst(..)) | Some(def::DefAssociatedConst(..)) => {
for subpat in subpats { for subpat in subpats {
try!(self.cat_pattern_(cmt.clone(), &**subpat, op)); try!(self.cat_pattern_(cmt.clone(), &**subpat, op));
} }
@ -1299,6 +1299,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
} }
} }
ast::PatQPath(..) => {
// Lone constant: ignore
}
ast::PatIdent(_, _, Some(ref subpat)) => { ast::PatIdent(_, _, Some(ref subpat)) => {
try!(self.cat_pattern_(cmt, &**subpat, op)); try!(self.cat_pattern_(cmt, &**subpat, op));
} }

View file

@ -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 { pub fn pat_is_refutable(dm: &DefMap, pat: &ast::Pat) -> bool {
match pat.node { match pat.node {
ast::PatLit(_) | ast::PatRange(_, _) => true, ast::PatLit(_) | ast::PatRange(_, _) | ast::PatQPath(..) => true,
ast::PatEnum(_, _) | ast::PatEnum(_, _) |
ast::PatIdent(_, _, None) | ast::PatIdent(_, _, None) |
ast::PatStruct(..) => { ast::PatStruct(..) => {
@ -60,9 +60,25 @@ pub fn pat_is_variant_or_struct(dm: &DefMap, pat: &ast::Pat) -> bool {
pub fn pat_is_const(dm: &DefMap, pat: &ast::Pat) -> bool { pub fn pat_is_const(dm: &DefMap, pat: &ast::Pat) -> bool {
match pat.node { 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()) { match dm.borrow().get(&pat.id).map(|d| d.full_def()) {
Some(DefConst(..)) => true, Some(DefConst(..)) | Some(DefAssociatedConst(..)) => true,
_ => false
}
}
_ => false
}
}
// 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
} }
} }

View file

@ -113,7 +113,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ReachableContext<'a, 'tcx> {
// If this path leads to a constant, then we need to // If this path leads to a constant, then we need to
// recurse into the constant to continue finding // recurse into the constant to continue finding
// items that are reachable. // items that are reachable.
def::DefConst(..) => { def::DefConst(..) | def::DefAssociatedConst(..) => {
self.worklist.push(def_id.node); self.worklist.push(def_id.node);
} }
@ -183,12 +183,14 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
} }
Some(ast_map::NodeTraitItem(trait_method)) => { Some(ast_map::NodeTraitItem(trait_method)) => {
match trait_method.node { match trait_method.node {
ast::ConstTraitItem(_, ref default) => default.is_some(),
ast::MethodTraitItem(_, ref body) => body.is_some(), ast::MethodTraitItem(_, ref body) => body.is_some(),
ast::TypeTraitItem(..) => false, ast::TypeTraitItem(..) => false,
} }
} }
Some(ast_map::NodeImplItem(impl_item)) => { Some(ast_map::NodeImplItem(impl_item)) => {
match impl_item.node { match impl_item.node {
ast::ConstImplItem(..) => true,
ast::MethodImplItem(ref sig, _) => { ast::MethodImplItem(ref sig, _) => {
if generics_require_inlining(&sig.generics) || if generics_require_inlining(&sig.generics) ||
attr::requests_inline(&impl_item.attrs) { attr::requests_inline(&impl_item.attrs) {
@ -303,9 +305,13 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
} }
ast_map::NodeTraitItem(trait_method) => { ast_map::NodeTraitItem(trait_method) => {
match trait_method.node { match trait_method.node {
ast::ConstTraitItem(_, None) |
ast::MethodTraitItem(_, None) => { ast::MethodTraitItem(_, None) => {
// Keep going, nothing to get exported // Keep going, nothing to get exported
} }
ast::ConstTraitItem(_, Some(ref expr)) => {
self.visit_expr(&*expr);
}
ast::MethodTraitItem(_, Some(ref body)) => { ast::MethodTraitItem(_, Some(ref body)) => {
visit::walk_block(self, body); visit::walk_block(self, body);
} }
@ -314,6 +320,9 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
} }
ast_map::NodeImplItem(impl_item) => { ast_map::NodeImplItem(impl_item) => {
match impl_item.node { match impl_item.node {
ast::ConstImplItem(_, ref expr) => {
self.visit_expr(&*expr);
}
ast::MethodImplItem(ref sig, ref body) => { ast::MethodImplItem(ref sig, ref body) => {
let did = self.tcx.map.get_parent_did(search_item); let did = self.tcx.map.get_parent_did(search_item);
if method_might_be_inlined(self.tcx, sig, impl_item, did) { if method_might_be_inlined(self.tcx, sig, impl_item, did) {

View file

@ -100,9 +100,7 @@ fn object_safety_violations_for_trait<'tcx>(tcx: &ty::ctxt<'tcx>,
.map(|code| ObjectSafetyViolation::Method(m.clone(), code)) .map(|code| ObjectSafetyViolation::Method(m.clone(), code))
.into_iter() .into_iter()
} }
ty::TypeTraitItem(_) => { _ => None.into_iter(),
None.into_iter()
}
} }
}) })
.collect(); .collect();

View file

@ -863,7 +863,7 @@ fn confirm_impl_candidate<'cx,'tcx>(
for impl_item in impl_items { for impl_item in impl_items {
let assoc_type = match *impl_or_trait_items_map.get(&impl_item.def_id()).unwrap() { let assoc_type = match *impl_or_trait_items_map.get(&impl_item.def_id()).unwrap() {
ty::TypeTraitItem(ref assoc_type) => assoc_type.clone(), ty::TypeTraitItem(ref assoc_type) => assoc_type.clone(),
ty::MethodTraitItem(..) => { continue; } ty::ConstTraitItem(..) | ty::MethodTraitItem(..) => { continue; }
}; };
if assoc_type.name != obligation.predicate.item_name { if assoc_type.name != obligation.predicate.item_name {

View file

@ -434,7 +434,7 @@ pub fn get_vtable_index_of_object_method<'tcx>(tcx: &ty::ctxt<'tcx>,
for trait_item in &**trait_items { for trait_item in &**trait_items {
match *trait_item { match *trait_item {
ty::MethodTraitItem(_) => method_count += 1, ty::MethodTraitItem(_) => method_count += 1,
ty::TypeTraitItem(_) => {} _ => {}
} }
} }
} }
@ -445,14 +445,14 @@ pub fn get_vtable_index_of_object_method<'tcx>(tcx: &ty::ctxt<'tcx>,
for trait_item in trait_items.iter().take(method_offset_in_trait) { for trait_item in trait_items.iter().take(method_offset_in_trait) {
match *trait_item { match *trait_item {
ty::MethodTraitItem(_) => method_count += 1, ty::MethodTraitItem(_) => method_count += 1,
ty::TypeTraitItem(_) => {} _ => {}
} }
} }
// the item at the offset we were given really ought to be a method // the item at the offset we were given really ought to be a method
assert!(match trait_items[method_offset_in_trait] { assert!(match trait_items[method_offset_in_trait] {
ty::MethodTraitItem(_) => true, ty::MethodTraitItem(_) => true,
ty::TypeTraitItem(_) => false _ => false
}); });
method_count method_count

View file

@ -80,7 +80,7 @@ use std::vec::IntoIter;
use collections::enum_set::{EnumSet, CLike}; use collections::enum_set::{EnumSet, CLike};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use syntax::abi; 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::{MutImmutable, MutMutable, Name, NamedField, NodeId};
use syntax::ast::{StmtExpr, StmtSemi, StructField, UnnamedField, Visibility}; use syntax::ast::{StmtExpr, StmtSemi, StructField, UnnamedField, Visibility};
use syntax::ast_util::{self, is_local, lit_is_str, local_def}; use syntax::ast_util::{self, is_local, lit_is_str, local_def};
@ -133,6 +133,7 @@ impl ImplOrTraitItemContainer {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum ImplOrTraitItem<'tcx> { pub enum ImplOrTraitItem<'tcx> {
ConstTraitItem(Rc<AssociatedConst<'tcx>>),
MethodTraitItem(Rc<Method<'tcx>>), MethodTraitItem(Rc<Method<'tcx>>),
TypeTraitItem(Rc<AssociatedType>), TypeTraitItem(Rc<AssociatedType>),
} }
@ -140,6 +141,9 @@ pub enum ImplOrTraitItem<'tcx> {
impl<'tcx> ImplOrTraitItem<'tcx> { impl<'tcx> ImplOrTraitItem<'tcx> {
fn id(&self) -> ImplOrTraitItemId { fn id(&self) -> ImplOrTraitItemId {
match *self { match *self {
ConstTraitItem(ref associated_const) => {
ConstTraitItemId(associated_const.def_id)
}
MethodTraitItem(ref method) => MethodTraitItemId(method.def_id), MethodTraitItem(ref method) => MethodTraitItemId(method.def_id),
TypeTraitItem(ref associated_type) => { TypeTraitItem(ref associated_type) => {
TypeTraitItemId(associated_type.def_id) TypeTraitItemId(associated_type.def_id)
@ -149,6 +153,7 @@ impl<'tcx> ImplOrTraitItem<'tcx> {
pub fn def_id(&self) -> ast::DefId { pub fn def_id(&self) -> ast::DefId {
match *self { match *self {
ConstTraitItem(ref associated_const) => associated_const.def_id,
MethodTraitItem(ref method) => method.def_id, MethodTraitItem(ref method) => method.def_id,
TypeTraitItem(ref associated_type) => associated_type.def_id, TypeTraitItem(ref associated_type) => associated_type.def_id,
} }
@ -156,13 +161,23 @@ impl<'tcx> ImplOrTraitItem<'tcx> {
pub fn name(&self) -> ast::Name { pub fn name(&self) -> ast::Name {
match *self { match *self {
ConstTraitItem(ref associated_const) => associated_const.name,
MethodTraitItem(ref method) => method.name, MethodTraitItem(ref method) => method.name,
TypeTraitItem(ref associated_type) => associated_type.name, TypeTraitItem(ref associated_type) => associated_type.name,
} }
} }
pub fn vis(&self) -> ast::Visibility {
match *self {
ConstTraitItem(ref associated_const) => associated_const.vis,
MethodTraitItem(ref method) => method.vis,
TypeTraitItem(ref associated_type) => associated_type.vis,
}
}
pub fn container(&self) -> ImplOrTraitItemContainer { pub fn container(&self) -> ImplOrTraitItemContainer {
match *self { match *self {
ConstTraitItem(ref associated_const) => associated_const.container,
MethodTraitItem(ref method) => method.container, MethodTraitItem(ref method) => method.container,
TypeTraitItem(ref associated_type) => associated_type.container, TypeTraitItem(ref associated_type) => associated_type.container,
} }
@ -171,13 +186,14 @@ impl<'tcx> ImplOrTraitItem<'tcx> {
pub fn as_opt_method(&self) -> Option<Rc<Method<'tcx>>> { pub fn as_opt_method(&self) -> Option<Rc<Method<'tcx>>> {
match *self { match *self {
MethodTraitItem(ref m) => Some((*m).clone()), MethodTraitItem(ref m) => Some((*m).clone()),
TypeTraitItem(_) => None _ => None,
} }
} }
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum ImplOrTraitItemId { pub enum ImplOrTraitItemId {
ConstTraitItemId(ast::DefId),
MethodTraitItemId(ast::DefId), MethodTraitItemId(ast::DefId),
TypeTraitItemId(ast::DefId), TypeTraitItemId(ast::DefId),
} }
@ -185,6 +201,7 @@ pub enum ImplOrTraitItemId {
impl ImplOrTraitItemId { impl ImplOrTraitItemId {
pub fn def_id(&self) -> ast::DefId { pub fn def_id(&self) -> ast::DefId {
match *self { match *self {
ConstTraitItemId(def_id) => def_id,
MethodTraitItemId(def_id) => def_id, MethodTraitItemId(def_id) => def_id,
TypeTraitItemId(def_id) => def_id, TypeTraitItemId(def_id) => def_id,
} }
@ -238,6 +255,16 @@ impl<'tcx> Method<'tcx> {
} }
} }
#[derive(Clone, Copy, Debug)]
pub struct AssociatedConst<'tcx> {
pub name: ast::Name,
pub ty: Ty<'tcx>,
pub vis: ast::Visibility,
pub def_id: ast::DefId,
pub container: ImplOrTraitItemContainer,
pub default: Option<ast::DefId>,
}
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct AssociatedType { pub struct AssociatedType {
pub name: ast::Name, pub name: ast::Name,
@ -2291,6 +2318,16 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
match cx.map.find(id) { match cx.map.find(id) {
Some(ast_map::NodeImplItem(ref impl_item)) => { Some(ast_map::NodeImplItem(ref impl_item)) => {
match impl_item.node { match impl_item.node {
ast::ConstImplItem(_, _) => {
let def_id = ast_util::local_def(id);
let scheme = lookup_item_type(cx, def_id);
let predicates = lookup_predicates(cx, def_id);
construct_parameter_environment(cx,
impl_item.span,
&scheme.generics,
&predicates,
id)
}
ast::MethodImplItem(_, ref body) => { ast::MethodImplItem(_, ref body) => {
let method_def_id = ast_util::local_def(id); let method_def_id = ast_util::local_def(id);
match ty::impl_or_trait_item(cx, method_def_id) { match ty::impl_or_trait_item(cx, method_def_id) {
@ -2304,11 +2341,10 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
method_bounds, method_bounds,
body.id) body.id)
} }
TypeTraitItem(_) => { _ => {
cx.sess cx.sess
.bug("ParameterEnvironment::for_item(): \ .bug("ParameterEnvironment::for_item(): \
can't create a parameter environment \ got non-method item from impl method?!")
for type trait items")
} }
} }
} }
@ -2322,6 +2358,25 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
} }
Some(ast_map::NodeTraitItem(trait_item)) => { Some(ast_map::NodeTraitItem(trait_item)) => {
match trait_item.node { match trait_item.node {
ast::ConstTraitItem(_, ref default) => {
match *default {
Some(_) => {
let def_id = ast_util::local_def(id);
let scheme = lookup_item_type(cx, def_id);
let predicates = lookup_predicates(cx, def_id);
construct_parameter_environment(cx,
trait_item.span,
&scheme.generics,
&predicates,
id)
}
None => {
cx.sess.bug("ParameterEnvironment::from_item(): \
can't create a parameter environment \
for const trait items without defaults")
}
}
}
ast::MethodTraitItem(_, None) => { ast::MethodTraitItem(_, None) => {
cx.sess.span_bug(trait_item.span, cx.sess.span_bug(trait_item.span,
"ParameterEnvironment::for_item(): "ParameterEnvironment::for_item():
@ -2342,11 +2397,11 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
method_bounds, method_bounds,
body.id) body.id)
} }
TypeTraitItem(_) => { _ => {
cx.sess cx.sess
.bug("ParameterEnvironment::for_item(): \ .bug("ParameterEnvironment::for_item(): \
can't create a parameter environment \ got non-method item from provided \
for type trait items") method?!")
} }
} }
} }
@ -4719,7 +4774,8 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
def::DefUpvar(..) | def::DefUpvar(..) |
def::DefLocal(..) => LvalueExpr, def::DefLocal(..) => LvalueExpr,
def::DefConst(..) => RvalueDatumExpr, def::DefConst(..) |
def::DefAssociatedConst(..) => RvalueDatumExpr,
def => { def => {
tcx.sess.span_bug( tcx.sess.span_bug(
@ -5070,10 +5126,10 @@ pub fn provided_trait_methods<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId)
if let ast::MethodTraitItem(_, Some(_)) = ti.node { if let ast::MethodTraitItem(_, Some(_)) = ti.node {
match impl_or_trait_item(cx, ast_util::local_def(ti.id)) { match impl_or_trait_item(cx, ast_util::local_def(ti.id)) {
MethodTraitItem(m) => Some(m), MethodTraitItem(m) => Some(m),
TypeTraitItem(_) => { _ => {
cx.sess.bug("provided_trait_methods(): \ cx.sess.bug("provided_trait_methods(): \
associated type found from \ non-method item found from \
looking up ProvidedMethod?!") looking up provided method?!")
} }
} }
} else { } else {
@ -5088,6 +5144,52 @@ pub fn provided_trait_methods<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId)
} }
} }
pub fn associated_consts<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId)
-> Vec<Rc<AssociatedConst<'tcx>>> {
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 {
csearch::get_associated_consts(cx, id)
}
}
/// Helper for looking things up in the various maps that are populated during /// 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 /// 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 /// these share the pattern that if the id is local, it should have been loaded
@ -5174,7 +5276,7 @@ pub fn is_associated_type(cx: &ctxt, id: ast::DefId) -> bool {
Some(ref item) => { Some(ref item) => {
match **item { match **item {
TypeTraitItem(_) => true, TypeTraitItem(_) => true,
MethodTraitItem(_) => false, _ => false,
} }
} }
None => false, None => false,
@ -6188,7 +6290,7 @@ pub fn populate_implementations_for_type_if_necessary(tcx: &ctxt,
.insert(method_def_id, source); .insert(method_def_id, source);
} }
} }
TypeTraitItem(_) => {} _ => {}
} }
} }
@ -6240,7 +6342,7 @@ pub fn populate_implementations_for_trait_if_necessary(
.insert(method_def_id, source); .insert(method_def_id, source);
} }
} }
TypeTraitItem(_) => {} _ => {}
} }
} }

View file

@ -830,6 +830,7 @@ impl<'tcx> Repr<'tcx> for ty::TraitDef<'tcx> {
impl<'tcx> Repr<'tcx> for ast::TraitItem { impl<'tcx> Repr<'tcx> for ast::TraitItem {
fn repr(&self, _tcx: &ctxt) -> String { fn repr(&self, _tcx: &ctxt) -> String {
let kind = match self.node { let kind = match self.node {
ast::ConstTraitItem(..) => "ConstTraitItem",
ast::MethodTraitItem(..) => "MethodTraitItem", ast::MethodTraitItem(..) => "MethodTraitItem",
ast::TypeTraitItem(..) => "TypeTraitItem", ast::TypeTraitItem(..) => "TypeTraitItem",
}; };
@ -1054,9 +1055,39 @@ impl<'tcx> Repr<'tcx> for ty::Variance {
} }
} }
impl<'tcx> Repr<'tcx> for ty::ImplOrTraitItem<'tcx> {
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
format!("ImplOrTraitItem({})",
match *self {
ty::ImplOrTraitItem::MethodTraitItem(ref i) => i.repr(tcx),
ty::ImplOrTraitItem::ConstTraitItem(ref i) => i.repr(tcx),
ty::ImplOrTraitItem::TypeTraitItem(ref i) => i.repr(tcx),
})
}
}
impl<'tcx> Repr<'tcx> for ty::AssociatedConst<'tcx> {
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
format!("AssociatedConst(name: {}, ty: {}, vis: {}, def_id: {})",
self.name.repr(tcx),
self.ty.repr(tcx),
self.vis.repr(tcx),
self.def_id.repr(tcx))
}
}
impl<'tcx> Repr<'tcx> for ty::AssociatedType {
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
format!("AssociatedType(name: {}, vis: {}, def_id: {})",
self.name.repr(tcx),
self.vis.repr(tcx),
self.def_id.repr(tcx))
}
}
impl<'tcx> Repr<'tcx> for ty::Method<'tcx> { impl<'tcx> Repr<'tcx> for ty::Method<'tcx> {
fn repr(&self, tcx: &ctxt<'tcx>) -> String { fn repr(&self, tcx: &ctxt<'tcx>) -> String {
format!("method(name: {}, generics: {}, predicates: {}, fty: {}, \ format!("Method(name: {}, generics: {}, predicates: {}, fty: {}, \
explicit_self: {}, vis: {}, def_id: {})", explicit_self: {}, vis: {}, def_id: {})",
self.name.repr(tcx), self.name.repr(tcx),
self.generics.repr(tcx), self.generics.repr(tcx),

View file

@ -77,6 +77,20 @@ impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
fn visit_item(&mut self, item: &ast::Item) { fn visit_item(&mut self, item: &ast::Item) {
borrowck_item(self, 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) { pub fn check_crate(tcx: &ty::ctxt) {

View file

@ -35,6 +35,7 @@ use syntax::codemap;
use syntax::fold::{self, Folder}; use syntax::fold::{self, Folder};
use syntax::print::{pp, pprust}; use syntax::print::{pp, pprust};
use syntax::ptr::P; use syntax::ptr::P;
use syntax::util::small_vector::SmallVector;
use graphviz as dot; use graphviz as dot;
@ -475,6 +476,29 @@ impl fold::Folder for ReplaceBodyWithLoop {
} }
} }
fn fold_trait_item(&mut self, i: P<ast::TraitItem>) -> SmallVector<P<ast::TraitItem>> {
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<ast::ImplItem>) -> SmallVector<P<ast::ImplItem>> {
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<ast::Block>) -> P<ast::Block> { fn fold_block(&mut self, b: P<ast::Block>) -> P<ast::Block> {
fn expr_to_block(rules: ast::BlockCheckMode, fn expr_to_block(rules: ast::BlockCheckMode,

View file

@ -1068,6 +1068,26 @@ 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) { fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
// Lint for constants that look like binding identifiers (#7526) // 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())) { match (&p.node, cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def())) {
@ -1584,8 +1604,9 @@ impl LintPass for MissingDoc {
if self.private_traits.contains(&trait_item.id) { return } if self.private_traits.contains(&trait_item.id) { return }
let desc = match trait_item.node { let desc = match trait_item.node {
ast::ConstTraitItem(..) => "an associated constant",
ast::MethodTraitItem(..) => "a trait method", ast::MethodTraitItem(..) => "a trait method",
ast::TypeTraitItem(..) => "an associated type" ast::TypeTraitItem(..) => "an associated type",
}; };
self.check_missing_docs_attrs(cx, Some(trait_item.id), self.check_missing_docs_attrs(cx, Some(trait_item.id),
@ -1600,9 +1621,10 @@ impl LintPass for MissingDoc {
} }
let desc = match impl_item.node { let desc = match impl_item.node {
ast::ConstImplItem(..) => "an associated constant",
ast::MethodImplItem(..) => "a method", ast::MethodImplItem(..) => "a method",
ast::TypeImplItem(_) => "an associated type", ast::TypeImplItem(_) => "an associated type",
ast::MacImplItem(_) => "an impl item macro" ast::MacImplItem(_) => "an impl item macro",
}; };
self.check_missing_docs_attrs(cx, Some(impl_item.id), self.check_missing_docs_attrs(cx, Some(impl_item.id),
&impl_item.attrs, &impl_item.attrs,

View file

@ -119,6 +119,15 @@ impl<'v> Visitor<'v> for ParentVisitor {
visit::walk_fn(self, a, b, c, d); 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, fn visit_struct_def(&mut self, s: &ast::StructDef, _: ast::Ident,
_: &'v ast::Generics, n: ast::NodeId) { _: &'v ast::Generics, n: ast::NodeId) {
// Struct constructors are parented to their struct definitions because // Struct constructors are parented to their struct definitions because
@ -272,6 +281,12 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
if public_ty || public_trait { if public_ty || public_trait {
for impl_item in impl_items { for impl_item in impl_items {
match impl_item.node { match impl_item.node {
ast::ConstImplItem(..) => {
if (public_ty && impl_item.vis == ast::Public)
|| tr.is_some() {
self.exported_items.insert(impl_item.id);
}
}
ast::MethodImplItem(ref sig, _) => { ast::MethodImplItem(ref sig, _) => {
let meth_public = match sig.explicit_self.node { let meth_public = match sig.explicit_self.node {
ast::SelfStatic => public_ty, ast::SelfStatic => public_ty,
@ -399,6 +414,33 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
debug!("privacy - is {:?} a public method", did); debug!("privacy - is {:?} a public method", did);
return match self.tcx.impl_or_trait_items.borrow().get(&did) { return match self.tcx.impl_or_trait_items.borrow().get(&did) {
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)) => { Some(&ty::MethodTraitItem(ref meth)) => {
debug!("privacy - well at least it's a method: {:?}", debug!("privacy - well at least it's a method: {:?}",
*meth); *meth);
@ -490,6 +532,7 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
// where the method was defined? // where the method was defined?
Some(ast_map::NodeImplItem(ii)) => { Some(ast_map::NodeImplItem(ii)) => {
match ii.node { match ii.node {
ast::ConstImplItem(..) |
ast::MethodImplItem(..) => { ast::MethodImplItem(..) => {
let imp = self.tcx.map let imp = self.tcx.map
.get_parent_did(closest_private_id); .get_parent_did(closest_private_id);
@ -693,7 +736,11 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
ty::MethodTraitItem(method_type) => { ty::MethodTraitItem(method_type) => {
method_type.provided_source.unwrap_or(method_id) method_type.provided_source.unwrap_or(method_id)
} }
ty::TypeTraitItem(_) => method_id, _ => {
self.tcx.sess
.span_bug(span,
"got non-method item in check_static_method")
}
}; };
let string = token::get_name(name); let string = token::get_name(name);
@ -787,6 +834,7 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
def::DefFn(..) => ck("function"), def::DefFn(..) => ck("function"),
def::DefStatic(..) => ck("static"), def::DefStatic(..) => ck("static"),
def::DefConst(..) => ck("const"), def::DefConst(..) => ck("const"),
def::DefAssociatedConst(..) => ck("associated const"),
def::DefVariant(..) => ck("variant"), def::DefVariant(..) => ck("variant"),
def::DefTy(_, false) => ck("type"), def::DefTy(_, false) => ck("type"),
def::DefTy(_, true) => ck("enum"), def::DefTy(_, true) => ck("enum"),
@ -1128,8 +1176,7 @@ impl<'a, 'tcx> SanePrivacyVisitor<'a, 'tcx> {
ast::MethodImplItem(..) => { ast::MethodImplItem(..) => {
check_inherited(tcx, impl_item.span, impl_item.vis); check_inherited(tcx, impl_item.span, impl_item.vis);
} }
ast::TypeImplItem(_) | _ => {}
ast::MacImplItem(_) => {}
} }
} }
} }
@ -1307,6 +1354,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> {
impl_items.iter() impl_items.iter()
.any(|impl_item| { .any(|impl_item| {
match impl_item.node { match impl_item.node {
ast::ConstImplItem(..) |
ast::MethodImplItem(..) => { ast::MethodImplItem(..) => {
self.exported_items.contains(&impl_item.id) self.exported_items.contains(&impl_item.id)
} }
@ -1330,6 +1378,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> {
// don't erroneously report errors for private // don't erroneously report errors for private
// types in private items. // types in private items.
match impl_item.node { match impl_item.node {
ast::ConstImplItem(..) |
ast::MethodImplItem(..) ast::MethodImplItem(..)
if self.item_is_public(&impl_item.id, impl_item.vis) => if self.item_is_public(&impl_item.id, impl_item.vis) =>
{ {
@ -1360,13 +1409,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> {
// Those in 3. are warned with this call. // Those in 3. are warned with this call.
for impl_item in impl_items { for impl_item in impl_items {
match impl_item.node { if let ast::TypeImplItem(ref ty) = impl_item.node {
ast::TypeImplItem(ref ty) => {
self.visit_ty(ty); self.visit_ty(ty);
} }
ast::MethodImplItem(..) |
ast::MacImplItem(_) => {},
}
} }
} }
} }
@ -1376,6 +1421,12 @@ impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> {
let mut found_pub_static = false; let mut found_pub_static = false;
for impl_item in impl_items { for impl_item in impl_items {
match impl_item.node { match impl_item.node {
ast::ConstImplItem(..) => {
if self.item_is_public(&impl_item.id, impl_item.vis) {
found_pub_static = true;
visit::walk_impl_item(self, impl_item);
}
}
ast::MethodImplItem(ref sig, _) => { ast::MethodImplItem(ref sig, _) => {
if sig.explicit_self.node == ast::SelfStatic && if sig.explicit_self.node == ast::SelfStatic &&
self.item_is_public(&impl_item.id, impl_item.vis) { self.item_is_public(&impl_item.id, impl_item.vis) {
@ -1383,8 +1434,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> {
visit::walk_impl_item(self, impl_item); visit::walk_impl_item(self, impl_item);
} }
} }
ast::TypeImplItem(_) | _ => {}
ast::MacImplItem(_) => {}
} }
} }
if found_pub_static { if found_pub_static {

View file

@ -530,6 +530,12 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
trait_item.span); trait_item.span);
match trait_item.node { match trait_item.node {
ast::ConstTraitItem(..) => {
let def = DefAssociatedConst(local_def(trait_item.id),
FromTrait(local_def(item.id)));
// NB: not IMPORTABLE
name_bindings.define_value(def, trait_item.span, PUBLIC);
}
ast::MethodTraitItem(..) => { ast::MethodTraitItem(..) => {
let def = DefMethod(local_def(trait_item.id), let def = DefMethod(local_def(trait_item.id),
FromTrait(local_def(item.id))); FromTrait(local_def(item.id)));
@ -703,7 +709,8 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
csearch::get_tuple_struct_definition_if_ctor(&self.session.cstore, ctor_id) csearch::get_tuple_struct_definition_if_ctor(&self.session.cstore, ctor_id)
.map_or(def, |_| DefStruct(ctor_id)), DUMMY_SP, modifiers); .map_or(def, |_| DefStruct(ctor_id)), DUMMY_SP, modifiers);
} }
DefFn(..) | DefStatic(..) | DefConst(..) | DefMethod(..) => { DefFn(..) | DefStatic(..) | DefConst(..) | DefAssociatedConst(..) |
DefMethod(..) => {
debug!("(building reduced graph for external \ debug!("(building reduced graph for external \
crate) building value (fn/static) {}", final_ident); crate) building value (fn/static) {}", final_ident);
// impl methods have already been defined with the correct importability modifier // impl methods have already been defined with the correct importability modifier

View file

@ -41,6 +41,7 @@ use self::TypeParameters::*;
use self::RibKind::*; use self::RibKind::*;
use self::UseLexicalScopeFlag::*; use self::UseLexicalScopeFlag::*;
use self::ModulePrefixResult::*; use self::ModulePrefixResult::*;
use self::AssocItemResolveResult::*;
use self::NameSearchType::*; use self::NameSearchType::*;
use self::BareIdentifierPatternResolution::*; use self::BareIdentifierPatternResolution::*;
use self::ParentLink::*; use self::ParentLink::*;
@ -60,7 +61,8 @@ use rustc::middle::ty::{Freevar, FreevarMap, TraitMap, GlobMap};
use rustc::util::nodemap::{NodeMap, NodeSet, DefIdSet, FnvHashMap}; use rustc::util::nodemap::{NodeMap, NodeSet, DefIdSet, FnvHashMap};
use rustc::util::lev_distance::lev_distance; use rustc::util::lev_distance::lev_distance;
use syntax::ast::{Arm, BindByRef, BindByValue, BindingMode, Block, Crate, CrateNum}; use syntax::ast::{Arm, BindByRef, BindByValue, BindingMode, Block};
use syntax::ast::{ConstImplItem, Crate, CrateNum};
use syntax::ast::{DefId, Expr, ExprAgain, ExprBreak, ExprField}; use syntax::ast::{DefId, Expr, ExprAgain, ExprBreak, ExprField};
use syntax::ast::{ExprLoop, ExprWhile, ExprMethodCall}; use syntax::ast::{ExprLoop, ExprWhile, ExprMethodCall};
use syntax::ast::{ExprPath, ExprStruct, FnDecl}; use syntax::ast::{ExprPath, ExprStruct, FnDecl};
@ -69,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::{ItemFn, ItemForeignMod, ItemImpl, ItemMac, ItemMod, ItemStatic, ItemDefaultImpl};
use syntax::ast::{ItemStruct, ItemTrait, ItemTy, ItemUse}; use syntax::ast::{ItemStruct, ItemTrait, ItemTy, ItemUse};
use syntax::ast::{Local, MethodImplItem, Name, NodeId}; 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::{PatRange, PatStruct, Path, PrimTy};
use syntax::ast::{TraitRef, Ty, TyBool, TyChar, TyF32}; use syntax::ast::{TraitRef, Ty, TyBool, TyChar, TyF32};
use syntax::ast::{TyF64, TyFloat, TyIs, TyI8, TyI16, TyI32, TyI64, TyInt}; use syntax::ast::{TyF64, TyFloat, TyIs, TyI8, TyI16, TyI32, TyI64, TyInt};
@ -330,6 +332,15 @@ enum ModulePrefixResult {
PrefixFound(Rc<Module>, usize) PrefixFound(Rc<Module>, usize)
} }
#[derive(Copy, Clone)]
enum AssocItemResolveResult {
/// Syntax such as `<T>::item`, which can't be resolved until type
/// checking.
TypecheckRequired,
/// We should have been able to resolve the associated item.
ResolveAttempt(Option<PathResolution>),
}
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
enum NameSearchType { enum NameSearchType {
/// We're doing a name search in order to resolve a `use` directive. /// We're doing a name search in order to resolve a `use` directive.
@ -1830,22 +1841,37 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// //
// FIXME #4951: Do we need a node ID here? // FIXME #4951: Do we need a node ID here?
let type_parameters = match trait_item.node { 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, _) => { ast::MethodTraitItem(ref sig, _) => {
let type_parameters =
HasTypeParameters(&sig.generics, HasTypeParameters(&sig.generics,
FnSpace, FnSpace,
MethodRibKind) MethodRibKind);
this.with_type_parameter_rib(type_parameters, |this| {
visit::walk_trait_item(this, trait_item)
});
} }
ast::TypeTraitItem(..) => { ast::TypeTraitItem(..) => {
this.check_if_primitive_type_name(trait_item.ident.name, this.check_if_primitive_type_name(trait_item.ident.name,
trait_item.span); trait_item.span);
NoTypeParameters this.with_type_parameter_rib(NoTypeParameters, |this| {
}
};
this.with_type_parameter_rib(type_parameters, |this| {
visit::walk_trait_item(this, trait_item) visit::walk_trait_item(this, trait_item)
}); });
} }
};
}
}); });
}); });
} }
@ -2094,6 +2120,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
this.with_current_self_type(self_type, |this| { this.with_current_self_type(self_type, |this| {
for impl_item in impl_items { for impl_item in impl_items {
match impl_item.node { match impl_item.node {
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, _) => { MethodImplItem(ref sig, _) => {
// If this is a trait impl, ensure the method // If this is a trait impl, ensure the method
// exists in trait // exists in trait
@ -2280,32 +2315,22 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
fn resolve_type(&mut self, ty: &Ty) { fn resolve_type(&mut self, ty: &Ty) {
match ty.node { match ty.node {
// `<T>::a::b::c` is resolved by typeck alone.
TyPath(Some(ast::QSelf { position: 0, .. }), _) => {}
TyPath(ref maybe_qself, ref path) => { TyPath(ref maybe_qself, ref path) => {
let max_assoc_types = if let Some(ref qself) = *maybe_qself { let resolution =
// Make sure the trait is valid. match self.resolve_possibly_assoc_item(ty.id,
let _ = self.resolve_trait_reference(ty.id, path, 1); maybe_qself.as_ref(),
path.segments.len() - qself.position path,
} else { TypeNS,
path.segments.len() true) {
// `<T>::a::b::c` is resolved by typeck alone.
TypecheckRequired => {
// Resolve embedded types.
visit::walk_ty(self, ty);
return;
}
ResolveAttempt(resolution) => resolution,
}; };
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;
}
// This is a path in the type namespace. Walk through scopes // This is a path in the type namespace. Walk through scopes
// looking for it. // looking for it.
match resolution { match resolution {
@ -2464,7 +2489,22 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
PatEnum(ref path, _) => { PatEnum(ref path, _) => {
// This must be an enum variant, struct or const. // 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 { match path_res.base_def {
DefVariant(..) | DefStruct(..) | DefConst(..) => { DefVariant(..) | DefStruct(..) | DefConst(..) => {
self.record_def(pattern.id, path_res); self.record_def(pattern.id, path_res);
@ -2476,10 +2516,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
use a `const` instead"); use a `const` instead");
} }
_ => { _ => {
self.resolve_error(path.span, // 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", &format!("`{}` is not an enum variant, struct or const",
token::get_ident( token::get_ident(
path.segments.last().unwrap().identifier))); path.segments.last().unwrap().identifier)));
} else {
self.record_def(pattern.id, path_res);
}
} }
} }
} else { } else {
@ -2490,6 +2540,47 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
visit::walk_path(self, path); 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 `<T>::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 `<T as Trait>::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 associated const",
token::get_ident(
path.segments.last().unwrap().identifier)));
}
}
} else {
self.resolve_error(path.span,
&format!("unresolved associated const `{}`",
token::get_ident(path.segments.last().unwrap().identifier)));
}
visit::walk_pat(self, pattern);
}
PatStruct(ref path, _, _) => { PatStruct(ref path, _, _) => {
match self.resolve_path(pat_id, path, 0, TypeNS, false) { match self.resolve_path(pat_id, path, 0, TypeNS, false) {
Some(definition) => { Some(definition) => {
@ -2542,7 +2633,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
def @ DefVariant(..) | def @ DefStruct(..) => { def @ DefVariant(..) | def @ DefStruct(..) => {
return FoundStructOrEnumVariant(def, LastMod(AllPublic)); return FoundStructOrEnumVariant(def, LastMod(AllPublic));
} }
def @ DefConst(..) => { def @ DefConst(..) | def @ DefAssociatedConst(..) => {
return FoundConst(def, LastMod(AllPublic)); return FoundConst(def, LastMod(AllPublic));
} }
DefStatic(..) => { DefStatic(..) => {
@ -2579,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. /// If `check_ribs` is true, checks the local definitions first; i.e.
/// doesn't skip straight to the containing module. /// doesn't skip straight to the containing module.
/// Skips `path_depth` trailing segments, which is also reflected in the /// Skips `path_depth` trailing segments, which is also reflected in the
@ -3093,39 +3225,24 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// Next, resolve the node. // Next, resolve the node.
match expr.node { match expr.node {
ExprPath(ref maybe_qself, ref path) => {
let resolution =
match self.resolve_possibly_assoc_item(expr.id,
maybe_qself.as_ref(),
path,
ValueNS,
true) {
// `<T>::a::b::c` is resolved by typeck alone. // `<T>::a::b::c` is resolved by typeck alone.
ExprPath(Some(ast::QSelf { position: 0, .. }), ref path) => { TypecheckRequired => {
let method_name = path.segments.last().unwrap().identifier.name; 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); self.trait_map.insert(expr.id, traits);
visit::walk_expr(self, expr); visit::walk_expr(self, expr);
return;
} }
ResolveAttempt(resolution) => resolution,
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;
}
// This is a local path in the value namespace. Walk through // This is a local path in the value namespace. Walk through
// scopes looking for it. // scopes looking for it.
if let Some(path_res) = resolution { if let Some(path_res) = resolution {
@ -3155,7 +3272,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// so they can be completed during typeck. // so they can be completed during typeck.
if path_res.depth != 0 { if path_res.depth != 0 {
let method_name = path.segments.last().unwrap().identifier.name; 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); self.trait_map.insert(expr.id, traits);
} }
@ -3313,14 +3430,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// field, we need to add any trait methods we find that match // field, we need to add any trait methods we find that match
// the field name so that we can do some nice error reporting // the field name so that we can do some nice error reporting
// later on in typeck. // 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); self.trait_map.insert(expr.id, traits);
} }
ExprMethodCall(ident, _, _) => { ExprMethodCall(ident, _, _) => {
debug!("(recording candidate traits for expr) recording \ debug!("(recording candidate traits for expr) recording \
traits for {}", traits for {}",
expr.id); 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); self.trait_map.insert(expr.id, traits);
} }
_ => { _ => {
@ -3329,8 +3446,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
} }
} }
fn search_for_traits_containing_method(&mut self, name: Name) -> Vec<DefId> { fn get_traits_containing_item(&mut self, name: Name) -> Vec<DefId> {
debug!("(searching for traits containing method) looking for '{}'", debug!("(getting traits containing item) looking for '{}'",
token::get_name(name)); token::get_name(name));
fn add_trait_info(found_traits: &mut Vec<DefId>, fn add_trait_info(found_traits: &mut Vec<DefId>,

View file

@ -242,6 +242,7 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
def::DefTrait(_) => Some(recorder::TypeRef), def::DefTrait(_) => Some(recorder::TypeRef),
def::DefStatic(_, _) | def::DefStatic(_, _) |
def::DefConst(_) | def::DefConst(_) |
def::DefAssociatedConst(..) |
def::DefLocal(_) | def::DefLocal(_) |
def::DefVariant(_, _, _) | def::DefVariant(_, _, _) |
def::DefUpvar(..) => Some(recorder::VarRef), def::DefUpvar(..) => Some(recorder::VarRef),
@ -359,14 +360,10 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
// record the decl for this def (if it has one) // record the decl for this def (if it has one)
let decl_id = ty::trait_item_of_item(&self.analysis.ty_cx, let decl_id = ty::trait_item_of_item(&self.analysis.ty_cx,
ast_util::local_def(id)) ast_util::local_def(id))
.and_then(|def_id| { .and_then(|new_id| {
if match def_id { let def_id = new_id.def_id();
ty::MethodTraitItemId(def_id) => { if def_id.node != 0 && def_id != ast_util::local_def(id) {
def_id.node != 0 && def_id != ast_util::local_def(id) Some(def_id)
}
ty::TypeTraitItemId(_) => false,
} {
Some(def_id.def_id())
} else { } else {
None None
} }
@ -542,25 +539,27 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
} }
fn process_const(&mut self, fn process_const(&mut self,
item: &ast::Item, id: ast::NodeId,
ident: &ast::Ident,
span: Span,
typ: &ast::Ty, typ: &ast::Ty,
expr: &ast::Expr) 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); keywords::Const);
self.fmt.static_str(item.span, self.fmt.static_str(span,
sub_span, sub_span,
item.id, id,
&get_ident(item.ident), &get_ident((*ident).clone()),
&qualname[..], &qualname[..],
"", "",
&ty_to_string(&*typ), &ty_to_string(&*typ),
self.cur_scope); self.cur_scope);
// walk type and init value // walk type and init value
self.visit_ty(&*typ); self.visit_ty(typ);
self.visit_expr(expr); self.visit_expr(expr);
} }
@ -800,6 +799,7 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
def::DefLocal(..) | def::DefLocal(..) |
def::DefStatic(..) | def::DefStatic(..) |
def::DefConst(..) | def::DefConst(..) |
def::DefAssociatedConst(..) |
def::DefVariant(..) => self.fmt.ref_str(ref_kind.unwrap_or(recorder::VarRef), def::DefVariant(..) => self.fmt.ref_str(ref_kind.unwrap_or(recorder::VarRef),
span, span,
sub_span, sub_span,
@ -883,6 +883,7 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
def::DefLocal(_) | def::DefLocal(_) |
def::DefStatic(_,_) | def::DefStatic(_,_) |
def::DefConst(..) | def::DefConst(..) |
def::DefAssociatedConst(..) |
def::DefStruct(_) | def::DefStruct(_) |
def::DefVariant(..) | def::DefVariant(..) |
def::DefFn(..) => self.write_sub_paths_truncated(path, false), def::DefFn(..) => self.write_sub_paths_truncated(path, false),
@ -966,7 +967,10 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
ty::MethodTraitItem(method) => { ty::MethodTraitItem(method) => {
method.provided_source.unwrap_or(def_id) method.provided_source.unwrap_or(def_id)
} }
ty::TypeTraitItem(_) => def_id, _ => self.sess
.span_bug(ex.span,
"save::process_method_call: non-method \
DefId in MethodStatic or MethodStaticClosure"),
}; };
(Some(def_id), decl_id) (Some(def_id), decl_id)
} }
@ -1008,7 +1012,7 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
let def = self.analysis.ty_cx.def_map.borrow().get(&p.id).unwrap().full_def(); let def = self.analysis.ty_cx.def_map.borrow().get(&p.id).unwrap().full_def();
let struct_def = match def { let struct_def = match def {
def::DefConst(..) => None, def::DefConst(..) | def::DefAssociatedConst(..) => None,
def::DefVariant(_, variant_id, _) => Some(variant_id), def::DefVariant(_, variant_id, _) => Some(variant_id),
_ => { _ => {
match ty::ty_to_def_id(ty::node_id_to_type(&self.analysis.ty_cx, p.id)) { match ty::ty_to_def_id(ty::node_id_to_type(&self.analysis.ty_cx, p.id)) {
@ -1040,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)); self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef));
visit::walk_pat(self, p); visit::walk_pat(self, p);
} }
@ -1186,7 +1191,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
ast::ItemStatic(ref typ, mt, ref expr) => ast::ItemStatic(ref typ, mt, ref expr) =>
self.process_static(item, &**typ, mt, &**expr), self.process_static(item, &**typ, mt, &**expr),
ast::ItemConst(ref typ, ref 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::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::ItemEnum(ref def, ref ty_params) => self.process_enum(item, def, ty_params),
ast::ItemImpl(_, _, ast::ItemImpl(_, _,
@ -1236,16 +1241,25 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) { fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
match trait_item.node { match trait_item.node {
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) => { ast::MethodTraitItem(ref sig, ref body) => {
self.process_method(sig, body.as_ref().map(|x| &**x), self.process_method(sig, body.as_ref().map(|x| &**x),
trait_item.id, trait_item.ident.name, trait_item.span); trait_item.id, trait_item.ident.name, trait_item.span);
} }
ast::ConstTraitItem(_, None) |
ast::TypeTraitItem(..) => {} ast::TypeTraitItem(..) => {}
} }
} }
fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) { fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) {
match impl_item.node { match impl_item.node {
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) => { ast::MethodImplItem(ref sig, ref body) => {
self.process_method(sig, Some(body), impl_item.id, self.process_method(sig, Some(body), impl_item.id,
impl_item.ident.name, impl_item.span); impl_item.ident.name, impl_item.span);
@ -1432,8 +1446,9 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
paths_to_process.push((id, p.clone(), Some(ref_kind))) paths_to_process.push((id, p.clone(), Some(ref_kind)))
} }
// FIXME(nrc) what are these doing here? // FIXME(nrc) what are these doing here?
def::DefStatic(_, _) => {} def::DefStatic(_, _) |
def::DefConst(..) => {} def::DefConst(..) |
def::DefAssociatedConst(..) => {}
_ => error!("unexpected definition kind when processing collected paths: {:?}", _ => error!("unexpected definition kind when processing collected paths: {:?}",
def) def)
} }

View file

@ -1809,7 +1809,8 @@ fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ast::PatMac(..) => { ast::PatMac(..) => {
bcx.sess().span_bug(pat.span, "unexpanded macro"); bcx.sess().span_bug(pat.span, "unexpanded macro");
} }
ast::PatWild(_) | ast::PatLit(_) | ast::PatRange(_, _) => () ast::PatQPath(..) | ast::PatWild(_) | ast::PatLit(_) |
ast::PatRange(_, _) => ()
} }
return bcx; return bcx;
} }

View file

@ -1078,25 +1078,17 @@ fn build_cfg(tcx: &ty::ctxt, id: ast::NodeId) -> (ast::NodeId, Option<cfg::CFG>)
Some(ast_map::NodeTraitItem(trait_item)) => { Some(ast_map::NodeTraitItem(trait_item)) => {
match trait_item.node { match trait_item.node {
ast::MethodTraitItem(_, Some(ref body)) => body, ast::MethodTraitItem(_, Some(ref body)) => body,
ast::MethodTraitItem(_, None) => { _ => {
tcx.sess.bug("unexpected variant: required trait method \ tcx.sess.bug("unexpected variant: trait item other than a \
in has_nested_returns") provided method in has_nested_returns")
}
ast::TypeTraitItem(..) => {
tcx.sess.bug("unexpected variant: associated type trait item in \
has_nested_returns")
} }
} }
} }
Some(ast_map::NodeImplItem(impl_item)) => { Some(ast_map::NodeImplItem(impl_item)) => {
match impl_item.node { match impl_item.node {
ast::MethodImplItem(_, ref body) => body, ast::MethodImplItem(_, ref body) => body,
ast::TypeImplItem(_) => { _ => {
tcx.sess.bug("unexpected variant: associated type impl item in \ tcx.sess.bug("unexpected variant: non-method impl item in \
has_nested_returns")
}
ast::MacImplItem(_) => {
tcx.sess.bug("unexpected variant: unexpanded macro impl item in \
has_nested_returns") has_nested_returns")
} }
} }
@ -2363,13 +2355,14 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
ast_map::NodeTraitItem(trait_item) => { ast_map::NodeTraitItem(trait_item) => {
debug!("get_item_val(): processing a NodeTraitItem"); debug!("get_item_val(): processing a NodeTraitItem");
match trait_item.node { match trait_item.node {
ast::MethodTraitItem(_, None) | ast::TypeTraitItem(..) => {
ccx.sess().span_bug(trait_item.span,
"unexpected variant: required trait method in get_item_val()");
}
ast::MethodTraitItem(_, Some(_)) => { ast::MethodTraitItem(_, Some(_)) => {
register_method(ccx, id, &trait_item.attrs, trait_item.span) register_method(ccx, id, &trait_item.attrs, trait_item.span)
} }
_ => {
ccx.sess().span_bug(trait_item.span,
"unexpected variant: trait item other than a provided \
method in get_item_val()");
}
} }
} }
@ -2378,13 +2371,10 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
ast::MethodImplItem(..) => { ast::MethodImplItem(..) => {
register_method(ccx, id, &impl_item.attrs, impl_item.span) register_method(ccx, id, &impl_item.attrs, impl_item.span)
} }
ast::TypeImplItem(_) => { _ => {
ccx.sess().span_bug(impl_item.span, ccx.sess().span_bug(impl_item.span,
"unexpected variant: associated type in get_item_val()") "unexpected variant: non-method impl item in \
} get_item_val()");
ast::MacImplItem(_) => {
ccx.sess().span_bug(impl_item.span,
"unexpected variant: unexpanded macro in get_item_val()")
} }
} }
} }

View file

@ -202,6 +202,7 @@ fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr)
} }
def::DefStatic(..) | def::DefStatic(..) |
def::DefConst(..) | def::DefConst(..) |
def::DefAssociatedConst(..) |
def::DefLocal(..) | def::DefLocal(..) |
def::DefUpvar(..) => { def::DefUpvar(..) => {
datum_callee(bcx, ref_expr) datum_callee(bcx, ref_expr)
@ -465,9 +466,9 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>(
(true, source_id, new_substs) (true, source_id, new_substs)
} }
ty::TypeTraitItem(_) => { _ => {
tcx.sess.bug("trans_fn_ref_with_vtables() tried \ tcx.sess.bug("trans_fn_ref_with_vtables() tried \
to translate an associated type?!") to translate a non-method?!")
} }
} }
} }

View file

@ -173,13 +173,11 @@ pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
"cross crate constant could not be inlined"); "cross crate constant could not be inlined");
} }
let item = ccx.tcx().map.expect_item(def_id.node); match const_eval::lookup_const_by_id(ccx.tcx(), def_id, Some(ref_expr.id)) {
if let ast::ItemConst(_, ref expr) = item.node { Some(ref expr) => expr,
&**expr None => {
} else { ccx.sess().span_bug(ref_expr.span, "constant item not found")
ccx.sess().span_bug(ref_expr.span, }
&format!("get_const_expr given non-constant item {}",
item.repr(ccx.tcx())));
} }
} }
@ -201,7 +199,7 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
ast::ExprPath(..) => { ast::ExprPath(..) => {
let def = ccx.tcx().def_map.borrow().get(&expr.id).unwrap().full_def(); let def = ccx.tcx().def_map.borrow().get(&expr.id).unwrap().full_def();
match def { match def {
def::DefConst(def_id) => { def::DefConst(def_id) | def::DefAssociatedConst(def_id, _) => {
if !ccx.tcx().adjustments.borrow().contains_key(&expr.id) { if !ccx.tcx().adjustments.borrow().contains_key(&expr.id) {
return get_const_val(ccx, def_id, expr); 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(..) => { def::DefFn(..) | def::DefMethod(..) => {
expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val 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)) const_deref_ptr(cx, get_const_val(cx, def_id, e))
} }
def::DefVariant(enum_did, variant_did, _) => { def::DefVariant(enum_did, variant_did, _) => {

View file

@ -1314,15 +1314,10 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
impl_item.span, impl_item.span,
true) true)
} }
ast::TypeImplItem(_) => { _ => {
cx.sess().span_bug(impl_item.span, cx.sess().span_bug(impl_item.span,
"create_function_debug_context() \ "create_function_debug_context() \
called on associated type?!") called on non-method impl item?!")
}
ast::MacImplItem(_) => {
cx.sess().span_bug(impl_item.span,
"create_function_debug_context() \
called on unexpanded macro?!")
} }
} }
} }
@ -3442,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, _) => { ast::PatStruct(_, ref field_pats, _) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);

View file

@ -134,6 +134,14 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1); ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1);
// Associated consts already have to be evaluated in `typeck`, so
// the logic to do that already exists in `middle`. In order to
// reuse that code, it needs to be able to look up the traits for
// inlined items.
let ty_trait_item = ty::impl_or_trait_item(ccx.tcx(), fn_id).clone();
ccx.tcx().impl_or_trait_items.borrow_mut()
.insert(local_def(trait_item.id), ty_trait_item);
// If this is a default method, we can't look up the // If this is a default method, we can't look up the
// impl type. But we aren't going to translate anyways, so // impl type. But we aren't going to translate anyways, so
// don't. // don't.

View file

@ -74,8 +74,7 @@ pub fn trans_impl(ccx: &CrateContext,
ast::MethodImplItem(..) => { ast::MethodImplItem(..) => {
visit::walk_impl_item(&mut v, impl_item); visit::walk_impl_item(&mut v, impl_item);
} }
ast::TypeImplItem(_) | _ => {}
ast::MacImplItem(_) => {}
} }
} }
return; return;
@ -98,8 +97,7 @@ pub fn trans_impl(ccx: &CrateContext,
} }
visit::walk_impl_item(&mut v, impl_item); visit::walk_impl_item(&mut v, impl_item);
} }
ast::TypeImplItem(_) | _ => {}
ast::MacImplItem(_) => {}
} }
} }
} }
@ -336,9 +334,9 @@ fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let impl_did = vtable_impl.impl_def_id; let impl_did = vtable_impl.impl_def_id;
let mname = match ty::trait_item(ccx.tcx(), trait_id, n_method) { let mname = match ty::trait_item(ccx.tcx(), trait_id, n_method) {
ty::MethodTraitItem(method) => method.name, ty::MethodTraitItem(method) => method.name,
ty::TypeTraitItem(_) => { _ => {
bcx.tcx().sess.bug("can't monomorphize an associated \ bcx.tcx().sess.bug("can't monomorphize a non-method trait \
type") item")
} }
}; };
let mth_id = method_with_name(bcx.ccx(), impl_did, mname); let mth_id = method_with_name(bcx.ccx(), impl_did, mname);
@ -579,8 +577,8 @@ pub fn trans_object_shim<'a, 'tcx>(
// Lookup the type of this method as declared in the trait and apply substitutions. // Lookup the type of this method as declared in the trait and apply substitutions.
let method_ty = match ty::trait_item(tcx, trait_id, method_offset_in_trait) { let method_ty = match ty::trait_item(tcx, trait_id, method_offset_in_trait) {
ty::MethodTraitItem(method) => method, ty::MethodTraitItem(method) => method,
ty::TypeTraitItem(_) => { _ => {
tcx.sess.bug("can't create a method shim for an associated type") tcx.sess.bug("can't create a method shim for a non-method item")
} }
}; };
let fty = monomorphize::apply_param_substs(tcx, &object_substs, &method_ty.fty); let fty = monomorphize::apply_param_substs(tcx, &object_substs, &method_ty.fty);
@ -789,11 +787,11 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
trait_item_def_ids trait_item_def_ids
.iter() .iter()
// Filter out the associated types. // Filter out non-method items.
.filter_map(|item_def_id| { .filter_map(|item_def_id| {
match *item_def_id { match *item_def_id {
ty::MethodTraitItemId(def_id) => Some(def_id), ty::MethodTraitItemId(def_id) => Some(def_id),
ty::TypeTraitItemId(_) => None, _ => None,
} }
}) })
@ -806,7 +804,7 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
let trait_method_type = match ty::impl_or_trait_item(tcx, trait_method_def_id) { let trait_method_type = match ty::impl_or_trait_item(tcx, trait_method_def_id) {
ty::MethodTraitItem(m) => m, ty::MethodTraitItem(m) => m,
ty::TypeTraitItem(_) => ccx.sess().bug("should be a method, not assoc type") _ => ccx.sess().bug("should be a method, not other assoc item"),
}; };
let name = trait_method_type.name; let name = trait_method_type.name;
@ -824,7 +822,7 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
let impl_method_def_id = method_with_name(ccx, impl_id, name); let impl_method_def_id = method_with_name(ccx, impl_id, name);
let impl_method_type = match ty::impl_or_trait_item(tcx, impl_method_def_id) { let impl_method_type = match ty::impl_or_trait_item(tcx, impl_method_def_id) {
ty::MethodTraitItem(m) => m, ty::MethodTraitItem(m) => m,
ty::TypeTraitItem(_) => ccx.sess().bug("should be a method, not assoc type") _ => ccx.sess().bug("should be a method, not other assoc item"),
}; };
debug!("emit_vtable_methods: impl_method_type={}", debug!("emit_vtable_methods: impl_method_type={}",

View file

@ -236,11 +236,9 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
} }
d d
} }
ast::TypeImplItem(_) => { _ => {
ccx.sess().bug("can't monomorphize an associated type") ccx.sess().bug(&format!("can't monomorphize a {:?}",
} map_node))
ast::MacImplItem(_) => {
ccx.sess().bug("can't monomorphize an unexpanded macro")
} }
} }
} }

View file

@ -11,12 +11,14 @@
use middle::const_eval; use middle::const_eval;
use middle::def; use middle::def;
use middle::infer; 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::subst::Substs;
use middle::ty::{self, Ty}; use middle::ty::{self, Ty};
use check::{check_expr, check_expr_has_type, check_expr_with_expectation}; use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
use check::{check_expr_coercable_to_type, demand, FnCtxt, 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 require_same_types;
use util::nodemap::FnvHashMap; use util::nodemap::FnvHashMap;
use util::ppaux::Repr; 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 // subtyping doesn't matter here, as the value is some kind of scalar
demand::eqtype(fcx, pat.span, expected, lhs_ty); 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_did = tcx.def_map.borrow().get(&pat.id).unwrap().def_id();
let const_scheme = ty::lookup_item_type(tcx, const_did); let const_scheme = ty::lookup_item_type(tcx, const_did);
assert!(const_scheme.generics.is_empty()); 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[..]); let subpats = subpats.as_ref().map(|v| &v[..]);
check_pat_enum(pcx, pat, path, subpats, expected); 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) => { ast::PatStruct(ref path, ref fields, etc) => {
check_pat_struct(pcx, pat, path, fields, etc, expected); 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. // 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>, pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
span: Span, expected: Ty<'tcx>, span: Span, expected: Ty<'tcx>,
inner: &ast::Pat) -> bool { 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 fcx = pcx.fcx;
let tcx = pcx.fcx.ccx.tcx; 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() let enum_def = def.variant_def_ids()
.map_or_else(|| def.def_id(), |(enum_def, _)| enum_def); .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 { } else {
ctor_scheme ctor_scheme
}; };
instantiate_path(pcx.fcx, &path.segments, instantiate_path(pcx.fcx, segments,
path_scheme, &ctor_predicates, 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); let pat_ty = fcx.node_ty(pat.id);
demand::eqtype(fcx, pat.span, expected, pat_ty); demand::eqtype(fcx, pat.span, expected, pat_ty);
let real_path_ty = fcx.node_ty(pat.id); let real_path_ty = fcx.node_ty(pat.id);
let (arg_tys, kind_name): (Vec<_>, &'static str) = match real_path_ty.sty { let (arg_tys, kind_name): (Vec<_>, &'static str) = match real_path_ty.sty {
ty::ty_enum(enum_def_id, expected_substs) ty::ty_enum(enum_def_id, expected_substs)

View file

@ -412,3 +412,85 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
return true; 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;
}
}
}

View file

@ -109,10 +109,11 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
self.add_obligations(&pick, &all_substs, &method_predicates); self.add_obligations(&pick, &all_substs, &method_predicates);
// Create the final `MethodCallee`. // 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 { let fty = ty::mk_bare_fn(self.tcx(), None, self.tcx().mk_bare_fn(ty::BareFnTy {
sig: ty::Binder(method_sig), sig: ty::Binder(method_sig),
unsafety: pick.method_ty.fty.unsafety, unsafety: method_ty.fty.unsafety,
abi: pick.method_ty.fty.abi.clone(), abi: method_ty.fty.abi.clone(),
})); }));
let callee = MethodCallee { let callee = MethodCallee {
origin: method_origin, origin: method_origin,
@ -204,7 +205,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
"impl {:?} is not an inherent impl", impl_def_id); "impl {:?} is not an inherent impl", impl_def_id);
let impl_polytype = check::impl_self_ty(self.fcx, self.span, 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) => { 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 // If they were not explicitly supplied, just construct fresh
// variables. // variables.
let num_supplied_types = supplied_method_types.len(); 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 = { let method_types = {
if num_supplied_types == 0 { if num_supplied_types == 0 {
self.fcx.infcx().next_ty_vars(num_method_types) self.fcx.infcx().next_ty_vars(num_method_types)
@ -360,7 +362,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
let method_regions = let method_regions =
self.fcx.infcx().region_vars_for_defs( self.fcx.infcx().region_vars_for_defs(
self.span, 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) (method_types, method_regions)
} }
@ -397,7 +400,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
// Instantiate the bounds on the method with the // Instantiate the bounds on the method with the
// type/early-bound-regions substitutions performed. There can // type/early-bound-regions substitutions performed. There can
// be no late-bound regions appearing here. // 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, let method_predicates = self.fcx.normalize_associated_types_in(self.span,
&method_predicates); &method_predicates);
@ -410,7 +414,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
// NB: Instantiate late-bound regions first so that // NB: Instantiate late-bound regions first so that
// `instantiate_type_scheme` can normalize associated types that // `instantiate_type_scheme` can normalize associated types that
// may reference those regions. // 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={}", debug!("late-bound lifetimes from method instantiated, method_sig={}",
method_sig.repr(self.tcx())); method_sig.repr(self.tcx()));
@ -616,7 +621,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
fn enforce_illegal_method_limitations(&self, pick: &probe::Pick) { fn enforce_illegal_method_limitations(&self, pick: &probe::Pick) {
// Disallow calls to the method `drop` defined in the `Drop` trait. // 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) => { ty::TraitContainer(trait_def_id) => {
callee::check_legal_trait_for_method_call(self.fcx.ccx, self.span, 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 // potential calls to it will wind up in the other
// arm. But just to be sure, check that the method id // arm. But just to be sure, check that the method id
// does not appear in the list of destructors. // 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()));
} }
} }
} }

View file

@ -58,7 +58,7 @@ pub enum CandidateSource {
TraitSource(/* trait id */ ast::DefId), 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. /// Determines whether the type `self_ty` supports a method name `method_name` or not.
pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, 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 mode = probe::Mode::Path;
let pick = try!(probe::probe(fcx, span, mode, method_name, self_ty, expr_id)); 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 mut lp = LastMod(AllPublic);
let provenance = match pick.kind { let provenance = match pick.kind {
probe::InherentImplPick(impl_def_id) => { probe::InherentImplPick(impl_def_id) => {
if pick.method_ty.vis != ast::Public { if pick.item.vis() != ast::Public {
lp = LastMod(DependsOn(def_id)); lp = LastMod(DependsOn(def_id));
} }
def::FromImpl(impl_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))
} }

View file

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
use super::MethodError; use super::MethodError;
use super::MethodIndex; use super::ItemIndex;
use super::{CandidateSource,ImplSource,TraitSource}; use super::{CandidateSource,ImplSource,TraitSource};
use super::suggest; use super::suggest;
@ -37,7 +37,7 @@ struct ProbeContext<'a, 'tcx:'a> {
fcx: &'a FnCtxt<'a, 'tcx>, fcx: &'a FnCtxt<'a, 'tcx>,
span: Span, span: Span,
mode: Mode, mode: Mode,
method_name: ast::Name, item_name: ast::Name,
steps: Rc<Vec<CandidateStep<'tcx>>>, steps: Rc<Vec<CandidateStep<'tcx>>>,
opt_simplified_steps: Option<Vec<fast_reject::SimplifiedType>>, opt_simplified_steps: Option<Vec<fast_reject::SimplifiedType>>,
inherent_candidates: Vec<Candidate<'tcx>>, inherent_candidates: Vec<Candidate<'tcx>>,
@ -54,7 +54,7 @@ struct CandidateStep<'tcx> {
struct Candidate<'tcx> { struct Candidate<'tcx> {
xform_self_ty: Ty<'tcx>, xform_self_ty: Ty<'tcx>,
method_ty: Rc<ty::Method<'tcx>>, item: ty::ImplOrTraitItem<'tcx>,
kind: CandidateKind<'tcx>, kind: CandidateKind<'tcx>,
} }
@ -62,14 +62,14 @@ enum CandidateKind<'tcx> {
InherentImplCandidate(/* Impl */ ast::DefId, subst::Substs<'tcx>), InherentImplCandidate(/* Impl */ ast::DefId, subst::Substs<'tcx>),
ObjectCandidate(/* Trait */ ast::DefId, /* method_num */ usize, /* vtable index */ usize), ObjectCandidate(/* Trait */ ast::DefId, /* method_num */ usize, /* vtable index */ usize),
ExtensionImplCandidate(/* Impl */ ast::DefId, Rc<ty::TraitRef<'tcx>>, ExtensionImplCandidate(/* Impl */ ast::DefId, Rc<ty::TraitRef<'tcx>>,
subst::Substs<'tcx>, MethodIndex), subst::Substs<'tcx>, ItemIndex),
ClosureCandidate(/* Trait */ ast::DefId, MethodIndex), ClosureCandidate(/* Trait */ ast::DefId, ItemIndex),
WhereClauseCandidate(ty::PolyTraitRef<'tcx>, MethodIndex), WhereClauseCandidate(ty::PolyTraitRef<'tcx>, ItemIndex),
ProjectionCandidate(ast::DefId, MethodIndex), ProjectionCandidate(ast::DefId, ItemIndex),
} }
pub struct Pick<'tcx> { pub struct Pick<'tcx> {
pub method_ty: Rc<ty::Method<'tcx>>, pub item: ty::ImplOrTraitItem<'tcx>,
pub kind: PickKind<'tcx>, pub kind: PickKind<'tcx>,
// Indicates that the source expression should be autoderef'd N times // Indicates that the source expression should be autoderef'd N times
@ -94,20 +94,20 @@ pub struct Pick<'tcx> {
pub enum PickKind<'tcx> { pub enum PickKind<'tcx> {
InherentImplPick(/* Impl */ ast::DefId), InherentImplPick(/* Impl */ ast::DefId),
ObjectPick(/* Trait */ ast::DefId, /* method_num */ usize, /* real_index */ usize), ObjectPick(/* Trait */ ast::DefId, /* method_num */ usize, /* real_index */ usize),
ExtensionImplPick(/* Impl */ ast::DefId, MethodIndex), ExtensionImplPick(/* Impl */ ast::DefId, ItemIndex),
TraitPick(/* Trait */ ast::DefId, MethodIndex), TraitPick(/* Trait */ ast::DefId, ItemIndex),
WhereClausePick(/* Trait */ ty::PolyTraitRef<'tcx>, MethodIndex), WhereClausePick(/* Trait */ ty::PolyTraitRef<'tcx>, ItemIndex),
} }
pub type PickResult<'tcx> = Result<Pick<'tcx>, MethodError>; pub type PickResult<'tcx> = Result<Pick<'tcx>, MethodError>;
#[derive(PartialEq, Eq, Copy, Clone)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Mode { pub enum Mode {
// An expression of the form `receiver.method_name(...)`. // An expression of the form `receiver.method_name(...)`.
// Autoderefs are performed on `receiver`, lookup is done based on the // Autoderefs are performed on `receiver`, lookup is done based on the
// `self` argument of the method, and static methods aren't considered. // `self` argument of the method, and static methods aren't considered.
MethodCall, MethodCall,
// An expression of the form `Type::method` or `<T>::method`. // An expression of the form `Type::item` or `<T>::item`.
// No autoderefs are performed, lookup is done based on the type each // No autoderefs are performed, lookup is done based on the type each
// implementation is for, and static methods are included. // implementation is for, and static methods are included.
Path Path
@ -116,14 +116,14 @@ pub enum Mode {
pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span, span: Span,
mode: Mode, mode: Mode,
method_name: ast::Name, item_name: ast::Name,
self_ty: Ty<'tcx>, self_ty: Ty<'tcx>,
scope_expr_id: ast::NodeId) scope_expr_id: ast::NodeId)
-> PickResult<'tcx> -> 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()), self_ty.repr(fcx.tcx()),
method_name, item_name,
scope_expr_id); scope_expr_id);
// FIXME(#18741) -- right now, creating the steps involves evaluating the // 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, let mut probe_cx = ProbeContext::new(fcx,
span, span,
mode, mode,
method_name, item_name,
steps, steps,
opt_simplified_steps); opt_simplified_steps);
probe_cx.assemble_inherent_candidates(); probe_cx.assemble_inherent_candidates();
@ -221,7 +221,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
fn new(fcx: &'a FnCtxt<'a,'tcx>, fn new(fcx: &'a FnCtxt<'a,'tcx>,
span: Span, span: Span,
mode: Mode, mode: Mode,
method_name: ast::Name, item_name: ast::Name,
steps: Vec<CandidateStep<'tcx>>, steps: Vec<CandidateStep<'tcx>>,
opt_simplified_steps: Option<Vec<fast_reject::SimplifiedType>>) opt_simplified_steps: Option<Vec<fast_reject::SimplifiedType>>)
-> ProbeContext<'a,'tcx> -> ProbeContext<'a,'tcx>
@ -230,7 +230,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
fcx: fcx, fcx: fcx,
span: span, span: span,
mode: mode, mode: mode,
method_name: method_name, item_name: item_name,
inherent_candidates: Vec::new(), inherent_candidates: Vec::new(),
extension_candidates: Vec::new(), extension_candidates: Vec::new(),
impl_dups: HashSet::new(), impl_dups: HashSet::new(),
@ -387,12 +387,12 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
debug!("assemble_inherent_impl_probe {:?}", impl_def_id); 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, Some(m) => m,
None => { return; } // No method with correct name on this impl 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. // No receiver declared. Not a candidate.
return self.record_static_candidate(ImplSource(impl_def_id)); 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. // Determine the receiver type that the method itself expects.
let xform_self_ty = 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 { self.inherent_candidates.push(Candidate {
xform_self_ty: xform_self_ty, xform_self_ty: xform_self_ty,
method_ty: method, item: item,
kind: InherentImplCandidate(impl_def_id, impl_substs) 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 // itself. Hence, a `&self` method will wind up with an
// argument type like `&Trait`. // argument type like `&Trait`.
let trait_ref = data.principal_trait_ref_with_self_ty(self.tcx(), self_ty); 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 new_trait_ref = this.erase_late_bound_regions(&new_trait_ref);
let vtable_index = let vtable_index =
traits::get_vtable_index_of_object_method(tcx, traits::get_vtable_index_of_object_method(tcx,
trait_ref.clone(), trait_ref.clone(),
new_trait_ref.def_id, 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.self_ty(),
new_trait_ref.substs); new_trait_ref.substs);
this.inherent_candidates.push(Candidate { this.inherent_candidates.push(Candidate {
xform_self_ty: xform_self_ty, xform_self_ty: xform_self_ty,
method_ty: m, item: item,
kind: ObjectCandidate(new_trait_ref.def_id, method_num, vtable_index) kind: ObjectCandidate(new_trait_ref.def_id, item_num, vtable_index)
}); });
}); });
} }
@ -476,15 +476,16 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
}) })
.collect(); .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 = let trait_ref =
this.erase_late_bound_regions(&poly_trait_ref); this.erase_late_bound_regions(&poly_trait_ref);
let xform_self_ty = let xform_self_ty =
this.xform_self_ty(&m, this.xform_self_ty(&item,
trait_ref.self_ty(), trait_ref.self_ty(),
trait_ref.substs); trait_ref.substs);
if let Some(ref m) = item.as_opt_method() {
debug!("found match: trait_ref={} substs={} m={}", debug!("found match: trait_ref={} substs={} m={}",
trait_ref.repr(this.tcx()), trait_ref.repr(this.tcx()),
trait_ref.substs.repr(this.tcx()), trait_ref.substs.repr(this.tcx()),
@ -497,6 +498,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
trait_ref.substs.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(), assert_eq!(m.generics.regions.get_slice(subst::SelfSpace).len(),
trait_ref.substs.regions().get_slice(subst::SelfSpace).len()); trait_ref.substs.regions().get_slice(subst::SelfSpace).len());
}
// Because this trait derives from a where-clause, it // Because this trait derives from a where-clause, it
// should not contain any inference variables or other // should not contain any inference variables or other
@ -507,8 +509,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
this.inherent_candidates.push(Candidate { this.inherent_candidates.push(Candidate {
xform_self_ty: xform_self_ty, xform_self_ty: xform_self_ty,
method_ty: m, item: item,
kind: WhereClauseCandidate(poly_trait_ref, method_num) kind: WhereClauseCandidate(poly_trait_ref, item_num)
}); });
}); });
} }
@ -523,7 +525,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
F: for<'b> FnMut( F: for<'b> FnMut(
&mut ProbeContext<'b, 'tcx>, &mut ProbeContext<'b, 'tcx>,
ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>,
Rc<ty::Method<'tcx>>, ty::ImplOrTraitItem<'tcx>,
usize, usize,
), ),
{ {
@ -531,17 +533,17 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
let tcx = self.tcx(); let tcx = self.tcx();
for bound_trait_ref in traits::transitive_bounds(tcx, bounds) { for bound_trait_ref in traits::transitive_bounds(tcx, bounds) {
let (pos, method) = match trait_method(tcx, let (pos, item) = match trait_item(tcx,
bound_trait_ref.def_id(), bound_trait_ref.def_id(),
self.method_name) { self.item_name) {
Some(v) => v, Some(v) => v,
None => { continue; } None => { continue; }
}; };
if !self.has_applicable_self(&*method) { if !self.has_applicable_self(&item) {
self.record_static_candidate(TraitSource(bound_trait_ref.def_id())); self.record_static_candidate(TraitSource(bound_trait_ref.def_id()));
} else { } 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); ty::trait_items(self.tcx(), trait_def_id);
let matching_index = let matching_index =
trait_items.iter() trait_items.iter()
.position(|item| item.name() == self.method_name); .position(|item| item.name() == self.item_name);
let matching_index = match matching_index { let matching_index = match matching_index {
Some(i) => i, Some(i) => i,
None => { return Ok(()); } None => { return Ok(()); }
}; };
let method = match (&*trait_items)[matching_index].as_opt_method() { let ref item = (&*trait_items)[matching_index];
Some(m) => m,
None => { return Ok(()); }
};
// Check whether `trait_def_id` defines a method with suitable name: // 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"); debug!("method has inapplicable self");
self.record_static_candidate(TraitSource(trait_def_id)); self.record_static_candidate(TraitSource(trait_def_id));
return Ok(()); return Ok(());
} }
self.assemble_extension_candidates_for_trait_impls(trait_def_id, self.assemble_extension_candidates_for_trait_impls(trait_def_id,
method.clone(), item.clone(),
matching_index); matching_index);
try!(self.assemble_closure_candidates(trait_def_id, try!(self.assemble_closure_candidates(trait_def_id,
method.clone(), item.clone(),
matching_index)); matching_index));
self.assemble_projection_candidates(trait_def_id, self.assemble_projection_candidates(trait_def_id,
method.clone(), item.clone(),
matching_index); matching_index);
self.assemble_where_clause_candidates(trait_def_id, self.assemble_where_clause_candidates(trait_def_id,
method, item.clone(),
matching_index); matching_index);
Ok(()) Ok(())
@ -622,8 +621,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
fn assemble_extension_candidates_for_trait_impls(&mut self, fn assemble_extension_candidates_for_trait_impls(&mut self,
trait_def_id: ast::DefId, trait_def_id: ast::DefId,
method: Rc<ty::Method<'tcx>>, item: ty::ImplOrTraitItem<'tcx>,
method_index: usize) item_index: usize)
{ {
ty::populate_implementations_for_trait_if_necessary(self.tcx(), ty::populate_implementations_for_trait_if_necessary(self.tcx(),
trait_def_id); trait_def_id);
@ -657,7 +656,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
// Determine the receiver type that the method itself expects. // Determine the receiver type that the method itself expects.
let xform_self_ty = let xform_self_ty =
self.xform_self_ty(&method, self.xform_self_ty(&item,
impl_trait_ref.self_ty(), impl_trait_ref.self_ty(),
impl_trait_ref.substs); impl_trait_ref.substs);
@ -665,8 +664,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
self.extension_candidates.push(Candidate { self.extension_candidates.push(Candidate {
xform_self_ty: xform_self_ty, xform_self_ty: xform_self_ty,
method_ty: method.clone(), item: item.clone(),
kind: ExtensionImplCandidate(impl_def_id, impl_trait_ref, impl_substs, method_index) 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, fn assemble_closure_candidates(&mut self,
trait_def_id: ast::DefId, trait_def_id: ast::DefId,
method_ty: Rc<ty::Method<'tcx>>, item: ty::ImplOrTraitItem<'tcx>,
method_index: usize) item_index: usize)
-> Result<(),MethodError> -> Result<(),MethodError>
{ {
// Check if this is one of the Fn,FnMut,FnOnce traits. // Check if this is one of the Fn,FnMut,FnOnce traits.
@ -736,13 +735,13 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
&trait_def.generics, &trait_def.generics,
step.self_ty); 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, step.self_ty,
&substs); &substs);
self.inherent_candidates.push(Candidate { self.inherent_candidates.push(Candidate {
xform_self_ty: xform_self_ty, xform_self_ty: xform_self_ty,
method_ty: method_ty.clone(), item: item.clone(),
kind: ClosureCandidate(trait_def_id, method_index) kind: ClosureCandidate(trait_def_id, item_index)
}); });
} }
@ -751,16 +750,16 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
fn assemble_projection_candidates(&mut self, fn assemble_projection_candidates(&mut self,
trait_def_id: ast::DefId, trait_def_id: ast::DefId,
method: Rc<ty::Method<'tcx>>, item: ty::ImplOrTraitItem<'tcx>,
method_index: usize) item_index: usize)
{ {
debug!("assemble_projection_candidates(\ debug!("assemble_projection_candidates(\
trait_def_id={}, \ trait_def_id={}, \
method={}, \ item={}, \
method_index={})", item_index={})",
trait_def_id.repr(self.tcx()), trait_def_id.repr(self.tcx()),
method.repr(self.tcx()), item.repr(self.tcx()),
method_index); item_index);
for step in &*self.steps { for step in &*self.steps {
debug!("assemble_projection_candidates: step={}", debug!("assemble_projection_candidates: step={}",
@ -792,7 +791,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
bound.repr(self.tcx())); bound.repr(self.tcx()));
if self.infcx().can_equate(&step.self_ty, &bound.self_ty()).is_ok() { 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.self_ty(),
bound.substs); bound.substs);
@ -802,8 +801,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
self.extension_candidates.push(Candidate { self.extension_candidates.push(Candidate {
xform_self_ty: xform_self_ty, xform_self_ty: xform_self_ty,
method_ty: method.clone(), item: item.clone(),
kind: ProjectionCandidate(trait_def_id, method_index) kind: ProjectionCandidate(trait_def_id, item_index)
}); });
} }
} }
@ -812,8 +811,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
fn assemble_where_clause_candidates(&mut self, fn assemble_where_clause_candidates(&mut self,
trait_def_id: ast::DefId, trait_def_id: ast::DefId,
method_ty: Rc<ty::Method<'tcx>>, item: ty::ImplOrTraitItem<'tcx>,
method_index: usize) item_index: usize)
{ {
debug!("assemble_where_clause_candidates(trait_def_id={})", debug!("assemble_where_clause_candidates(trait_def_id={})",
trait_def_id.repr(self.tcx())); trait_def_id.repr(self.tcx()));
@ -824,7 +823,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
.filter(|b| b.def_id() == trait_def_id) .filter(|b| b.def_id() == trait_def_id)
{ {
let bound = self.erase_late_bound_regions(&poly_bound); 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.self_ty(),
bound.substs); bound.substs);
@ -834,8 +833,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
self.extension_candidates.push(Candidate { self.extension_candidates.push(Candidate {
xform_self_ty: xform_self_ty, xform_self_ty: xform_self_ty,
method_ty: method_ty.clone(), item: item.clone(),
kind: WhereClauseCandidate(poly_bound, method_index) kind: WhereClauseCandidate(poly_bound, item_index)
}); });
} }
} }
@ -860,7 +859,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
try!(self.assemble_extension_candidates_for_all_traits()); try!(self.assemble_extension_candidates_for_all_traits());
let out_of_scope_traits = match self.pick_core() { 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| { Some(Err(MethodError::Ambiguity(v))) => v.into_iter().map(|source| {
match source { match source {
TraitSource(id) => id, TraitSource(id) => id,
@ -1099,11 +1098,11 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
} }
// If so, just use this trait and call it a day. // If so, just use this trait and call it a day.
let (trait_def_id, method_num) = trait_data; let (trait_def_id, item_num) = trait_data;
let method_ty = probes[0].method_ty.clone(); let item = probes[0].item.clone();
Some(Pick { Some(Pick {
method_ty: method_ty, item: item,
kind: TraitPick(trait_def_id, method_num), kind: TraitPick(trait_def_id, item_num),
autoderefs: 0, autoderefs: 0,
autoref: None, autoref: None,
unsize: None unsize: None
@ -1117,28 +1116,25 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
self.infcx().sub_types(false, infer::Misc(DUMMY_SP), sub, sup) 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 // "fast track" -- check for usage of sugar
match *item {
ty::ImplOrTraitItem::MethodTraitItem(ref method) =>
match method.explicit_self { match method.explicit_self {
ty::StaticExplicitSelfCategory => { ty::StaticExplicitSelfCategory => self.mode == Mode::Path,
if self.mode == Mode::Path {
return true;
}
}
ty::ByValueExplicitSelfCategory | ty::ByValueExplicitSelfCategory |
ty::ByReferenceExplicitSelfCategory(..) | ty::ByReferenceExplicitSelfCategory(..) |
ty::ByBoxExplicitSelfCategory => { ty::ByBoxExplicitSelfCategory => true,
return true; },
ty::ImplOrTraitItem::ConstTraitItem(..) => self.mode == Mode::Path,
_ => false,
} }
}
// FIXME -- check for types that deref to `Self`, // FIXME -- check for types that deref to `Self`,
// like `Rc<Self>` and so on. // like `Rc<Self>` and so on.
// //
// Note also that the current code will break if this type // Note also that the current code will break if this type
// includes any of the type parameters defined on the method // includes any of the type parameters defined on the method
// -- but this could be overcome. // -- but this could be overcome.
return false;
} }
fn record_static_candidate(&mut self, source: CandidateSource) { fn record_static_candidate(&mut self, source: CandidateSource) {
@ -1146,6 +1142,19 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
} }
fn xform_self_ty(&self, fn xform_self_ty(&self,
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<ty::Method<'tcx>>, method: &Rc<ty::Method<'tcx>>,
impl_ty: Ty<'tcx>, impl_ty: Ty<'tcx>,
substs: &subst::Substs<'tcx>) substs: &subst::Substs<'tcx>)
@ -1245,46 +1254,45 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
} }
} }
fn impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, fn impl_item<'tcx>(tcx: &ty::ctxt<'tcx>,
impl_def_id: ast::DefId, impl_def_id: ast::DefId,
method_name: ast::Name) item_name: ast::Name)
-> Option<Rc<ty::Method<'tcx>>> -> Option<ty::ImplOrTraitItem<'tcx>>
{ {
let impl_items = tcx.impl_items.borrow(); let impl_items = tcx.impl_items.borrow();
let impl_items = impl_items.get(&impl_def_id).unwrap(); let impl_items = impl_items.get(&impl_def_id).unwrap();
impl_items impl_items
.iter() .iter()
.map(|&did| ty::impl_or_trait_item(tcx, did.def_id())) .map(|&did| ty::impl_or_trait_item(tcx, did.def_id()))
.find(|m| m.name() == method_name) .find(|item| item.name() == item_name)
.and_then(|item| item.as_opt_method())
} }
/// Find method with name `method_name` defined in `trait_def_id` and return it, along with its /// Find item with name `item_name` defined in `trait_def_id` and return it,
/// index (or `None`, if no such method). /// along with its index (or `None`, if no such item).
fn trait_method<'tcx>(tcx: &ty::ctxt<'tcx>, fn trait_item<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_def_id: ast::DefId, trait_def_id: ast::DefId,
method_name: ast::Name) item_name: ast::Name)
-> Option<(usize, Rc<ty::Method<'tcx>>)> -> Option<(usize, ty::ImplOrTraitItem<'tcx>)>
{ {
let trait_items = ty::trait_items(tcx, trait_def_id); let trait_items = ty::trait_items(tcx, trait_def_id);
debug!("trait_method; items: {:?}", trait_items); debug!("trait_method; items: {:?}", trait_items);
trait_items trait_items
.iter() .iter()
.enumerate() .enumerate()
.find(|&(_, ref item)| item.name() == method_name) .find(|&(_, ref item)| item.name() == item_name)
.and_then(|(idx, item)| item.as_opt_method().map(|m| (idx, m))) .map(|(num, ref item)| (num, (*item).clone()))
} }
impl<'tcx> Candidate<'tcx> { impl<'tcx> Candidate<'tcx> {
fn to_unadjusted_pick(&self) -> Pick<'tcx> { fn to_unadjusted_pick(&self) -> Pick<'tcx> {
Pick { Pick {
method_ty: self.method_ty.clone(), item: self.item.clone(),
kind: match self.kind { kind: match self.kind {
InherentImplCandidate(def_id, _) => { InherentImplCandidate(def_id, _) => {
InherentImplPick(def_id) InherentImplPick(def_id)
} }
ObjectCandidate(def_id, method_num, real_index) => { ObjectCandidate(def_id, item_num, real_index) => {
ObjectPick(def_id, method_num, real_index) ObjectPick(def_id, item_num, real_index)
} }
ExtensionImplCandidate(def_id, _, _, index) => { ExtensionImplCandidate(def_id, _, _, index) => {
ExtensionImplPick(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 { match self.kind {
InherentImplCandidate(..) => { InherentImplCandidate(..) => {
None None
} }
ObjectCandidate(trait_def_id, method_num, _) => { ObjectCandidate(trait_def_id, item_num, _) => {
Some((trait_def_id, method_num)) Some((trait_def_id, item_num))
} }
ClosureCandidate(trait_def_id, method_num) => { ClosureCandidate(trait_def_id, item_num) => {
Some((trait_def_id, method_num)) Some((trait_def_id, item_num))
} }
ExtensionImplCandidate(_, ref trait_ref, _, method_num) => { ExtensionImplCandidate(_, ref trait_ref, _, item_num) => {
Some((trait_ref.def_id, method_num)) Some((trait_ref.def_id, item_num))
} }
WhereClauseCandidate(ref trait_ref, method_num) => { WhereClauseCandidate(ref trait_ref, item_num) => {
Some((trait_ref.def_id(), method_num)) Some((trait_ref.def_id(), item_num))
} }
ProjectionCandidate(trait_def_id, method_num) => { ProjectionCandidate(trait_def_id, item_num) => {
Some((trait_def_id, method_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> { impl<'tcx> Repr<'tcx> for Pick<'tcx> {
fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
format!("Pick(method_ty={}, autoderefs={}, format!("Pick(item={}, autoderefs={},
autoref={}, unsize={}, kind={:?})", autoref={}, unsize={}, kind={:?})",
self.method_ty.repr(tcx), self.item.repr(tcx),
self.autoderefs, self.autoderefs,
self.autoref.repr(tcx), self.autoref.repr(tcx),
self.unsize.repr(tcx), self.unsize.repr(tcx),

View file

@ -78,7 +78,7 @@ type parameter).
pub use self::LvaluePreference::*; pub use self::LvaluePreference::*;
pub use self::Expectation::*; 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 self::TupleArgumentsFlag::*;
use astconv::{self, ast_region_to_region, ast_ty_to_ty, AstConv, PathParamMode}; use astconv::{self, ast_region_to_region, ast_ty_to_ty, AstConv, PathParamMode};
@ -807,6 +807,9 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
for impl_item in impl_items { for impl_item in impl_items {
match impl_item.node { match impl_item.node {
ast::ConstImplItem(_, ref expr) => {
check_const(ccx, impl_item.span, &*expr, impl_item.id)
}
ast::MethodImplItem(ref sig, ref body) => { ast::MethodImplItem(ref sig, ref body) => {
check_method_body(ccx, &impl_pty.generics, sig, body, check_method_body(ccx, &impl_pty.generics, sig, body,
impl_item.id, impl_item.span); impl_item.id, impl_item.span);
@ -822,14 +825,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)); let trait_def = ty::lookup_trait_def(ccx.tcx, local_def(it.id));
for trait_item in trait_items { for trait_item in trait_items {
match trait_item.node { match trait_item.node {
ast::MethodTraitItem(_, None) => { ast::ConstTraitItem(_, Some(ref expr)) => {
// Nothing to do, since required methods don't have check_const(ccx, trait_item.span, &*expr, trait_item.id)
// bodies to check.
} }
ast::MethodTraitItem(ref sig, Some(ref body)) => { ast::MethodTraitItem(ref sig, Some(ref body)) => {
check_method_body(ccx, &trait_def.generics, sig, body, check_method_body(ccx, &trait_def.generics, sig, body,
trait_item.id, trait_item.span); trait_item.id, trait_item.span);
} }
ast::ConstTraitItem(_, None) |
ast::MethodTraitItem(_, None) |
ast::TypeTraitItem(..) => { ast::TypeTraitItem(..) => {
// Nothing to do. // Nothing to do.
} }
@ -919,6 +923,48 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
// and compatible with trait signature // and compatible with trait signature
for impl_item in impl_items { for impl_item in impl_items {
match impl_item.node { match impl_item.node {
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) => { ast::MethodImplItem(_, ref body) => {
let impl_method_def_id = local_def(impl_item.id); let impl_method_def_id = local_def(impl_item.id);
let impl_item_ty = ty::impl_or_trait_item(ccx.tcx, let impl_item_ty = ty::impl_or_trait_item(ccx.tcx,
@ -942,13 +988,11 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
&*impl_trait_ref); &*impl_trait_ref);
} }
_ => { _ => {
// This is span_bug as it should have already been span_err!(tcx.sess, impl_item.span, E0324,
// caught in resolve. "item `{}` is an associated method, \
tcx.sess.span_bug( which doesn't match its trait `{}`",
impl_item.span,
&format!("item `{}` is of a different kind from its trait `{}`",
token::get_name(impl_item_ty.name()), token::get_name(impl_item_ty.name()),
impl_trait_ref.repr(tcx))); impl_trait_ref.repr(tcx))
} }
} }
} }
@ -978,13 +1022,11 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
match (associated_type, &typedef_ty) { match (associated_type, &typedef_ty) {
(&ty::TypeTraitItem(_), &ty::TypeTraitItem(_)) => {} (&ty::TypeTraitItem(_), &ty::TypeTraitItem(_)) => {}
_ => { _ => {
// This is `span_bug` as it should have span_err!(tcx.sess, impl_item.span, E0325,
// already been caught in resolve. "item `{}` is an associated type, \
tcx.sess.span_bug( which doesn't match its trait `{}`",
impl_item.span,
&format!("item `{}` is of a different kind from its trait `{}`",
token::get_name(typedef_ty.name()), token::get_name(typedef_ty.name()),
impl_trait_ref.repr(tcx))); impl_trait_ref.repr(tcx))
} }
} }
} }
@ -1008,9 +1050,27 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
// Check for missing items from trait // Check for missing items from trait
let provided_methods = ty::provided_trait_methods(tcx, impl_trait_ref.def_id); let provided_methods = ty::provided_trait_methods(tcx, impl_trait_ref.def_id);
let associated_consts = ty::associated_consts(tcx, impl_trait_ref.def_id);
let mut missing_methods = Vec::new(); let mut missing_methods = Vec::new();
for trait_item in &*trait_items { for trait_item in &*trait_items {
match *trait_item { match *trait_item {
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) => { ty::MethodTraitItem(ref trait_method) => {
let is_implemented = let is_implemented =
impl_items.iter().any(|ii| { impl_items.iter().any(|ii| {
@ -1018,8 +1078,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
ast::MethodImplItem(..) => { ast::MethodImplItem(..) => {
ii.ident.name == trait_method.name ii.ident.name == trait_method.name
} }
ast::TypeImplItem(_) | _ => false,
ast::MacImplItem(_) => false,
} }
}); });
let is_provided = let is_provided =
@ -1034,8 +1093,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
ast::TypeImplItem(_) => { ast::TypeImplItem(_) => {
ii.ident.name == associated_type.name ii.ident.name == associated_type.name
} }
ast::MethodImplItem(..) | _ => false,
ast::MacImplItem(_) => false,
} }
}); });
if !is_implemented { if !is_implemented {
@ -3171,53 +3229,20 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
&format!("unbound path {}", expr.repr(tcx))) &format!("unbound path {}", expr.repr(tcx)))
}; };
let def = path_res.base_def; if let Some((opt_ty, segments, def)) =
if path_res.depth == 0 { 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, let (scheme, predicates) = type_scheme_and_predicates_for_def(fcx,
expr.span, expr.span,
def); def);
instantiate_path(fcx, instantiate_path(fcx,
&path.segments, segments,
scheme, scheme,
&predicates, &predicates,
opt_self_ty, opt_ty,
def, def,
expr.span, expr.span,
id); 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 // We always require that the type provided as the value for
@ -3679,6 +3704,52 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
unifier(); unifier();
} }
pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>,
path_res: def::PathResolution,
opt_self_ty: Option<Ty<'tcx>>,
path: &'a ast::Path,
span: Span,
node_id: ast::NodeId)
-> Option<(Option<Ty<'tcx>>,
&'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, fn constrain_path_type_parameters(fcx: &FnCtxt,
expr: &ast::Expr) expr: &ast::Expr)
{ {
@ -4207,7 +4278,7 @@ fn type_scheme_and_predicates_for_def<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
} }
def::DefFn(id, _) | def::DefMethod(id, _) | def::DefFn(id, _) | def::DefMethod(id, _) |
def::DefStatic(id, _) | def::DefVariant(_, id, _) | def::DefStatic(id, _) | def::DefVariant(_, id, _) |
def::DefStruct(id) | def::DefConst(id) => { def::DefStruct(id) | def::DefConst(id) | def::DefAssociatedConst(id, _) => {
(ty::lookup_item_type(fcx.tcx(), id), ty::lookup_predicates(fcx.tcx(), id)) (ty::lookup_item_type(fcx.tcx(), id), ty::lookup_predicates(fcx.tcx(), id))
} }
def::DefTrait(_) | def::DefTrait(_) |
@ -4249,7 +4320,7 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
// Luckily, we can (at least for now) deduce the intermediate steps // Luckily, we can (at least for now) deduce the intermediate steps
// just from the end-point. // 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: // 1. Reference to a *type*, such as a struct or enum:
// //
@ -4299,6 +4370,16 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
// `SomeStruct::<A>`, contains parameters in TypeSpace, and the // `SomeStruct::<A>`, contains parameters in TypeSpace, and the
// final segment, `foo::<B>` contains parameters in fn space. // final segment, `foo::<B>` contains parameters in fn space.
// //
// 4. Reference to an *associated const*:
//
// impl<A> AnotherStruct<A> {
// const FOO: B = BAR;
// }
//
// The path in this case will look like
// `a::b::AnotherStruct::<A>::FOO`, so the penultimate segment
// only will have parameters in TypeSpace.
//
// The first step then is to categorize the segments appropriately. // The first step then is to categorize the segments appropriately.
assert!(!segments.is_empty()); assert!(!segments.is_empty());
@ -4350,6 +4431,23 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
} }
} }
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 // Other cases. Various nonsense that really shouldn't show up
// here. If they do, an error will have been reported // here. If they do, an error will have been reported
// elsewhere. (I hope) // elsewhere. (I hope)

View file

@ -20,8 +20,9 @@ use metadata::csearch::{each_impl, get_impl_trait};
use metadata::csearch; use metadata::csearch;
use middle::subst::{self, Subst}; use middle::subst::{self, Subst};
use middle::ty::RegionEscape; use middle::ty::RegionEscape;
use middle::ty::{ImplContainer, ImplOrTraitItemId, MethodTraitItemId}; use middle::ty::{ImplContainer, ImplOrTraitItemId, ConstTraitItemId};
use middle::ty::{ParameterEnvironment, TypeTraitItemId, lookup_item_type}; use middle::ty::{MethodTraitItemId, TypeTraitItemId};
use middle::ty::{ParameterEnvironment, lookup_item_type};
use middle::ty::{Ty, ty_bool, ty_char, ty_enum, ty_err}; use middle::ty::{Ty, ty_bool, ty_char, ty_enum, ty_err};
use middle::ty::{ty_param, TypeScheme, ty_ptr}; use middle::ty::{ty_param, TypeScheme, ty_ptr};
use middle::ty::{ty_rptr, ty_struct, ty_trait, ty_tup}; use middle::ty::{ty_rptr, ty_struct, ty_trait, ty_tup};
@ -278,6 +279,9 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
let mut items: Vec<ImplOrTraitItemId> = let mut items: Vec<ImplOrTraitItemId> =
impl_items.iter().map(|impl_item| { impl_items.iter().map(|impl_item| {
match impl_item.node { match impl_item.node {
ast::ConstImplItem(..) => {
ConstTraitItemId(local_def(impl_item.id))
}
ast::MethodImplItem(..) => { ast::MethodImplItem(..) => {
MethodTraitItemId(local_def(impl_item.id)) MethodTraitItemId(local_def(impl_item.id))
} }
@ -348,7 +352,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
.insert(item_def_id.def_id(), source); .insert(item_def_id.def_id(), source);
} }
} }
ty::TypeTraitItem(_) => {} _ => {}
} }
} }

View file

@ -197,7 +197,7 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> {
let def_id = local_def(method_id); let def_id = local_def(method_id);
match *self.tcx.impl_or_trait_items.borrow().get(&def_id).unwrap() { match *self.tcx.impl_or_trait_items.borrow().get(&def_id).unwrap() {
ty::MethodTraitItem(ref mty) => mty.clone(), ty::MethodTraitItem(ref mty) => mty.clone(),
ty::TypeTraitItem(..) => { _ => {
self.tcx.sess.bug(&format!("method with id {} has the wrong type", method_id)); self.tcx.sess.bug(&format!("method with id {} has the wrong type", method_id));
} }
} }
@ -692,6 +692,32 @@ 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>, fn as_refsociated_type<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
container: ImplOrTraitItemContainer, container: ImplOrTraitItemContainer,
ident: ast::Ident, ident: ast::Ident,
@ -829,10 +855,26 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
it.vis 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. // Convert all the associated types.
for impl_item in impl_items { for impl_item in impl_items {
match impl_item.node { if let ast::TypeImplItem(ref ty) = impl_item.node {
ast::TypeImplItem(ref ty) => {
if opt_trait_ref.is_none() { if opt_trait_ref.is_none() {
span_err!(tcx.sess, impl_item.span, E0202, span_err!(tcx.sess, impl_item.span, E0202,
"associated items are not allowed in inherent impls"); "associated items are not allowed in inherent impls");
@ -851,23 +893,18 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
ty::GenericPredicates::empty()); ty::GenericPredicates::empty());
write_ty_to_tcx(tcx, impl_item.id, typ); write_ty_to_tcx(tcx, impl_item.id, typ);
} }
ast::MethodImplItem(..) |
ast::MacImplItem(_) => {}
}
} }
let methods = impl_items.iter().filter_map(|ii| { let methods = impl_items.iter().filter_map(|ii| {
match ii.node { if let ast::MethodImplItem(ref sig, _) = ii.node {
ast::MethodImplItem(ref sig, _) => {
// if the method specifies a visibility, use that, otherwise // if the method specifies a visibility, use that, otherwise
// inherit the visibility from the impl (so `foo` in `pub impl // inherit the visibility from the impl (so `foo` in `pub impl
// { fn foo(); }` is public, but private in `priv impl { fn // { fn foo(); }` is public, but private in `priv impl { fn
// foo(); }`). // foo(); }`).
let method_vis = ii.vis.inherit_from(parent_visibility); let method_vis = ii.vis.inherit_from(parent_visibility);
Some((sig, ii.id, ii.ident, method_vis, ii.span)) Some((sig, ii.id, ii.ident, method_vis, ii.span))
} } else {
ast::TypeImplItem(_) | None
ast::MacImplItem(_) => None
} }
}); });
convert_methods(ccx, convert_methods(ccx,
@ -878,8 +915,7 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
&ty_predicates); &ty_predicates);
for impl_item in impl_items { for impl_item in impl_items {
match impl_item.node { if let ast::MethodImplItem(ref sig, ref body) = impl_item.node {
ast::MethodImplItem(ref sig, ref body) => {
let body_id = body.id; let body_id = body.id;
check_method_self_type(ccx, check_method_self_type(ccx,
&BindingRscope::new(), &BindingRscope::new(),
@ -888,9 +924,6 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
&sig.explicit_self, &sig.explicit_self,
body_id); body_id);
} }
ast::TypeImplItem(_) |
ast::MacImplItem(_) => {}
}
} }
if let Some(ref ast_trait_ref) = *opt_trait_ref { if let Some(ref ast_trait_ref) = *opt_trait_ref {
@ -920,18 +953,37 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
// Convert all the associated types. // Convert all the associated types.
for trait_item in trait_items { for trait_item in trait_items {
match trait_item.node { match trait_item.node {
ast::MethodTraitItem(..) => {} 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 {
ast::TypeTraitItem(..) => { ast::TypeTraitItem(..) => {
as_refsociated_type(ccx, TraitContainer(local_def(it.id)), as_refsociated_type(ccx, TraitContainer(local_def(it.id)),
trait_item.ident, trait_item.id, ast::Public); trait_item.ident, trait_item.id, ast::Public);
} }
_ => {}
} }
}; };
let methods = trait_items.iter().filter_map(|ti| { let methods = trait_items.iter().filter_map(|ti| {
let sig = match ti.node { let sig = match ti.node {
ast::MethodTraitItem(ref sig, _) => sig, ast::MethodTraitItem(ref sig, _) => sig,
ast::TypeTraitItem(..) => return None, _ => return None,
}; };
Some((sig, ti.id, ti.ident, ast::Inherited, ti.span)) Some((sig, ti.id, ti.ident, ast::Inherited, ti.span))
}); });
@ -948,6 +1000,9 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
let trait_item_def_ids = Rc::new(trait_items.iter().map(|trait_item| { let trait_item_def_ids = Rc::new(trait_items.iter().map(|trait_item| {
let def_id = local_def(trait_item.id); let def_id = local_def(trait_item.id);
match trait_item.node { match trait_item.node {
ast::ConstTraitItem(..) => {
ty::ConstTraitItemId(def_id)
}
ast::MethodTraitItem(..) => { ast::MethodTraitItem(..) => {
ty::MethodTraitItemId(def_id) ty::MethodTraitItemId(def_id)
} }
@ -963,7 +1018,7 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
for trait_item in trait_items { for trait_item in trait_items {
let sig = match trait_item.node { let sig = match trait_item.node {
ast::MethodTraitItem(ref sig, _) => sig, ast::MethodTraitItem(ref sig, _) => sig,
ast::TypeTraitItem(..) => continue _ => continue
}; };
check_method_self_type(ccx, check_method_self_type(ccx,
&BindingRscope::new(), &BindingRscope::new(),
@ -1186,8 +1241,8 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
let associated_type_names: Vec<_> = items.iter().filter_map(|trait_item| { let associated_type_names: Vec<_> = items.iter().filter_map(|trait_item| {
match trait_item.node { match trait_item.node {
ast::MethodTraitItem(..) => None,
ast::TypeTraitItem(..) => Some(trait_item.ident.name), ast::TypeTraitItem(..) => Some(trait_item.ident.name),
_ => None,
} }
}).collect(); }).collect();
@ -1261,7 +1316,7 @@ fn trait_defines_associated_type_named(ccx: &CrateCtxt,
trait_items.iter().any(|trait_item| { trait_items.iter().any(|trait_item| {
match trait_item.node { match trait_item.node {
ast::TypeTraitItem(..) => trait_item.ident.name == assoc_name, ast::TypeTraitItem(..) => trait_item.ident.name == assoc_name,
ast::MethodTraitItem(..) => false, _ => false,
} }
}) })
} }
@ -1321,7 +1376,7 @@ fn convert_trait_predicates<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, it: &ast::Item)
trait_items.iter().flat_map(|trait_item| { trait_items.iter().flat_map(|trait_item| {
let bounds = match trait_item.node { let bounds = match trait_item.node {
ast::TypeTraitItem(ref bounds, _) => bounds, ast::TypeTraitItem(ref bounds, _) => bounds,
ast::MethodTraitItem(..) => { _ => {
return vec!().into_iter(); return vec!().into_iter();
} }
}; };
@ -2237,7 +2292,8 @@ fn enforce_impl_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
impl_items.iter() impl_items.iter()
.filter_map(|item| match item.node { .filter_map(|item| match item.node {
ast::TypeImplItem(..) => Some(ty::node_id_to_type(tcx, item.id)), ast::TypeImplItem(..) => Some(ty::node_id_to_type(tcx, item.id)),
ast::MethodImplItem(..) | ast::MacImplItem(..) => None, ast::ConstImplItem(..) | ast::MethodImplItem(..) |
ast::MacImplItem(..) => None,
}) })
.flat_map(|ty| ctp::parameters_for_type(ty).into_iter()) .flat_map(|ty| ctp::parameters_for_type(ty).into_iter())
.filter_map(|p| match p { .filter_map(|p| match p {

View file

@ -176,6 +176,11 @@ register_diagnostics! {
E0320, // recursive overflow during dropck E0320, // recursive overflow during dropck
E0321, // extended coherence rules for defaulted traits violated E0321, // extended coherence rules for defaulted traits violated
E0322, // cannot implement Sized explicitly E0322, // cannot implement Sized explicitly
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
E0327, // referred to method instead of constant in match pattern
E0366, // dropck forbid specialization to concrete type or region E0366, // dropck forbid specialization to concrete type or region
E0367, // dropck forbid specialization to predicate not in struct/enum E0367, // dropck forbid specialization to predicate not in struct/enum
E0368, // binary operation `<op>=` cannot be applied to types E0368, // binary operation `<op>=` cannot be applied to types

View file

@ -22,12 +22,13 @@ use rustc::middle::def;
use rustc::middle::ty; use rustc::middle::ty;
use rustc::middle::subst; use rustc::middle::subst;
use rustc::middle::stability; use rustc::middle::stability;
use rustc::middle::const_eval;
use core::DocContext; use core::DocContext;
use doctree; use doctree;
use clean; use clean;
use super::Clean; use super::{Clean, ToSource};
/// Attempt to inline the definition of a local node id into this AST. /// 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); record_extern_fqn(cx, did, clean::TypeStatic);
clean::StaticItem(build_static(cx, tcx, did, mtbl)) clean::StaticItem(build_static(cx, tcx, did, mtbl))
} }
def::DefConst(did) => { def::DefConst(did) | def::DefAssociatedConst(did, _) => {
record_extern_fqn(cx, did, clean::TypeConst); record_extern_fqn(cx, did, clean::TypeConst);
clean::ConstantItem(build_const(cx, tcx, did)) clean::ConstantItem(build_const(cx, tcx, did))
} }
@ -312,6 +313,27 @@ pub fn build_impl(cx: &DocContext,
let did = did.def_id(); let did = did.def_id();
let impl_item = ty::impl_or_trait_item(tcx, did); let impl_item = ty::impl_or_trait_item(tcx, did);
match impl_item { match impl_item {
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) => { ty::MethodTraitItem(method) => {
if method.vis != ast::Public && associated_trait.is_none() { if method.vis != ast::Public && associated_trait.is_none() {
return None return None
@ -443,7 +465,7 @@ fn build_const(cx: &DocContext, tcx: &ty::ctxt,
use rustc::middle::const_eval; use rustc::middle::const_eval;
use syntax::print::pprust; 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); panic!("expected lookup_const_by_id to succeed for {:?}", did);
}); });
debug!("converting constant expr {:?} to snippet", expr); debug!("converting constant expr {:?} to snippet", expr);

View file

@ -361,6 +361,7 @@ pub enum ItemEnum {
ForeignStaticItem(Static), ForeignStaticItem(Static),
MacroItem(Macro), MacroItem(Macro),
PrimitiveItem(PrimitiveType), PrimitiveItem(PrimitiveType),
AssociatedConstItem(Type, Option<String>),
AssociatedTypeItem(Vec<TyParamBound>, Option<Type>), AssociatedTypeItem(Vec<TyParamBound>, Option<Type>),
DefaultImplItem(DefaultImpl), DefaultImplItem(DefaultImpl),
} }
@ -1235,6 +1236,11 @@ impl Clean<PolyTrait> for ast::PolyTraitRef {
impl Clean<Item> for ast::TraitItem { impl Clean<Item> for ast::TraitItem {
fn clean(&self, cx: &DocContext) -> Item { fn clean(&self, cx: &DocContext) -> Item {
let inner = match self.node { let inner = match self.node {
ast::ConstTraitItem(ref ty, ref default) => {
AssociatedConstItem(ty.clean(cx),
default.as_ref().map(|expr|
expr.span.to_src(cx)))
}
ast::MethodTraitItem(ref sig, Some(_)) => { ast::MethodTraitItem(ref sig, Some(_)) => {
MethodItem(sig.clean(cx)) MethodItem(sig.clean(cx))
} }
@ -1260,6 +1266,12 @@ impl Clean<Item> for ast::TraitItem {
impl Clean<Item> for ast::ImplItem { impl Clean<Item> for ast::ImplItem {
fn clean(&self, cx: &DocContext) -> Item { fn clean(&self, cx: &DocContext) -> Item {
let inner = match self.node { let inner = match self.node {
ast::ConstImplItem(ref ty, ref expr) => {
ConstantItem(Constant{
type_: ty.clean(cx),
expr: expr.span.to_src(cx),
})
}
ast::MethodImplItem(ref sig, _) => { ast::MethodImplItem(ref sig, _) => {
MethodItem(sig.clean(cx)) MethodItem(sig.clean(cx))
} }
@ -1363,6 +1375,7 @@ impl<'tcx> Clean<Item> for ty::Method<'tcx> {
impl<'tcx> Clean<Item> for ty::ImplOrTraitItem<'tcx> { impl<'tcx> Clean<Item> for ty::ImplOrTraitItem<'tcx> {
fn clean(&self, cx: &DocContext) -> Item { fn clean(&self, cx: &DocContext) -> Item {
match *self { match *self {
ty::ConstTraitItem(ref cti) => cti.clean(cx),
ty::MethodTraitItem(ref mti) => mti.clean(cx), ty::MethodTraitItem(ref mti) => mti.clean(cx),
ty::TypeTraitItem(ref tti) => tti.clean(cx), ty::TypeTraitItem(ref tti) => tti.clean(cx),
} }
@ -2509,6 +2522,8 @@ fn name_from_pat(p: &ast::Pat) -> String {
PatWild(PatWildMulti) => "..".to_string(), PatWild(PatWildMulti) => "..".to_string(),
PatIdent(_, ref p, _) => token::get_ident(p.node).to_string(), PatIdent(_, ref p, _) => token::get_ident(p.node).to_string(),
PatEnum(ref p, _) => path_to_string(p), 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) => { PatStruct(ref name, ref fields, etc) => {
format!("{} {{ {}{} }}", path_to_string(name), format!("{} {{ {}{} }}", path_to_string(name),
fields.iter().map(|&Spanned { node: ref fp, .. }| fields.iter().map(|&Spanned { node: ref fp, .. }|
@ -2672,6 +2687,20 @@ impl Clean<Stability> for attr::Stability {
} }
} }
impl<'tcx> Clean<Item> for ty::AssociatedConst<'tcx> {
fn clean(&self, cx: &DocContext) -> Item {
Item {
source: DUMMY_SP.clean(cx),
name: Some(self.name.clean(cx)),
attrs: Vec::new(),
inner: AssociatedConstItem(self.ty.clean(cx), None),
visibility: None,
def_id: self.def_id,
stability: None,
}
}
}
impl Clean<Item> for ty::AssociatedType { impl Clean<Item> for ty::AssociatedType {
fn clean(&self, cx: &DocContext) -> Item { fn clean(&self, cx: &DocContext) -> Item {
// When loading a cross-crate associated type, the bounds for this type // When loading a cross-crate associated type, the bounds for this type

View file

@ -39,6 +39,7 @@ pub enum ItemType {
Primitive = 15, Primitive = 15,
AssociatedType = 16, AssociatedType = 16,
Constant = 17, Constant = 17,
AssociatedConst = 18,
} }
impl ItemType { impl ItemType {
@ -63,6 +64,7 @@ impl ItemType {
clean::ForeignStaticItem(..) => ItemType::Static, // no ForeignStatic clean::ForeignStaticItem(..) => ItemType::Static, // no ForeignStatic
clean::MacroItem(..) => ItemType::Macro, clean::MacroItem(..) => ItemType::Macro,
clean::PrimitiveItem(..) => ItemType::Primitive, clean::PrimitiveItem(..) => ItemType::Primitive,
clean::AssociatedConstItem(..) => ItemType::AssociatedConst,
clean::AssociatedTypeItem(..) => ItemType::AssociatedType, clean::AssociatedTypeItem(..) => ItemType::AssociatedType,
clean::DefaultImplItem(..) => ItemType::Impl, clean::DefaultImplItem(..) => ItemType::Impl,
} }
@ -102,6 +104,7 @@ impl ItemType {
ItemType::Primitive => "primitive", ItemType::Primitive => "primitive",
ItemType::AssociatedType => "associatedtype", ItemType::AssociatedType => "associatedtype",
ItemType::Constant => "constant", ItemType::Constant => "constant",
ItemType::AssociatedConst => "associatedconstant",
} }
} }
} }

View file

@ -1629,6 +1629,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
ItemType::Macro => ("macros", "Macros"), ItemType::Macro => ("macros", "Macros"),
ItemType::Primitive => ("primitives", "Primitive Types"), ItemType::Primitive => ("primitives", "Primitive Types"),
ItemType::AssociatedType => ("associated-types", "Associated Types"), ItemType::AssociatedType => ("associated-types", "Associated Types"),
ItemType::AssociatedConst => ("associated-consts", "Associated Constants"),
}; };
try!(write!(w, try!(write!(w,
"<h2 id='{id}' class='section-header'>\ "<h2 id='{id}' class='section-header'>\
@ -1799,7 +1800,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
try!(write!(w, "{{\n")); try!(write!(w, "{{\n"));
for t in &types { for t in &types {
try!(write!(w, " ")); try!(write!(w, " "));
try!(render_method(w, t, MethodLink::Anchor)); try!(render_assoc_item(w, t, AssocItemLink::Anchor));
try!(write!(w, ";\n")); try!(write!(w, ";\n"));
} }
if !types.is_empty() && !required.is_empty() { if !types.is_empty() && !required.is_empty() {
@ -1807,7 +1808,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
} }
for m in &required { for m in &required {
try!(write!(w, " ")); try!(write!(w, " "));
try!(render_method(w, m, MethodLink::Anchor)); try!(render_assoc_item(w, m, AssocItemLink::Anchor));
try!(write!(w, ";\n")); try!(write!(w, ";\n"));
} }
if !required.is_empty() && !provided.is_empty() { if !required.is_empty() && !provided.is_empty() {
@ -1815,7 +1816,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
} }
for m in &provided { for m in &provided {
try!(write!(w, " ")); try!(write!(w, " "));
try!(render_method(w, m, MethodLink::Anchor)); try!(render_assoc_item(w, m, AssocItemLink::Anchor));
try!(write!(w, " {{ ... }}\n")); try!(write!(w, " {{ ... }}\n"));
} }
try!(write!(w, "}}")); try!(write!(w, "}}"));
@ -1831,7 +1832,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
ty = shortty(m), ty = shortty(m),
name = *m.name.as_ref().unwrap(), name = *m.name.as_ref().unwrap(),
stab = m.stability_class())); stab = m.stability_class()));
try!(render_method(w, m, MethodLink::Anchor)); try!(render_assoc_item(w, m, AssocItemLink::Anchor));
try!(write!(w, "</code></h3>")); try!(write!(w, "</code></h3>"));
try!(document(w, m)); try!(document(w, m));
Ok(()) Ok(())
@ -1871,7 +1872,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
} }
// If there are methods directly on this trait object, render them here. // If there are methods directly on this trait object, render them here.
try!(render_methods(w, it.def_id, MethodRender::All)); try!(render_assoc_items(w, it.def_id, AssocItemRender::All));
let cache = cache(); let cache = cache();
try!(write!(w, " try!(write!(w, "
@ -1903,6 +1904,17 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
Ok(()) Ok(())
} }
fn assoc_const(w: &mut fmt::Formatter, it: &clean::Item,
ty: &clean::Type, default: &Option<String>)
-> 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, fn assoc_type(w: &mut fmt::Formatter, it: &clean::Item,
bounds: &Vec<clean::TyParamBound>, bounds: &Vec<clean::TyParamBound>,
default: &Option<clean::Type>) default: &Option<clean::Type>)
@ -1917,19 +1929,19 @@ fn assoc_type(w: &mut fmt::Formatter, it: &clean::Item,
Ok(()) Ok(())
} }
fn render_method(w: &mut fmt::Formatter, meth: &clean::Item, fn render_assoc_item(w: &mut fmt::Formatter, meth: &clean::Item,
link: MethodLink) -> fmt::Result { link: AssocItemLink) -> fmt::Result {
fn method(w: &mut fmt::Formatter, it: &clean::Item, fn method(w: &mut fmt::Formatter, it: &clean::Item,
unsafety: ast::Unsafety, abi: abi::Abi, unsafety: ast::Unsafety, abi: abi::Abi,
g: &clean::Generics, selfty: &clean::SelfTy, g: &clean::Generics, selfty: &clean::SelfTy,
d: &clean::FnDecl, link: MethodLink) -> fmt::Result { d: &clean::FnDecl, link: AssocItemLink) -> fmt::Result {
use syntax::abi::Abi; use syntax::abi::Abi;
let name = it.name.as_ref().unwrap(); let name = it.name.as_ref().unwrap();
let anchor = format!("#{}.{}", shortty(it), name); let anchor = format!("#{}.{}", shortty(it), name);
let href = match link { let href = match link {
MethodLink::Anchor => anchor, AssocItemLink::Anchor => anchor,
MethodLink::GotoSource(did) => { AssocItemLink::GotoSource(did) => {
href(did).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor) href(did).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
} }
}; };
@ -1958,10 +1970,13 @@ fn render_method(w: &mut fmt::Formatter, meth: &clean::Item,
method(w, meth, m.unsafety, m.abi, &m.generics, &m.self_, &m.decl, method(w, meth, m.unsafety, m.abi, &m.generics, &m.self_, &m.decl,
link) link)
} }
clean::AssociatedConstItem(ref ty, ref default) => {
assoc_const(w, meth, ty, default)
}
clean::AssociatedTypeItem(ref bounds, ref default) => { clean::AssociatedTypeItem(ref bounds, ref default) => {
assoc_type(w, meth, bounds, default) assoc_type(w, meth, bounds, default)
} }
_ => panic!("render_method called on non-method") _ => panic!("render_assoc_item called on non-associated-item")
} }
} }
@ -2001,7 +2016,7 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item,
try!(write!(w, "</table>")); try!(write!(w, "</table>"));
} }
} }
render_methods(w, it.def_id, MethodRender::All) render_assoc_items(w, it.def_id, AssocItemRender::All)
} }
fn item_enum(w: &mut fmt::Formatter, it: &clean::Item, fn item_enum(w: &mut fmt::Formatter, it: &clean::Item,
@ -2100,7 +2115,7 @@ fn item_enum(w: &mut fmt::Formatter, it: &clean::Item,
try!(write!(w, "</table>")); try!(write!(w, "</table>"));
} }
try!(render_methods(w, it.def_id, MethodRender::All)); try!(render_assoc_items(w, it.def_id, AssocItemRender::All));
Ok(()) Ok(())
} }
@ -2184,19 +2199,19 @@ fn render_struct(w: &mut fmt::Formatter, it: &clean::Item,
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
enum MethodLink { enum AssocItemLink {
Anchor, Anchor,
GotoSource(ast::DefId), GotoSource(ast::DefId),
} }
enum MethodRender<'a> { enum AssocItemRender<'a> {
All, All,
DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type }, DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type },
} }
fn render_methods(w: &mut fmt::Formatter, fn render_assoc_items(w: &mut fmt::Formatter,
it: ast::DefId, it: ast::DefId,
what: MethodRender) -> fmt::Result { what: AssocItemRender) -> fmt::Result {
let c = cache(); let c = cache();
let v = match c.impls.get(&it) { let v = match c.impls.get(&it) {
Some(v) => v, Some(v) => v,
@ -2207,21 +2222,21 @@ fn render_methods(w: &mut fmt::Formatter,
}); });
if !non_trait.is_empty() { if !non_trait.is_empty() {
let render_header = match what { let render_header = match what {
MethodRender::All => { AssocItemRender::All => {
try!(write!(w, "<h2 id='methods'>Methods</h2>")); try!(write!(w, "<h2 id='methods'>Methods</h2>"));
true true
} }
MethodRender::DerefFor { trait_, type_ } => { AssocItemRender::DerefFor { trait_, type_ } => {
try!(write!(w, "<h2 id='deref-methods'>Methods from \ try!(write!(w, "<h2 id='deref-methods'>Methods from \
{}&lt;Target={}&gt;</h2>", trait_, type_)); {}&lt;Target={}&gt;</h2>", trait_, type_));
false false
} }
}; };
for i in &non_trait { for i in &non_trait {
try!(render_impl(w, i, MethodLink::Anchor, render_header)); try!(render_impl(w, i, AssocItemLink::Anchor, render_header));
} }
} }
if let MethodRender::DerefFor { .. } = what { if let AssocItemRender::DerefFor { .. } = what {
return Ok(()) return Ok(())
} }
if !traits.is_empty() { if !traits.is_empty() {
@ -2243,7 +2258,7 @@ fn render_methods(w: &mut fmt::Formatter,
}); });
for i in &manual { for i in &manual {
let did = i.trait_did().unwrap(); let did = i.trait_did().unwrap();
try!(render_impl(w, i, MethodLink::GotoSource(did), true)); try!(render_impl(w, i, AssocItemLink::GotoSource(did), true));
} }
if !derived.is_empty() { if !derived.is_empty() {
try!(write!(w, "<h3 id='derived_implementations'>\ try!(write!(w, "<h3 id='derived_implementations'>\
@ -2251,7 +2266,7 @@ fn render_methods(w: &mut fmt::Formatter,
</h3>")); </h3>"));
for i in &derived { for i in &derived {
let did = i.trait_did().unwrap(); let did = i.trait_did().unwrap();
try!(render_impl(w, i, MethodLink::GotoSource(did), true)); try!(render_impl(w, i, AssocItemLink::GotoSource(did), true));
} }
} }
} }
@ -2266,14 +2281,14 @@ fn render_deref_methods(w: &mut fmt::Formatter, impl_: &Impl) -> fmt::Result {
_ => None, _ => None,
} }
}).next().unwrap(); }).next().unwrap();
let what = MethodRender::DerefFor { trait_: deref_type, type_: target }; let what = AssocItemRender::DerefFor { trait_: deref_type, type_: target };
match *target { match *target {
clean::ResolvedPath { did, .. } => render_methods(w, did, what), clean::ResolvedPath { did, .. } => render_assoc_items(w, did, what),
_ => { _ => {
if let Some(prim) = target.primitive_type() { if let Some(prim) = target.primitive_type() {
if let Some(c) = cache().primitive_locations.get(&prim) { if let Some(c) = cache().primitive_locations.get(&prim) {
let did = ast::DefId { krate: *c, node: prim.to_node_id() }; let did = ast::DefId { krate: *c, node: prim.to_node_id() };
try!(render_methods(w, did, what)); try!(render_assoc_items(w, did, what));
} }
} }
Ok(()) Ok(())
@ -2281,7 +2296,7 @@ fn render_deref_methods(w: &mut fmt::Formatter, impl_: &Impl) -> fmt::Result {
} }
} }
fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink, fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: AssocItemLink,
render_header: bool) -> fmt::Result { render_header: bool) -> fmt::Result {
if render_header { if render_header {
try!(write!(w, "<h3 class='impl'><code>impl{} ", try!(write!(w, "<h3 class='impl'><code>impl{} ",
@ -2300,13 +2315,13 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink,
} }
fn doctraititem(w: &mut fmt::Formatter, item: &clean::Item, fn doctraititem(w: &mut fmt::Formatter, item: &clean::Item,
link: MethodLink) -> fmt::Result { link: AssocItemLink) -> fmt::Result {
match item.inner { match item.inner {
clean::MethodItem(..) | clean::TyMethodItem(..) => { clean::MethodItem(..) | clean::TyMethodItem(..) => {
try!(write!(w, "<h4 id='method.{}' class='{}'><code>", try!(write!(w, "<h4 id='method.{}' class='{}'><code>",
*item.name.as_ref().unwrap(), *item.name.as_ref().unwrap(),
shortty(item))); shortty(item)));
try!(render_method(w, item, link)); try!(render_assoc_item(w, item, link));
try!(write!(w, "</code></h4>\n")); try!(write!(w, "</code></h4>\n"));
} }
clean::TypedefItem(ref tydef) => { clean::TypedefItem(ref tydef) => {
@ -2317,6 +2332,14 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink,
try!(write!(w, "type {} = {}", name, tydef.type_)); try!(write!(w, "type {} = {}", name, tydef.type_));
try!(write!(w, "</code></h4>\n")); try!(write!(w, "</code></h4>\n"));
} }
clean::AssociatedConstItem(ref ty, ref default) => {
let name = item.name.as_ref().unwrap();
try!(write!(w, "<h4 id='assoc_const.{}' class='{}'><code>",
*name,
shortty(item)));
try!(assoc_const(w, item, ty, default));
try!(write!(w, "</code></h4>\n"));
}
clean::AssociatedTypeItem(ref bounds, ref default) => { clean::AssociatedTypeItem(ref bounds, ref default) => {
let name = item.name.as_ref().unwrap(); let name = item.name.as_ref().unwrap();
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'><code>", try!(write!(w, "<h4 id='assoc_type.{}' class='{}'><code>",
@ -2327,7 +2350,7 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink,
} }
_ => panic!("can't make docs for trait item with name {:?}", item.name) _ => panic!("can't make docs for trait item with name {:?}", item.name)
} }
if let MethodLink::Anchor = link { if let AssocItemLink::Anchor = link {
document(w, item) document(w, item)
} else { } else {
Ok(()) Ok(())
@ -2339,7 +2362,7 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink,
try!(doctraititem(w, trait_item, link)); try!(doctraititem(w, trait_item, link));
} }
fn render_default_methods(w: &mut fmt::Formatter, fn render_default_items(w: &mut fmt::Formatter,
did: ast::DefId, did: ast::DefId,
t: &clean::Trait, t: &clean::Trait,
i: &clean::Impl) -> fmt::Result { i: &clean::Impl) -> fmt::Result {
@ -2350,7 +2373,7 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink,
None => {} None => {}
} }
try!(doctraititem(w, trait_item, MethodLink::GotoSource(did))); try!(doctraititem(w, trait_item, AssocItemLink::GotoSource(did)));
} }
Ok(()) Ok(())
} }
@ -2361,7 +2384,7 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink,
// for them work. // for them work.
if let Some(clean::ResolvedPath { did, .. }) = i.impl_.trait_ { if let Some(clean::ResolvedPath { did, .. }) = i.impl_.trait_ {
if let Some(t) = cache().traits.get(&did) { if let Some(t) = cache().traits.get(&did) {
try!(render_default_methods(w, did, t, &i.impl_)); try!(render_default_items(w, did, t, &i.impl_));
} }
} }
try!(write!(w, "</div>")); try!(write!(w, "</div>"));
@ -2458,7 +2481,7 @@ fn item_primitive(w: &mut fmt::Formatter,
it: &clean::Item, it: &clean::Item,
_p: &clean::PrimitiveType) -> fmt::Result { _p: &clean::PrimitiveType) -> fmt::Result {
try!(document(w, it)); try!(document(w, it));
render_methods(w, it.def_id, MethodRender::All) render_assoc_items(w, it.def_id, AssocItemRender::All)
} }
fn get_basic_keywords() -> &'static str { fn get_basic_keywords() -> &'static str {

View file

@ -184,7 +184,8 @@ impl<'a> fold::DocFolder for Stripper<'a> {
// Primitives are never stripped // Primitives are never stripped
clean::PrimitiveItem(..) => {} clean::PrimitiveItem(..) => {}
// Associated types are never stripped // Associated consts and types are never stripped
clean::AssociatedConstItem(..) |
clean::AssociatedTypeItem(..) => {} clean::AssociatedTypeItem(..) => {}
} }

View file

@ -593,6 +593,12 @@ pub enum Pat_ {
/// "None" means a * pattern where we don't bind the fields to names. /// "None" means a * pattern where we don't bind the fields to names.
PatEnum(Path, Option<Vec<P<Pat>>>), PatEnum(Path, Option<Vec<P<Pat>>>),
/// An associated const named using the qualified path `<T>::CONST` or
/// `<T as Trait>::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, ..}` /// Destructuring of a struct, e.g. `Foo {x, y, ..}`
/// The `bool` is `true` in the presence of a `..` /// The `bool` is `true` in the presence of a `..`
PatStruct(Path, Vec<Spanned<FieldPat>>, bool), PatStruct(Path, Vec<Spanned<FieldPat>>, bool),
@ -1230,6 +1236,7 @@ pub struct TraitItem {
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum TraitItem_ { pub enum TraitItem_ {
ConstTraitItem(P<Ty>, Option<P<Expr>>),
MethodTraitItem(MethodSig, Option<P<Block>>), MethodTraitItem(MethodSig, Option<P<Block>>),
TypeTraitItem(TyParamBounds, Option<P<Ty>>), TypeTraitItem(TyParamBounds, Option<P<Ty>>),
} }
@ -1246,6 +1253,7 @@ pub struct ImplItem {
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum ImplItem_ { pub enum ImplItem_ {
ConstImplItem(P<Ty>, P<Expr>),
MethodImplItem(MethodSig, P<Block>), MethodImplItem(MethodSig, P<Block>),
TypeImplItem(P<Ty>), TypeImplItem(P<Ty>),
MacImplItem(Mac), MacImplItem(Mac),

View file

@ -222,8 +222,7 @@ impl<'a> FnLikeNode<'a> {
ast::MethodImplItem(ref sig, ref body) => { ast::MethodImplItem(ref sig, ref body) => {
method(ii.id, ii.ident, sig, Some(ii.vis), body, ii.span) 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") panic!("impl method FnLikeNode that is not fn-like")
} }
} }

View file

@ -940,6 +940,12 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String {
} }
Some(NodeImplItem(ii)) => { Some(NodeImplItem(ii)) => {
match ii.node { match ii.node {
ConstImplItem(..) => {
format!("assoc const {} in {}{}",
token::get_ident(ii.ident),
map.path_to_string(id),
id_str)
}
MethodImplItem(..) => { MethodImplItem(..) => {
format!("method {} in {}{}", format!("method {} in {}{}",
token::get_ident(ii.ident), 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)) => { Some(NodeTraitItem(ti)) => {
let kind = match ti.node { let kind = match ti.node {
ConstTraitItem(..) => "assoc constant",
MethodTraitItem(..) => "trait method", MethodTraitItem(..) => "trait method",
TypeTraitItem(..) => "assoc type", TypeTraitItem(..) => "assoc type",
// ConstTraitItem(..) => "assoc constant"
}; };
format!("{} {} in {}{}", format!("{} {} in {}{}",

View file

@ -579,7 +579,7 @@ pub fn walk_pat<F>(pat: &Pat, mut it: F) -> bool where F: FnMut(&Pat) -> bool {
} }
PatMac(_) => panic!("attempted to analyze unexpanded pattern"), PatMac(_) => panic!("attempted to analyze unexpanded pattern"),
PatWild(_) | PatLit(_) | PatRange(_, _) | PatIdent(_, _, _) | PatWild(_) | PatLit(_) | PatRange(_, _) | PatIdent(_, _, _) |
PatEnum(_, _) => { PatEnum(_, _) | PatQPath(_, _) => {
true true
} }
} }

View file

@ -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 // Allows use of unary negate on unsigned integers, e.g. -e for e: u8
("negate_unsigned", "1.0.0", Active), ("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) // (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); 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<F>(cm: &CodeMap, span_handler: &SpanHandler, fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,

View file

@ -980,6 +980,10 @@ pub fn noop_fold_trait_item<T: Folder>(i: P<TraitItem>, folder: &mut T)
ident: folder.fold_ident(ident), ident: folder.fold_ident(ident),
attrs: fold_attrs(attrs, folder), attrs: fold_attrs(attrs, folder),
node: match node { node: match node {
ConstTraitItem(ty, default) => {
ConstTraitItem(folder.fold_ty(ty),
default.map(|x| folder.fold_expr(x)))
}
MethodTraitItem(sig, body) => { MethodTraitItem(sig, body) => {
MethodTraitItem(noop_fold_method_sig(sig, folder), MethodTraitItem(noop_fold_method_sig(sig, folder),
body.map(|x| folder.fold_block(x))) body.map(|x| folder.fold_block(x)))
@ -1001,6 +1005,9 @@ pub fn noop_fold_impl_item<T: Folder>(i: P<ImplItem>, folder: &mut T)
attrs: fold_attrs(attrs, folder), attrs: fold_attrs(attrs, folder),
vis: vis, vis: vis,
node: match node { node: match node {
ConstImplItem(ty, expr) => {
ConstImplItem(folder.fold_ty(ty), folder.fold_expr(expr))
}
MethodImplItem(sig, body) => { MethodImplItem(sig, body) => {
MethodImplItem(noop_fold_method_sig(sig, folder), MethodImplItem(noop_fold_method_sig(sig, folder),
folder.fold_block(body)) folder.fold_block(body))
@ -1134,6 +1141,10 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
PatEnum(folder.fold_path(pth), PatEnum(folder.fold_path(pth),
pats.map(|pats| pats.move_map(|x| folder.fold_pat(x)))) 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) => { PatStruct(pth, fields, etc) => {
let pth = folder.fold_path(pth); let pth = folder.fold_path(pth);
let fs = fields.move_map(|f| { let fs = fields.move_map(|f| {

View file

@ -17,8 +17,8 @@ use ast::{Public, Unsafety};
use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue}; use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue};
use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, BiGt, Block}; use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, BiGt, Block};
use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause}; use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause};
use ast::{Crate, CrateConfig, Decl, DeclItem}; use ast::{ConstImplItem, ConstTraitItem, Crate, CrateConfig};
use ast::{DeclLocal, DefaultBlock, DefaultReturn}; use ast::{Decl, DeclItem, DeclLocal, DefaultBlock, DefaultReturn};
use ast::{UnDeref, BiDiv, EMPTY_CTXT, EnumDef, ExplicitSelf}; use ast::{UnDeref, BiDiv, EMPTY_CTXT, EnumDef, ExplicitSelf};
use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain}; use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain};
use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprBox}; use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprBox};
@ -40,8 +40,9 @@ use ast::{MacStmtWithBraces, MacStmtWithSemicolon, MacStmtWithoutBraces};
use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, MatchSource}; use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, MatchSource};
use ast::{MutTy, BiMul, Mutability}; use ast::{MutTy, BiMul, Mutability};
use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, UnNot}; use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, UnNot};
use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatMac, PatRange, PatRegion}; use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatQPath, PatMac, PatRange};
use ast::{PatStruct, PatTup, PatVec, PatWild, PatWildMulti, PatWildSingle}; use ast::{PatRegion, PatStruct, PatTup, PatVec, PatWild, PatWildMulti};
use ast::PatWildSingle;
use ast::{PolyTraitRef, QSelf}; use ast::{PolyTraitRef, QSelf};
use ast::{Return, BiShl, BiShr, Stmt, StmtDecl}; use ast::{Return, BiShl, BiShr, Stmt, StmtDecl};
use ast::{StmtExpr, StmtSemi, StmtMac, StructDef, StructField}; use ast::{StmtExpr, StmtSemi, StmtMac, StructDef, StructField};
@ -109,6 +110,15 @@ pub enum PathParsingMode {
LifetimeAndTypesWithColons, LifetimeAndTypesWithColons,
} }
/// How to parse a qualified path, whether to allow trailing parameters.
#[derive(Copy, Clone, PartialEq)]
pub enum QPathParsingMode {
/// No trailing parameters, e.g. `<T as Trait>::Item`
NoParameters,
/// Optional parameters, e.g. `<T as Trait>::item::<'a, U>`
MaybeParameters,
}
/// How to parse a bound, whether to allow bound modifiers such as `?`. /// How to parse a bound, whether to allow bound modifiers such as `?`.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub enum BoundParsingMode { pub enum BoundParsingMode {
@ -1161,6 +1171,20 @@ impl<'a> Parser<'a> {
let TyParam {ident, bounds, default, ..} = try!(p.parse_ty_param()); let TyParam {ident, bounds, default, ..} = try!(p.parse_ty_param());
try!(p.expect(&token::Semi)); try!(p.expect(&token::Semi));
(ident, TypeTraitItem(bounds, default)) (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 { } else {
let style = try!(p.parse_unsafety()); let style = try!(p.parse_unsafety());
let abi = if try!(p.eat_keyword(keywords::Extern)) { let abi = if try!(p.eat_keyword(keywords::Extern)) {
@ -1334,36 +1358,9 @@ impl<'a> Parser<'a> {
try!(self.expect(&token::CloseDelim(token::Paren))); try!(self.expect(&token::CloseDelim(token::Paren)));
TyTypeof(e) TyTypeof(e)
} else if try!(self.eat_lt()) { } else if try!(self.eat_lt()) {
// QUALIFIED PATH `<TYPE as TRAIT_REF>::item`
let self_type = try!(self.parse_ty_sum());
let mut path = if try!(self.eat_keyword(keywords::As) ){ let (qself, path) =
try!(self.parse_path(LifetimeAndTypesWithoutColons)) try!(self.parse_qualified_path(QPathParsingMode::NoParameters));
} 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;
TyPath(Some(qself), path) TyPath(Some(qself), path)
} else if self.check(&token::ModSep) || } else if self.check(&token::ModSep) ||
@ -1580,6 +1577,61 @@ impl<'a> Parser<'a> {
} }
} }
// QUALIFIED PATH `<TYPE [as TRAIT_REF]>::IDENT[::<PARAMS>]`
// 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 /// Parses a path and optional type parameter bounds, depending on the
/// mode. The `mode` parameter determines whether lifetimes, types, and/or /// mode. The `mode` parameter determines whether lifetimes, types, and/or
/// bounds are permitted and whether `::` must precede type parameter /// bounds are permitted and whether `::` must precede type parameter
@ -2043,49 +2095,10 @@ impl<'a> Parser<'a> {
} }
_ => { _ => {
if try!(self.eat_lt()){ if try!(self.eat_lt()){
// QUALIFIED PATH `<TYPE as TRAIT_REF>::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 (qself, path) =
let parameters = if try!(self.eat(&token::ModSep) ){ try!(self.parse_qualified_path(QPathParsingMode::MaybeParameters));
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 hi = self.span.hi;
return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path))); return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path)));
} }
if try!(self.eat_keyword(keywords::Move) ){ if try!(self.eat_keyword(keywords::Move) ){
@ -3158,16 +3171,25 @@ impl<'a> Parser<'a> {
fn parse_pat_range_end(&mut self) -> PResult<P<Expr>> { fn parse_pat_range_end(&mut self) -> PResult<P<Expr>> {
if self.is_path_start() { if self.is_path_start() {
let lo = self.span.lo; 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; 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 { } else {
self.parse_literal_maybe_minus() self.parse_literal_maybe_minus()
} }
} }
fn is_path_start(&self) -> bool { 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) && !self.token.is_keyword(keywords::True) && !self.token.is_keyword(keywords::False)
} }
@ -3243,18 +3265,31 @@ impl<'a> Parser<'a> {
pat = try!(self.parse_pat_ident(BindByValue(MutImmutable))); pat = try!(self.parse_pat_ident(BindByValue(MutImmutable)));
} }
} else { } else {
// Parse as a general path let (qself, path) = if try!(self.eat_lt()) {
let path = try!(self.parse_path(LifetimeAndTypesWithColons)); // 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 { match self.token {
token::DotDotDot => { token::DotDotDot => {
// Parse range // Parse range
let hi = self.last_span.hi; 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()); try!(self.bump());
let end = try!(self.parse_pat_range_end()); let end = try!(self.parse_pat_range_end());
pat = PatRange(begin, end); pat = PatRange(begin, end);
} }
token::OpenDelim(token::Brace) => { token::OpenDelim(token::Brace) => {
if qself.is_some() {
let span = self.span;
self.span_err(span,
"unexpected `{` after qualified path");
self.abort_if_errors();
}
// Parse struct pattern // Parse struct pattern
try!(self.bump()); try!(self.bump());
let (fields, etc) = try!(self.parse_pat_fields()); let (fields, etc) = try!(self.parse_pat_fields());
@ -3262,6 +3297,12 @@ impl<'a> Parser<'a> {
pat = PatStruct(path, fields, etc); pat = PatStruct(path, fields, etc);
} }
token::OpenDelim(token::Paren) => { 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 // Parse tuple struct or enum pattern
if self.look_ahead(1, |t| *t == token::DotDot) { if self.look_ahead(1, |t| *t == token::DotDot) {
// This is a "top constructor only" pat // This is a "top constructor only" pat
@ -3278,6 +3319,10 @@ impl<'a> Parser<'a> {
pat = PatEnum(path, Some(args)); pat = PatEnum(path, Some(args));
} }
} }
_ if qself.is_some() => {
// Parse qualified path
pat = PatQPath(qself.unwrap(), path);
}
_ => { _ => {
// Parse nullary enum // Parse nullary enum
pat = PatEnum(path, Some(vec![])); pat = PatEnum(path, Some(vec![]));
@ -4349,6 +4394,14 @@ impl<'a> Parser<'a> {
let typ = try!(self.parse_ty_sum()); let typ = try!(self.parse_ty_sum());
try!(self.expect(&token::Semi)); try!(self.expect(&token::Semi));
(name, TypeImplItem(typ)) (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 { } else {
let (name, inner_attrs, node) = try!(self.parse_impl_method(vis)); let (name, inner_attrs, node) = try!(self.parse_impl_method(vis));
attrs.extend(inner_attrs.into_iter()); attrs.extend(inner_attrs.into_iter());

View file

@ -761,6 +761,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, fn print_associated_type(&mut self,
ident: ast::Ident, ident: ast::Ident,
bounds: Option<&ast::TyParamBounds>, bounds: Option<&ast::TyParamBounds>,
@ -1234,6 +1254,11 @@ impl<'a> State<'a> {
try!(self.maybe_print_comment(ti.span.lo)); try!(self.maybe_print_comment(ti.span.lo));
try!(self.print_outer_attributes(&ti.attrs)); try!(self.print_outer_attributes(&ti.attrs));
match ti.node { match ti.node {
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) => { ast::MethodTraitItem(ref sig, ref body) => {
if body.is_some() { if body.is_some() {
try!(self.head("")); try!(self.head(""));
@ -1260,6 +1285,9 @@ impl<'a> State<'a> {
try!(self.maybe_print_comment(ii.span.lo)); try!(self.maybe_print_comment(ii.span.lo));
try!(self.print_outer_attributes(&ii.attrs)); try!(self.print_outer_attributes(&ii.attrs));
match ii.node { match ii.node {
ast::ConstImplItem(ref ty, ref expr) => {
try!(self.print_associated_const(ii.ident, &ty, Some(&expr), ii.vis));
}
ast::MethodImplItem(ref sig, ref body) => { ast::MethodImplItem(ref sig, ref body) => {
try!(self.head("")); try!(self.head(""));
try!(self.print_method_sig(ii.ident, sig, ii.vis)); try!(self.print_method_sig(ii.ident, sig, ii.vis));
@ -2152,6 +2180,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) => { ast::PatStruct(ref path, ref fields, etc) => {
try!(self.print_path(path, true, 0)); try!(self.print_path(path, true, 0));
try!(self.nbsp()); try!(self.nbsp());

View file

@ -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, _) => { PatStruct(ref path, ref fields, _) => {
visitor.visit_path(path, pattern.id); visitor.visit_path(path, pattern.id);
for field in fields { for field in fields {
@ -619,6 +623,12 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai
visitor.visit_attribute(attr); visitor.visit_attribute(attr);
} }
match trait_item.node { 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) => { MethodTraitItem(ref sig, None) => {
visitor.visit_explicit_self(&sig.explicit_self); visitor.visit_explicit_self(&sig.explicit_self);
visitor.visit_generics(&sig.generics); visitor.visit_generics(&sig.generics);
@ -641,6 +651,10 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt
visitor.visit_attribute(attr); visitor.visit_attribute(attr);
} }
match impl_item.node { match impl_item.node {
ConstImplItem(ref ty, ref expr) => {
visitor.visit_ty(ty);
visitor.visit_expr(expr);
}
MethodImplItem(ref sig, ref body) => { MethodImplItem(ref sig, ref body) => {
visitor.visit_fn(FkMethod(impl_item.ident, sig, Some(impl_item.vis)), &sig.decl, visitor.visit_fn(FkMethod(impl_item.ident, sig, Some(impl_item.vis)), &sig.decl,
body, impl_item.span, impl_item.id); body, impl_item.span, impl_item.id);

View file

@ -0,0 +1,46 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
#![crate_type="lib"]
// These items are for testing that associated consts work cross-crate.
pub trait Foo {
const BAR: usize;
}
pub struct FooNoDefault;
impl Foo for FooNoDefault {
const BAR: usize = 0;
}
// These test that defaults and default resolution work cross-crate.
pub trait FooDefault {
const BAR: usize = 1;
}
pub struct FooOverwriteDefault;
impl FooDefault for FooOverwriteDefault {
const BAR: usize = 2;
}
pub struct FooUseDefault;
impl FooDefault for FooUseDefault {}
// Test inherent impls.
pub struct InherentBar;
impl InherentBar {
pub const BAR: usize = 3;
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
#![deny(dead_code)]
struct MyFoo;
impl MyFoo {
const BAR: u32 = 1;
//~^ ERROR associated const is never used: `BAR`
}
fn main() {
let _: MyFoo = MyFoo;
}

View file

@ -0,0 +1,24 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
trait Foo {
const BAR: u32;
}
struct SignedBar;
impl Foo for SignedBar {
const BAR: i32 = -1;
//~^ ERROR E0326
}
fn main() {}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
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
}

View file

@ -0,0 +1,22 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// 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)]
struct Foo;
impl Foo {
const not_upper: bool = true;
}
//~^^ ERROR associated constant `not_upper` should have an upper case name such as `NOT_UPPER`
fn main() {}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
trait MyTrait {
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
}

View file

@ -0,0 +1,45 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// 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;
}
pub struct FooConstForMethod;
impl Foo for FooConstForMethod {
//~^ ERROR E0046
const bar: u64 = 1;
//~^ ERROR E0323
const MY_CONST: u32 = 1;
}
pub struct FooMethodForConst;
impl Foo for FooMethodForConst {
//~^ ERROR E0046
fn bar(&self) {}
fn MY_CONST() {}
//~^ ERROR E0324
}
pub struct FooTypeForMethod;
impl Foo for FooTypeForMethod {
//~^ ERROR E0046
type bar = u64;
//~^ ERROR E0325
const MY_CONST: u32 = 1;
}
fn main () {}

View file

@ -0,0 +1,33 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct Foo;
impl Foo {
fn bar(&self) {}
}
trait MyTrait {
fn trait_bar() {}
}
impl MyTrait for Foo {}
fn main() {
match 0u32 {
Foo::bar => {} //~ ERROR E0327
}
match 0u32 {
<Foo>::bar => {} //~ ERROR E0327
}
match 0u32 {
<Foo>::trait_bar => {} //~ ERROR E0327
}
}

View file

@ -0,0 +1,24 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct Foo;
trait MyTrait {
fn trait_bar() {}
}
impl MyTrait for Foo {}
fn main() {
match 0u32 {
<Foo as MyTrait>::trait_bar => {}
//~^ ERROR `trait_bar` is not an associated const
}
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn foo() {
match x {
<T as Trait>::Type{key: value} => (),
//~^ ERROR unexpected `{` after qualified path
_ => (),
}
}

View file

@ -16,6 +16,6 @@ impl Foo {
fn foo() {} fn foo() {}
#[stable(feature = "rust1", since = "1.0.0")] #[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() {} fn main() {}

View file

@ -14,6 +14,6 @@ struct Foo;
impl Foo { impl Foo {
#[stable(feature = "rust1", since = "1.0.0")] #[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() {} fn main() {}

View file

@ -11,5 +11,5 @@
// compile-flags: -Z parse-only // compile-flags: -Z parse-only
trait MyTrait<T>: Iterator { trait MyTrait<T>: 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`
} }

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn foo() {
match x {
<T as Trait>::Type(2) => (),
//~^ ERROR unexpected `(` after qualified path
_ => (),
}
}

View file

@ -14,5 +14,5 @@ struct S;
impl S { impl S {
static fn f() {} 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`

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() {}

View file

@ -9,7 +9,8 @@
// except according to those terms. // except according to those terms.
trait Foo { 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() {} fn main() {}

View file

@ -9,7 +9,8 @@
// except according to those terms. // except according to those terms.
trait Foo { 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() {} fn main() {}

View file

@ -0,0 +1,32 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:associated-const-cc-lib.rs
#![feature(associated_consts)]
extern crate associated_const_cc_lib as foolib;
pub struct LocalFooUseDefault;
impl foolib::FooDefault for LocalFooUseDefault {}
pub struct LocalFooOverwriteDefault;
impl foolib::FooDefault for LocalFooOverwriteDefault {
const BAR: usize = 4;
}
fn main() {
assert_eq!(1, <foolib::FooUseDefault as foolib::FooDefault>::BAR);
assert_eq!(2, <foolib::FooOverwriteDefault as foolib::FooDefault>::BAR);
assert_eq!(1, <LocalFooUseDefault as foolib::FooDefault>::BAR);
assert_eq!(4, <LocalFooOverwriteDefault as foolib::FooDefault>::BAR);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:associated-const-cc-lib.rs
#![feature(associated_consts)]
extern crate associated_const_cc_lib as foolib;
pub struct LocalFoo;
impl foolib::Foo for LocalFoo {
const BAR: usize = 1;
}
fn main() {
assert_eq!(0, <foolib::FooNoDefault as foolib::Foo>::BAR);
assert_eq!(1, <LocalFoo as foolib::Foo>::BAR);
assert_eq!(3, foolib::InherentBar::BAR);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
struct Foo;
impl Foo {
const BAR: f32 = 1.5;
}
const FOOBAR: f32 = <Foo>::BAR;
fn main() {
assert_eq!(1.5f32, FOOBAR);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
struct Foo;
impl Foo {
const ID: i32 = 1;
}
fn main() {
assert_eq!(1, Foo::ID);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// 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;
struct Foo;
impl Foo {
const BAR: u32 = GLOBAL_BAR;
}
pub fn main() {
let _: u32 = Foo::BAR;
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
struct Foo;
enum Bar {
Var1,
Var2,
}
// Use inherent and trait impls to test UFCS syntax.
impl Foo {
const MYBAR: Bar = Bar::Var2;
}
trait HasBar {
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 {
<Foo>::MYBAR => true,
_ => false,
});
// Trait impl
assert!(match Bar::Var1 {
<Foo>::THEBAR => true,
_ => false,
});
assert!(match Bar::Var1 {
<Foo as HasBar>::THEBAR => true,
_ => false,
});
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
trait Foo {
const ID: i32 = 2;
}
impl Foo for i32 {
const ID: i32 = 1;
}
fn main() {
assert_eq!(1, <i32 as Foo>::ID);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
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);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
struct MyType;
impl MyType {
const IMPL_IS_INHERENT: bool = true;
}
trait MyTrait {
const IMPL_IS_INHERENT: bool;
const IMPL_IS_ON_TRAIT: bool;
}
impl MyTrait for MyType {
const IMPL_IS_INHERENT: bool = false;
const IMPL_IS_ON_TRAIT: bool = true;
}
fn main() {
// Check that the inherent impl is used before the trait, but that the trait
// can still be accessed.
assert!(<MyType>::IMPL_IS_INHERENT);
assert!(!<MyType as MyTrait>::IMPL_IS_INHERENT);
assert!(<MyType>::IMPL_IS_ON_TRAIT);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
trait MyInt {
const ONE: Self;
}
impl MyInt for i32 {
const ONE: i32 = 1;
}
fn main() {
assert_eq!(1, <i32>::ONE);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
trait Foo {
const ID: i32;
}
impl Foo for i32 {
const ID: i32 = 1;
}
fn main() {
assert_eq!(1, <i32>::ID);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
trait Foo {
const ID: i32 = 1;
}
impl Foo for i32 {}
fn main() {
assert_eq!(1, <i32 as Foo>::ID);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
// The main purpose of this test is to ensure that different impls of the same
// trait can refer to each other without setting off the static recursion check
// (as long as there's no actual recursion).
trait Foo {
const BAR: u32;
}
struct IsFoo1;
impl Foo for IsFoo1 {
const BAR: u32 = 1;
}
struct IsFoo2;
impl Foo for IsFoo2 {
const BAR: u32 = <IsFoo1 as Foo>::BAR;
}
fn main() {
assert_eq!(<IsFoo1>::BAR, <IsFoo2 as Foo>::BAR);
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(associated_consts)]
trait Foo {
const ID: i32;
}
impl Foo for i32 {
const ID: i32 = 1;
}
fn main() {
assert_eq!(1, <i32 as Foo>::ID);
}