Skip to content

Commit

Permalink
Fix ICE that occurs when an associated const is ambiguous.
Browse files Browse the repository at this point in the history
Also change several error messages to refer to "items" rather than
"methods", since associated items that require resolution during type
checking are not always methods.
  • Loading branch information
quantheory committed May 14, 2015
1 parent 4774d5d commit b4bbf3a
Show file tree
Hide file tree
Showing 34 changed files with 182 additions and 148 deletions.
73 changes: 36 additions & 37 deletions src/librustc_typeck/check/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ use middle::def;
use middle::privacy::{AllPublic, DependsOn, LastPrivate, LastMod};
use middle::subst;
use middle::traits;
use middle::ty::*;
use middle::ty;
use middle::ty::{self, AsPredicate, ToPolyTraitRef};
use middle::infer;
use util::ppaux::Repr;

use std::rc::Rc;
use syntax::ast::DefId;
use syntax::ast;
use syntax::codemap::Span;
Expand All @@ -39,7 +37,7 @@ pub enum MethodError {
// Did not find an applicable method, but we did find various
// static methods that may apply, as well as a list of
// not-in-scope traits which may work.
NoMatch(Vec<CandidateSource>, Vec<ast::DefId>),
NoMatch(Vec<CandidateSource>, Vec<ast::DefId>, probe::Mode),

// Multiple methods might apply.
Ambiguity(Vec<CandidateSource>),
Expand All @@ -62,7 +60,7 @@ type ItemIndex = usize; // just for doc purposes
pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
method_name: ast::Name,
self_ty: Ty<'tcx>,
self_ty: ty::Ty<'tcx>,
call_expr_id: ast::NodeId)
-> bool
{
Expand Down Expand Up @@ -92,11 +90,11 @@ pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
method_name: ast::Name,
self_ty: Ty<'tcx>,
supplied_method_types: Vec<Ty<'tcx>>,
self_ty: ty::Ty<'tcx>,
supplied_method_types: Vec<ty::Ty<'tcx>>,
call_expr: &'tcx ast::Expr,
self_expr: &'tcx ast::Expr)
-> Result<MethodCallee<'tcx>, MethodError>
-> Result<ty::MethodCallee<'tcx>, MethodError>
{
debug!("lookup(method_name={}, self_ty={}, call_expr={}, self_expr={})",
method_name.repr(fcx.tcx()),
Expand All @@ -115,9 +113,9 @@ pub fn lookup_in_trait<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
self_expr: Option<&ast::Expr>,
m_name: ast::Name,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
opt_input_types: Option<Vec<Ty<'tcx>>>)
-> Option<MethodCallee<'tcx>>
self_ty: ty::Ty<'tcx>,
opt_input_types: Option<Vec<ty::Ty<'tcx>>>)
-> Option<ty::MethodCallee<'tcx>>
{
lookup_in_trait_adjusted(fcx, span, self_expr, m_name, trait_def_id,
0, false, self_ty, opt_input_types)
Expand All @@ -139,9 +137,9 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
trait_def_id: DefId,
autoderefs: usize,
unsize: bool,
self_ty: Ty<'tcx>,
opt_input_types: Option<Vec<Ty<'tcx>>>)
-> Option<MethodCallee<'tcx>>
self_ty: ty::Ty<'tcx>,
opt_input_types: Option<Vec<ty::Ty<'tcx>>>)
-> Option<ty::MethodCallee<'tcx>>
{
debug!("lookup_in_trait_adjusted(self_ty={}, self_expr={}, m_name={}, trait_def_id={})",
self_ty.repr(fcx.tcx()),
Expand Down Expand Up @@ -186,7 +184,9 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
// Trait must have a method named `m_name` and it should not have
// type parameters or early-bound regions.
let tcx = fcx.tcx();
let (method_num, method_ty) = trait_method(tcx, trait_def_id, m_name).unwrap();
let (method_num, method_ty) = trait_item(tcx, trait_def_id, m_name)
.and_then(|(idx, item)| item.as_opt_method().map(|m| (idx, m)))
.unwrap();
assert_eq!(method_ty.generics.types.len(subst::FnSpace), 0);
assert_eq!(method_ty.generics.regions.len(subst::FnSpace), 0);

Expand Down Expand Up @@ -288,10 +288,10 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
}

let callee = MethodCallee {
origin: MethodTypeParam(MethodParam{trait_ref: trait_ref.clone(),
method_num: method_num,
impl_def_id: None}),
let callee = ty::MethodCallee {
origin: ty::MethodTypeParam(ty::MethodParam{trait_ref: trait_ref.clone(),
method_num: method_num,
impl_def_id: None}),
ty: fty,
substs: trait_ref.substs.clone()
};
Expand All @@ -304,7 +304,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
pub fn resolve_ufcs<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
method_name: ast::Name,
self_ty: Ty<'tcx>,
self_ty: ty::Ty<'tcx>,
expr_id: ast::NodeId)
-> Result<(def::Def, LastPrivate), MethodError>
{
Expand All @@ -322,41 +322,40 @@ pub fn resolve_ufcs<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
_ => def::FromTrait(pick.item.container().id())
};
let def_result = match pick.item {
ImplOrTraitItem::MethodTraitItem(..) => def::DefMethod(def_id, provenance),
ImplOrTraitItem::ConstTraitItem(..) => def::DefAssociatedConst(def_id, provenance),
ImplOrTraitItem::TypeTraitItem(..) => {
ty::ImplOrTraitItem::MethodTraitItem(..) => def::DefMethod(def_id, provenance),
ty::ImplOrTraitItem::ConstTraitItem(..) => def::DefAssociatedConst(def_id, provenance),
ty::ImplOrTraitItem::TypeTraitItem(..) => {
fcx.tcx().sess.span_bug(span, "resolve_ufcs: probe picked associated type");
}
};
Ok((def_result, lp))
}


/// Find method with name `method_name` defined in `trait_def_id` and return it, along with its
/// index (or `None`, if no such method).
fn trait_method<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_def_id: ast::DefId,
method_name: ast::Name)
-> Option<(usize, Rc<ty::Method<'tcx>>)>
/// Find item with name `item_name` defined in `trait_def_id` and return it, along with its
/// index (or `None`, if no such item).
fn trait_item<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_def_id: ast::DefId,
item_name: ast::Name)
-> Option<(usize, ty::ImplOrTraitItem<'tcx>)>
{
let trait_items = ty::trait_items(tcx, trait_def_id);
trait_items
.iter()
.enumerate()
.find(|&(_, ref item)| item.name() == method_name)
.and_then(|(idx, item)| item.as_opt_method().map(|m| (idx, m)))
.find(|&(_, ref item)| item.name() == item_name)
.map(|(num, item)| (num, (*item).clone()))
}

fn impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
impl_def_id: ast::DefId,
method_name: ast::Name)
-> Option<Rc<ty::Method<'tcx>>>
fn impl_item<'tcx>(tcx: &ty::ctxt<'tcx>,
impl_def_id: ast::DefId,
item_name: ast::Name)
-> Option<ty::ImplOrTraitItem<'tcx>>
{
let impl_items = tcx.impl_items.borrow();
let impl_items = impl_items.get(&impl_def_id).unwrap();
impl_items
.iter()
.map(|&did| ty::impl_or_trait_item(tcx, did.def_id()))
.find(|m| m.name() == method_name)
.and_then(|item| item.as_opt_method())
.find(|m| m.name() == item_name)
}
6 changes: 3 additions & 3 deletions src/librustc_typeck/check/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
let steps = if mode == Mode::MethodCall {
match create_steps(fcx, span, self_ty) {
Some(steps) => steps,
None => return Err(MethodError::NoMatch(Vec::new(), Vec::new())),
None => return Err(MethodError::NoMatch(Vec::new(), Vec::new(), mode)),
}
} else {
vec![CandidateStep {
Expand Down Expand Up @@ -866,7 +866,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
}
}
}).collect(),
Some(Err(MethodError::NoMatch(_, others))) => {
Some(Err(MethodError::NoMatch(_, others, _))) => {
assert!(others.is_empty());
vec![]
}
Expand All @@ -877,7 +877,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
None => vec![],
};

Err(MethodError::NoMatch(static_candidates, out_of_scope_traits))
Err(MethodError::NoMatch(static_candidates, out_of_scope_traits, self.mode))
}

fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
Expand Down
71 changes: 37 additions & 34 deletions src/librustc_typeck/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Give useful errors and suggestions to users when a method can't be
//! Give useful errors and suggestions to users when an item can't be
//! found or is otherwise invalid.
use CrateCtxt;
Expand All @@ -27,12 +27,13 @@ use syntax::print::pprust;
use std::cell;
use std::cmp::Ordering;

use super::{MethodError, CandidateSource, impl_method, trait_method};
use super::{MethodError, CandidateSource, impl_item, trait_item};
use super::probe::Mode;

pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
rcvr_ty: Ty<'tcx>,
method_name: ast::Name,
item_name: ast::Name,
rcvr_expr: Option<&ast::Expr>,
error: MethodError)
{
Expand All @@ -42,28 +43,30 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}

match error {
MethodError::NoMatch(static_sources, out_of_scope_traits) => {
MethodError::NoMatch(static_sources, out_of_scope_traits, mode) => {
let cx = fcx.tcx();
let method_ustring = method_name.user_string(cx);
let item_ustring = item_name.user_string(cx);

fcx.type_error_message(
span,
|actual| {
format!("type `{}` does not implement any \
method in scope named `{}`",
actual,
method_ustring)
format!("no {} named `{}` found for type `{}` \
in the current scope",
if mode == Mode::MethodCall { "method" }
else { "associated item" },
item_ustring,
actual)
},
rcvr_ty,
None);

// If the method has the name of a field, give a help note
// If the item has the name of a field, give a help note
if let (&ty::ty_struct(did, _), Some(_)) = (&rcvr_ty.sty, rcvr_expr) {
let fields = ty::lookup_struct_fields(cx, did);
if fields.iter().any(|f| f.name == method_name) {
if fields.iter().any(|f| f.name == item_name) {
cx.sess.span_note(span,
&format!("use `(s.{0})(...)` if you meant to call the \
function stored in the `{0}` field", method_ustring));
function stored in the `{0}` field", item_ustring));
}
}

Expand All @@ -72,25 +75,25 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span,
"found defined static methods, maybe a `self` is missing?");

report_candidates(fcx, span, method_name, static_sources);
report_candidates(fcx, span, item_name, static_sources);
}

suggest_traits_to_import(fcx, span, rcvr_ty, method_name,
suggest_traits_to_import(fcx, span, rcvr_ty, item_name,
rcvr_expr, out_of_scope_traits)
}

MethodError::Ambiguity(sources) => {
span_err!(fcx.sess(), span, E0034,
"multiple applicable methods in scope");
"multiple applicable items in scope");

report_candidates(fcx, span, method_name, sources);
report_candidates(fcx, span, item_name, sources);
}

MethodError::ClosureAmbiguity(trait_def_id) => {
let msg = format!("the `{}` method from the `{}` trait cannot be explicitly \
invoked on this closure as we have not yet inferred what \
kind of closure it is",
method_name.user_string(fcx.tcx()),
item_name.user_string(fcx.tcx()),
ty::item_path_str(fcx.tcx(), trait_def_id));
let msg = if let Some(callee) = rcvr_expr {
format!("{}; use overloaded call notation instead (e.g., `{}()`)",
Expand All @@ -104,19 +107,19 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,

fn report_candidates(fcx: &FnCtxt,
span: Span,
method_name: ast::Name,
item_name: ast::Name,
mut sources: Vec<CandidateSource>) {
sources.sort();
sources.dedup();

for (idx, source) in sources.iter().enumerate() {
match *source {
CandidateSource::ImplSource(impl_did) => {
// Provide the best span we can. Use the method, if local to crate, else
// the impl, if local to crate (method may be defaulted), else the call site.
let method = impl_method(fcx.tcx(), impl_did, method_name).unwrap();
// Provide the best span we can. Use the item, if local to crate, else
// the impl, if local to crate (item may be defaulted), else the call site.
let item = impl_item(fcx.tcx(), impl_did, item_name).unwrap();
let impl_span = fcx.tcx().map.def_id_span(impl_did, span);
let method_span = fcx.tcx().map.def_id_span(method.def_id, impl_span);
let item_span = fcx.tcx().map.def_id_span(item.def_id(), impl_span);

let impl_ty = check::impl_self_ty(fcx, span, impl_did).ty;

Expand All @@ -127,16 +130,16 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
trait_ref.def_id)),
};

span_note!(fcx.sess(), method_span,
span_note!(fcx.sess(), item_span,
"candidate #{} is defined in an impl{} for the type `{}`",
idx + 1,
insertion,
impl_ty.user_string(fcx.tcx()));
}
CandidateSource::TraitSource(trait_did) => {
let (_, method) = trait_method(fcx.tcx(), trait_did, method_name).unwrap();
let method_span = fcx.tcx().map.def_id_span(method.def_id, span);
span_note!(fcx.sess(), method_span,
let (_, item) = trait_item(fcx.tcx(), trait_did, item_name).unwrap();
let item_span = fcx.tcx().map.def_id_span(item.def_id(), span);
span_note!(fcx.sess(), item_span,
"candidate #{} is defined in the trait `{}`",
idx + 1,
ty::item_path_str(fcx.tcx(), trait_did));
Expand All @@ -152,19 +155,19 @@ pub type AllTraitsVec = Vec<TraitInfo>;
fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
rcvr_ty: Ty<'tcx>,
method_name: ast::Name,
item_name: ast::Name,
rcvr_expr: Option<&ast::Expr>,
valid_out_of_scope_traits: Vec<ast::DefId>)
{
let tcx = fcx.tcx();
let method_ustring = method_name.user_string(tcx);
let item_ustring = item_name.user_string(tcx);

if !valid_out_of_scope_traits.is_empty() {
let mut candidates = valid_out_of_scope_traits;
candidates.sort();
candidates.dedup();
let msg = format!(
"methods from traits can only be called if the trait is in scope; \
"items from traits can only be used if the trait is in scope; \
the following {traits_are} implemented but not in scope, \
perhaps add a `use` for {one_of_them}:",
traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
Expand All @@ -185,7 +188,7 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
let type_is_local = type_derefs_to_local(fcx, span, rcvr_ty, rcvr_expr);

// there's no implemented traits, so lets suggest some traits to
// implement, by finding ones that have the method name, and are
// implement, by finding ones that have the item name, and are
// legal to implement.
let mut candidates = all_traits(fcx.ccx)
.filter(|info| {
Expand All @@ -196,7 +199,7 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
// implementing a trait would be legal but is rejected
// here).
(type_is_local || ast_util::is_local(info.def_id))
&& trait_method(tcx, info.def_id, method_name).is_some()
&& trait_item(tcx, info.def_id, item_name).is_some()
})
.collect::<Vec<_>>();

Expand All @@ -209,12 +212,12 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
// of a type parameter: suggest adding a trait bound rather
// than implementing.
let msg = format!(
"methods from traits can only be called if the trait is implemented and in scope; \
the following {traits_define} a method `{name}`, \
"items from traits can only be used if the trait is implemented and in scope; \
the following {traits_define} an item `{name}`, \
perhaps you need to implement {one_of_them}:",
traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"},
one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
name = method_ustring);
name = item_ustring);

fcx.sess().fileline_help(span, &msg[..]);

Expand Down
Loading

0 comments on commit b4bbf3a

Please sign in to comment.