diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index dbaebd07b0220..71855f38ba206 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -120,6 +120,13 @@ impl ImplOrTraitItem { TypeTraitItem(ref associated_type) => associated_type.container, } } + + pub fn as_opt_method(&self) -> Option> { + match *self { + MethodTraitItem(ref m) => Some((*m).clone()), + TypeTraitItem(_) => None + } + } } #[deriving(Clone)] @@ -1240,6 +1247,10 @@ impl Generics { } impl TraitRef { + pub fn new(def_id: ast::DefId, substs: Substs) -> TraitRef { + TraitRef { def_id: def_id, substs: substs } + } + pub fn self_ty(&self) -> ty::t { self.substs.self_ty().unwrap() } diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 9399c3a475a3c..4560c51946494 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -81,30 +81,31 @@ obtained the type `Foo`, we would never match this method. use middle::subst; -use middle::subst::Subst; +use middle::subst::{Subst, SelfSpace}; use middle::traits; use middle::ty::*; use middle::ty; use middle::typeck::astconv::AstConv; use middle::typeck::check::{FnCtxt, NoPreference, PreferMutLvalue}; use middle::typeck::check::{impl_self_ty}; +use middle::typeck::check::vtable2::select_fcx_obligations_where_possible; use middle::typeck::check; use middle::typeck::infer; use middle::typeck::{MethodCall, MethodCallee}; use middle::typeck::{MethodOrigin, MethodParam, MethodTypeParam}; use middle::typeck::{MethodStatic, MethodStaticUnboxedClosure, MethodObject, MethodTraitObject}; -use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig; +use middle::typeck::check::regionmanip::replace_late_bound_regions; use middle::typeck::TypeAndSubsts; +use middle::ty_fold::TypeFoldable; use util::common::indenter; use util::ppaux; -use util::ppaux::Repr; +use util::ppaux::{Repr, UserString}; use std::collections::HashSet; use std::rc::Rc; use syntax::ast::{DefId, MutImmutable, MutMutable}; use syntax::ast; use syntax::codemap::Span; -use syntax::parse::token; #[deriving(PartialEq)] pub enum CheckTraitsFlag { @@ -118,26 +119,31 @@ pub enum AutoderefReceiverFlag { DontAutoderefReceiver, } -#[deriving(PartialEq)] -pub enum StaticMethodsFlag { - ReportStaticMethods, - IgnoreStaticMethods, +pub enum MethodError { + // Did not find an applicable method, but we did find various + // static methods that may apply. + NoMatch(Vec), + + // Multiple methods might apply. + Ambiguity(Vec), } +pub type MethodResult = Result; + pub fn lookup<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - - // In a call `a.b::(...)`: - expr: &ast::Expr, // The expression `a.b(...)`. - self_expr: &'a ast::Expr, // The expression `a`. - m_name: ast::Name, // The name `b`. - self_ty: ty::t, // The type of `a`. - supplied_tps: &'a [ty::t], // The list of types X, Y, ... . - deref_args: check::DerefArgs, // Whether we autopointer first. - check_traits: CheckTraitsFlag, // Whether we check traits only. - autoderef_receiver: AutoderefReceiverFlag, - report_statics: StaticMethodsFlag) - -> Option { + fcx: &'a FnCtxt<'a, 'tcx>, + + // In a call `a.b::(...)`: + expr: &ast::Expr, // The expression `a.b(...)`. + self_expr: &'a ast::Expr, // The expression `a`. + m_name: ast::Name, // The name `b`. + self_ty: ty::t, // The type of `a`. + supplied_tps: &'a [ty::t], // The list of types X, Y, ... . + deref_args: check::DerefArgs, // Whether we autopointer first. + check_traits: CheckTraitsFlag, // Whether we check traits only. + autoderef_receiver: AutoderefReceiverFlag) + -> MethodResult +{ let mut lcx = LookupContext { fcx: fcx, span: expr.span, @@ -147,10 +153,10 @@ pub fn lookup<'a, 'tcx>( impl_dups: HashSet::new(), inherent_candidates: Vec::new(), extension_candidates: Vec::new(), + static_candidates: Vec::new(), deref_args: deref_args, check_traits: check_traits, autoderef_receiver: autoderef_receiver, - report_statics: report_statics, }; debug!("method lookup(self_ty={}, expr={}, self_expr={})", @@ -166,16 +172,17 @@ pub fn lookup<'a, 'tcx>( } pub fn lookup_in_trait<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - - // In a call `a.b::(...)`: - span: Span, // The expression `a.b(...)`'s span. - self_expr: Option<&'a ast::Expr>, // The expression `a`, if available. - m_name: ast::Name, // The name `b`. - trait_did: DefId, // The trait to limit the lookup to. - self_ty: ty::t, // The type of `a`. - supplied_tps: &'a [ty::t]) // The list of types X, Y, ... . - -> Option { + fcx: &'a FnCtxt<'a, 'tcx>, + + // In a call `a.b::(...)`: + span: Span, // The expression `a.b(...)`'s span. + self_expr: Option<&'a ast::Expr>, // The expression `a`, if available. + m_name: ast::Name, // The name `b`. + trait_did: DefId, // The trait to limit the lookup to. + self_ty: ty::t, // The type of `a`. + supplied_tps: &'a [ty::t]) // The list of types X, Y, ... . + -> Option +{ let mut lcx = LookupContext { fcx: fcx, span: span, @@ -185,18 +192,107 @@ pub fn lookup_in_trait<'a, 'tcx>( impl_dups: HashSet::new(), inherent_candidates: Vec::new(), extension_candidates: Vec::new(), + static_candidates: Vec::new(), deref_args: check::DoDerefArgs, check_traits: CheckTraitsOnly, autoderef_receiver: DontAutoderefReceiver, - report_statics: IgnoreStaticMethods, }; - debug!("method lookup_in_trait(self_ty={}, self_expr={})", - self_ty.repr(fcx.tcx()), self_expr.map(|e| e.repr(fcx.tcx()))); + debug!("method lookup_in_trait(self_ty={}, self_expr={}, m_name={}, trait_did={})", + self_ty.repr(fcx.tcx()), + self_expr.repr(fcx.tcx()), + m_name.repr(fcx.tcx()), + trait_did.repr(fcx.tcx())); lcx.push_bound_candidates(self_ty, Some(trait_did)); lcx.push_extension_candidate(trait_did); - lcx.search(self_ty) + + // when doing a trait search, ambiguity can't really happen except + // as part of the trait-lookup in general + match lcx.search(self_ty) { + Ok(callee) => Some(callee), + Err(_) => None + } +} + +pub fn report_error(fcx: &FnCtxt, + span: Span, + rcvr_ty: ty::t, + method_name: ast::Name, + error: MethodError) +{ + match error { + NoMatch(static_sources) => { + fcx.type_error_message( + span, + |actual| { + format!("type `{}` does not implement any \ + method in scope named `{}`", + actual, + method_name.user_string(fcx.tcx())) + }, + rcvr_ty, + None); + + if static_sources.len() > 0 { + fcx.tcx().sess.fileline_note( + span, + "found defined static methods, maybe a `self` is missing?"); + + report_candidates(fcx, span, method_name, static_sources); + } + } + + Ambiguity(sources) => { + span_err!(fcx.sess(), span, E0034, + "multiple applicable methods in scope"); + + report_candidates(fcx, span, method_name, sources); + } + } + + fn report_candidates(fcx: &FnCtxt, + span: Span, + method_name: ast::Name, + mut sources: Vec) { + sources.sort(); + sources.dedup(); + + for (idx, source) in sources.iter().enumerate() { + match *source { + 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(); + 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 impl_ty = impl_self_ty(fcx, span, impl_did).ty; + + let insertion = match impl_trait_ref(fcx.tcx(), impl_did) { + None => format!(""), + Some(trait_ref) => format!(" of the trait `{}`", + ty::item_path_str(fcx.tcx(), + trait_ref.def_id)), + }; + + span_note!(fcx.sess(), method_span, + "candidate #{} is defined in an impl{} for the type `{}`", + idx + 1u, + insertion, + impl_ty.user_string(fcx.tcx())); + } + 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, + "candidate #{} is defined in the trait `{}`", + idx + 1u, + ty::item_path_str(fcx.tcx(), trait_did)); + } + } + } + } } // Determine the index of a method in the list of all methods belonging @@ -228,75 +324,6 @@ fn get_method_index(tcx: &ty::ctxt, method_count + n_method } -fn construct_transformed_self_ty_for_object( - tcx: &ty::ctxt, - span: Span, - trait_def_id: ast::DefId, - rcvr_substs: &subst::Substs, - rcvr_bounds: ty::ExistentialBounds, - method_ty: &ty::Method) - -> ty::t -{ - /*! - * This is a bit tricky. We have a match against a trait method - * being invoked on an object, and we want to generate the - * self-type. As an example, consider a trait - * - * trait Foo { - * fn r_method<'a>(&'a self); - * fn u_method(Box); - * } - * - * Now, assuming that `r_method` is being called, we want the - * result to be `&'a Foo`. Assuming that `u_method` is being - * called, we want the result to be `Box`. Of course, - * this transformation has already been done as part of - * `method_ty.fty.sig.inputs[0]`, but there the type - * is expressed in terms of `Self` (i.e., `&'a Self`, `Box`). - * Because objects are not standalone types, we can't just substitute - * `s/Self/Foo/`, so we must instead perform this kind of hokey - * match below. - */ - - let mut obj_substs = rcvr_substs.clone(); - - // The subst we get in has Err as the "Self" type. For an object - // type, we don't put any type into the Self paramspace, so let's - // make a copy of rcvr_substs that has the Self paramspace empty. - obj_substs.types.pop(subst::SelfSpace).unwrap(); - - match method_ty.explicit_self { - StaticExplicitSelfCategory => { - tcx.sess.span_bug(span, "static method for object type receiver"); - } - ByValueExplicitSelfCategory => { - let tr = ty::mk_trait(tcx, trait_def_id, obj_substs, rcvr_bounds); - ty::mk_uniq(tcx, tr) - } - ByReferenceExplicitSelfCategory(..) | ByBoxExplicitSelfCategory => { - let transformed_self_ty = method_ty.fty.sig.inputs[0]; - match ty::get(transformed_self_ty).sty { - ty::ty_rptr(r, mt) => { // must be SelfRegion - let r = r.subst(tcx, rcvr_substs); // handle Early-Bound lifetime - let tr = ty::mk_trait(tcx, trait_def_id, obj_substs, - rcvr_bounds); - ty::mk_rptr(tcx, r, ty::mt{ ty: tr, mutbl: mt.mutbl }) - } - ty::ty_uniq(_) => { // must be SelfUniq - let tr = ty::mk_trait(tcx, trait_def_id, obj_substs, - rcvr_bounds); - ty::mk_uniq(tcx, tr) - } - _ => { - tcx.sess.span_bug(span, - format!("'impossible' transformed_self_ty: {}", - transformed_self_ty.repr(tcx)).as_slice()); - } - } - } - } -} - struct LookupContext<'a, 'tcx: 'a> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, @@ -311,42 +338,45 @@ struct LookupContext<'a, 'tcx: 'a> { supplied_tps: &'a [ty::t], impl_dups: HashSet, inherent_candidates: Vec, - extension_candidates: Vec, + extension_candidates: Vec, + static_candidates: Vec, deref_args: check::DerefArgs, check_traits: CheckTraitsFlag, autoderef_receiver: AutoderefReceiverFlag, - report_statics: StaticMethodsFlag, } -/** - * A potential method that might be called, assuming the receiver - * is of a suitable type. - */ +// A method that the user may be trying to invoke. Initially, we +// construct candidates only for inherent methods; for extension +// traits, we use an ExtensionCandidate. #[deriving(Clone)] struct Candidate { - rcvr_match_condition: RcvrMatchCondition, + xform_self_ty: ty::t, rcvr_substs: subst::Substs, method_ty: Rc, origin: MethodOrigin, } -/// This type represents the conditions under which the receiver is -/// considered to "match" a given method candidate. Typically the test -/// is whether the receiver is of a particular type. However, this -/// type is the type of the receiver *after accounting for the -/// method's self type* (e.g., if the method is an `Box` method, we -/// have *already verified* that the receiver is of some type `Box` and -/// now we must check that the type `T` is correct). Unfortunately, -/// because traits are not types, this is a pain to do. -#[deriving(Clone)] -pub enum RcvrMatchCondition { - RcvrMatchesIfObject(ast::DefId), - RcvrMatchesIfSubtype(ty::t), - RcvrMatchesIfEqtype(ty::t) +// A variation on a candidate that just stores the data needed +// extension trait matching. Once we pick the trait that matches, +// we'll construct a normal candidate from that. There is no deep +// reason for this, the code just worked out a bit cleaner. +struct ExtensionCandidate { + obligation: traits::Obligation, + xform_self_ty: ty::t, + method_ty: Rc, + method_num: uint, +} + +// A pared down enum describing just the places from which a method +// candidate can arise. Used for error reporting only. +#[deriving(PartialOrd, Ord, PartialEq, Eq)] +pub enum CandidateSource { + ImplSource(ast::DefId), + TraitSource(/* trait id */ ast::DefId), } impl<'a, 'tcx> LookupContext<'a, 'tcx> { - fn search(&self, self_ty: ty::t) -> Option { + fn search(self, self_ty: ty::t) -> MethodResult { let span = self.self_expr.map_or(self.span, |e| e.span); let self_expr_id = self.self_expr.map(|e| e.id); @@ -356,18 +386,33 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { |self_ty, autoderefs| self.search_step(self_ty, autoderefs)); match result { - Some(Some(result)) => { + Some(Some(Ok(result))) => { self.fixup_derefs_on_method_receiver_if_necessary(&result); - Some(result) + Ok(result) + } + Some(Some(Err(err))) => { + Err(err) + } + None | Some(None) => { + Err(NoMatch(self.static_candidates)) } - _ => None } } fn search_step(&self, self_ty: ty::t, autoderefs: uint) - -> Option> { + -> Option> + { + // Oh my, what a return type! + // + // Returning: + // - `None` => autoderef more, keep searching + // - `Some(None)` => stop searching, found nothing + // - `Some(Some(_))` => stop searching, found either callee/error + // - `Some(Some(Ok(_)))` => found a callee + // - `Some(Some(Err(_)))` => found an error (ambiguity, etc) + debug!("search_step: self_ty={} autoderefs={}", self.ty_to_string(self_ty), autoderefs); @@ -418,7 +463,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { self.self_expr.is_none() } - // ______________________________________________________________________ + /////////////////////////////////////////////////////////////////////////// // Candidate collection (see comment at start of file) fn push_inherent_candidates(&mut self, self_ty: ty::t) { @@ -435,8 +480,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { check::autoderef(self.fcx, span, self_ty, None, NoPreference, |self_ty, _| { match get(self_ty).sty { ty_trait(box TyTrait { def_id, ref substs, bounds, .. }) => { - self.push_inherent_candidates_from_object( - def_id, substs, bounds); + self.push_inherent_candidates_from_object(self_ty, def_id, substs, bounds); self.push_inherent_impl_candidates_for_type(def_id); } ty_enum(did, _) | @@ -465,10 +509,6 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { ty_param(p) => { self.push_inherent_candidates_from_param(self_ty, restrict_to, p); } - ty_unboxed_closure(closure_did, _) => { - self.push_unboxed_closure_call_candidates_if_applicable( - closure_did); - } _ => { /* No bound methods in these types */ } } @@ -481,131 +521,92 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { }); } - fn push_extension_candidate(&mut self, trait_did: DefId) { - ty::populate_implementations_for_trait_if_necessary(self.tcx(), trait_did); - - // Look for explicit implementations. - let impl_items = self.tcx().impl_items.borrow(); - for impl_infos in self.tcx().trait_impls.borrow().find(&trait_did).iter() { - for impl_did in impl_infos.borrow().iter() { - let items = &(*impl_items)[*impl_did]; - self.push_candidates_from_impl(*impl_did, - items.as_slice(), - true); - } - } - } - fn push_extension_candidates(&mut self, expr_id: ast::NodeId) { - // If the method being called is associated with a trait, then - // find all the impls of that trait. Each of those are - // candidates. + debug!("push_extension_candidates(expr_id={})", expr_id); + + let mut duplicates = HashSet::new(); let opt_applicable_traits = self.fcx.ccx.trait_map.find(&expr_id); for applicable_traits in opt_applicable_traits.into_iter() { - for trait_did in applicable_traits.iter() { - debug!("push_extension_candidates() found trait: {}", - if trait_did.krate == ast::LOCAL_CRATE { - self.fcx.ccx.tcx.map.node_to_string(trait_did.node) - } else { - "(external)".to_string() - }); - self.push_extension_candidate(*trait_did); + for &trait_did in applicable_traits.iter() { + if duplicates.insert(trait_did) { + self.push_extension_candidate(trait_did); + } } } } - fn push_unboxed_closure_call_candidate_if_applicable( - &mut self, - trait_did: DefId, - closure_did: DefId, - closure_function_type: &ClosureTy) { - let trait_item = (*ty::trait_items(self.tcx(), trait_did))[0] - .clone(); - let method = match trait_item { - ty::MethodTraitItem(method) => method, - ty::TypeTraitItem(_) => { - self.tcx().sess.bug( - "push_unboxed_closure_call_candidates_if_applicable(): \ - unexpected associated type in function trait") - } + fn push_extension_candidate(&mut self, trait_def_id: DefId) { + debug!("push_extension_candidates: trait_def_id={}", trait_def_id); + + // Check whether `trait_def_id` defines a method with suitable name: + let trait_items = + ty::trait_items(self.tcx(), trait_def_id); + let matching_index = + trait_items.iter() + .position(|item| item.ident().name == self.m_name); + let matching_index = match matching_index { + Some(i) => i, + None => { return; } + }; + let method = match (&*trait_items)[matching_index].as_opt_method() { + Some(m) => m, + None => { return; } }; - // Make sure it has the right name! - if method.ident.name != self.m_name { - return + // Check whether `trait_def_id` defines a method with suitable name: + if !self.has_applicable_self(&*method) { + debug!("method has inapplicable self"); + return self.record_static_candidate(TraitSource(trait_def_id)); } - // Get the tupled type of the arguments. - let arguments_type = closure_function_type.sig.inputs[0]; - let return_type = closure_function_type.sig.output; - - let closure_region = - self.fcx.infcx().next_region_var(infer::MiscVariable(self.span)); - let unboxed_closure_type = ty::mk_unboxed_closure(self.tcx(), - closure_did, - closure_region); - self.extension_candidates.push(Candidate { - rcvr_match_condition: - RcvrMatchesIfSubtype(unboxed_closure_type), - rcvr_substs: subst::Substs::new_trait( - vec![arguments_type, return_type], - vec![], - self.fcx.infcx().next_ty_vars(1)[0]), + // Otherwise, construct the receiver type. + let self_ty = + self.fcx.infcx().next_ty_var(); + let trait_def = + ty::lookup_trait_def(self.tcx(), trait_def_id); + let substs = + self.fcx.infcx().fresh_substs_for_trait(self.span, + &trait_def.generics, + self_ty); + let xform_self_ty = + self.xform_self_ty(&method, &substs); + + // Construct the obligation which must match. + let trait_ref = + Rc::new(ty::TraitRef::new(trait_def_id, substs)); + let obligation = + traits::Obligation::misc(self.span, trait_ref); + + debug!("extension-candidate(xform_self_ty={} obligation={})", + self.infcx().ty_to_string(xform_self_ty), + obligation.repr(self.tcx())); + + self.extension_candidates.push(ExtensionCandidate { + obligation: obligation, + xform_self_ty: xform_self_ty, method_ty: method, - origin: MethodStaticUnboxedClosure(closure_did), + method_num: matching_index, }); } - fn push_unboxed_closure_call_candidates_if_applicable( - &mut self, - closure_did: DefId) { - match self.tcx().unboxed_closures.borrow().find(&closure_did) { - None => {} // Fall through to try inherited. - Some(closure) => { - let tcx = self.tcx(); - self.push_unboxed_closure_call_candidate_if_applicable( - closure.kind.trait_did(tcx), - closure_did, - &closure.closure_type); - return - } - } - - match self.fcx.inh.unboxed_closures.borrow().find(&closure_did) { - Some(closure) => { - let tcx = self.tcx(); - self.push_unboxed_closure_call_candidate_if_applicable( - closure.kind.trait_did(tcx), - closure_did, - &closure.closure_type); - return - } - None => {} - } - - self.tcx().sess.bug("didn't find unboxed closure type in tcx map or \ - inherited map, so there") - } - fn push_inherent_candidates_from_object(&mut self, + self_ty: ty::t, did: DefId, substs: &subst::Substs, - bounds: ty::ExistentialBounds) { - debug!("push_inherent_candidates_from_object(did={}, substs={})", - self.did_to_string(did), - substs.repr(self.tcx())); + _bounds: ty::ExistentialBounds) { + debug!("push_inherent_candidates_from_object(self_ty={})", + self_ty.repr(self.tcx())); + let tcx = self.tcx(); - let span = self.span; // It is illegal to invoke a method on a trait instance that - // refers to the `self` type. An error will be reported by - // `enforce_object_limitations()` if the method refers - // to the `Self` type. Substituting ty_err here allows - // compiler to soldier on. - // - // `confirm_candidate()` also relies upon this substitution - // for Self. (fix) - let rcvr_substs = substs.with_self_ty(ty::mk_err()); + // refers to the `Self` type. An error will be reported by + // `enforce_object_limitations()` if the method refers to the + // `Self` type anywhere other than the receiver. Here, we use + // a substitution that replaces `Self` with the object type + // itself. Hence, a `&self` method will wind up with an + // argument type like `&Trait`. + let rcvr_substs = substs.with_self_ty(self_ty); let trait_ref = Rc::new(TraitRef { def_id: did, substs: rcvr_substs.clone() @@ -613,20 +614,35 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { self.push_inherent_candidates_from_bounds_inner( &[trait_ref.clone()], - |_this, new_trait_ref, m, method_num| { + |this, new_trait_ref, m, method_num| { let vtable_index = get_method_index(tcx, &*new_trait_ref, trait_ref.clone(), method_num); - let mut m = (*m).clone(); - // We need to fix up the transformed self type. - *m.fty.sig.inputs.get_mut(0) = - construct_transformed_self_ty_for_object( - tcx, span, did, &rcvr_substs, bounds, &m); + + // FIXME Hacky. By-value `self` methods in objects ought to be + // just a special case of passing ownership of a DST value + // as a parameter. *But* we currently hack them in and tie them to + // the particulars of the `Box` type. So basically for a `fn foo(self,...)` + // method invoked on an object, we don't want the receiver type to be + // `TheTrait`, but rather `Box`. Yuck. + let mut m = m; + match m.explicit_self { + ByValueExplicitSelfCategory => { + let mut n = (*m).clone(); + let self_ty = n.fty.sig.inputs[0]; + *n.fty.sig.inputs.get_mut(0) = ty::mk_uniq(tcx, self_ty); + m = Rc::new(n); + } + _ => { } + } + + let xform_self_ty = + this.xform_self_ty(&m, &new_trait_ref.substs); Some(Candidate { - rcvr_match_condition: RcvrMatchesIfObject(did), + xform_self_ty: xform_self_ty, rcvr_substs: new_trait_ref.substs.clone(), - method_ty: Rc::new(m), + method_ty: m, origin: MethodTraitObject(MethodObject { trait_ref: new_trait_ref, object_trait_id: did, @@ -650,9 +666,8 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { restrict_to); } - fn push_inherent_candidates_from_bounds(&mut self, - self_ty: ty::t, + _self_ty: ty::t, space: subst::ParamSpace, index: uint, restrict_to: Option) { @@ -670,12 +685,8 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { _ => {} } - let condition = match m.explicit_self { - ByReferenceExplicitSelfCategory(_, mt) if mt == MutMutable => - RcvrMatchesIfEqtype(self_ty), - _ => - RcvrMatchesIfSubtype(self_ty) - }; + let xform_self_ty = + this.xform_self_ty(&m, &trait_ref.substs); debug!("found match: trait_ref={} substs={} m={}", trait_ref.repr(this.tcx()), @@ -689,8 +700,9 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { trait_ref.substs.types.get_slice(subst::SelfSpace).len()); assert_eq!(m.generics.regions.get_slice(subst::SelfSpace).len(), trait_ref.substs.regions().get_slice(subst::SelfSpace).len()); + Some(Candidate { - rcvr_match_condition: condition, + xform_self_ty: xform_self_ty, rcvr_substs: trait_ref.substs.clone(), method_ty: m, origin: MethodTypeParam(MethodParam { @@ -698,7 +710,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { method_num: method_num, }) }) - }) + }) } // Do a search through a list of bounds, using a callback to actually @@ -720,41 +732,24 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { continue; } - let trait_items = ty::trait_items(tcx, bound_trait_ref.def_id); - match trait_items.iter().position(|ti| { - match *ti { - ty::MethodTraitItem(ref m) => { - m.explicit_self != ty::StaticExplicitSelfCategory && - m.ident.name == self.m_name - } - ty::TypeTraitItem(_) => false, - } - }) { - Some(pos) => { - let method = match (*trait_items)[pos] { - ty::MethodTraitItem(ref method) => (*method).clone(), - ty::TypeTraitItem(_) => { - tcx.sess.bug("typechecking associated type as \ - though it were a method") - } - }; + let (pos, method) = match trait_method(tcx, bound_trait_ref.def_id, self.m_name) { + Some(v) => v, + None => { continue; } + }; - match mk_cand(self, - bound_trait_ref, - method, - pos) { - Some(cand) => { - debug!("pushing inherent candidate for param: {}", - cand.repr(self.tcx())); - self.inherent_candidates.push(cand); - } - None => {} + if !self.has_applicable_self(&*method) { + self.record_static_candidate(TraitSource(bound_trait_ref.def_id)); + } else { + match mk_cand(self, + bound_trait_ref, + method, + pos) { + Some(cand) => { + debug!("pushing inherent candidate for param: {}", + cand.repr(self.tcx())); + self.inherent_candidates.push(cand); } - } - None => { - debug!("trait doesn't contain method: {}", - bound_trait_ref.def_id); - // check next trait or bound + None => {} } } } @@ -766,83 +761,47 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { // metadata if necessary. ty::populate_implementations_for_type_if_necessary(self.tcx(), did); - let impl_items = self.tcx().impl_items.borrow(); for impl_infos in self.tcx().inherent_impls.borrow().find(&did).iter() { for impl_did in impl_infos.iter() { - let items = &(*impl_items)[*impl_did]; - self.push_candidates_from_impl(*impl_did, - items.as_slice(), - false); + self.push_candidates_from_inherent_impl(*impl_did); } } } - fn push_candidates_from_impl(&mut self, - impl_did: DefId, - impl_items: &[ImplOrTraitItemId], - is_extension: bool) { - let did = if self.report_statics == ReportStaticMethods { - // we only want to report each base trait once - match ty::impl_trait_ref(self.tcx(), impl_did) { - Some(trait_ref) => trait_ref.def_id, - None => impl_did - } - } else { - impl_did - }; - - if !self.impl_dups.insert(did) { + fn push_candidates_from_inherent_impl(&mut self, + impl_did: DefId) { + if !self.impl_dups.insert(impl_did) { return; // already visited } - debug!("push_candidates_from_impl: {} {}", - token::get_name(self.m_name), - impl_items.iter() - .map(|&did| { - ty::impl_or_trait_item(self.tcx(), - did.def_id()).ident() - }) - .collect::>() - .repr(self.tcx())); - - let method = match impl_items.iter() - .map(|&did| { - ty::impl_or_trait_item(self.tcx(), - did.def_id()) - }) - .find(|m| { - m.ident().name == self.m_name - }) { - Some(ty::MethodTraitItem(method)) => method, - Some(ty::TypeTraitItem(_)) | None => { - // No method with the right name. - return - } + let method = match impl_method(self.tcx(), impl_did, self.m_name) { + Some(m) => m, + None => { return; } // No method with correct name on this impl }; - // determine the `self` of the impl with fresh - // variables for each parameter: + debug!("push_candidates_from_inherent_impl: impl_did={} method={}", + impl_did.repr(self.tcx()), + method.repr(self.tcx())); + + if !self.has_applicable_self(&*method) { + // No receiver declared. Not a candidate. + return self.record_static_candidate(ImplSource(impl_did)); + } + + // Determine the `self` of the impl with fresh + // variables for each parameter. let span = self.self_expr.map_or(self.span, |e| e.span); let TypeAndSubsts { substs: impl_substs, - ty: impl_ty + ty: _impl_ty } = impl_self_ty(self.fcx, span, impl_did); - let condition = match method.explicit_self { - ByReferenceExplicitSelfCategory(_, mt) if mt == MutMutable => - RcvrMatchesIfEqtype(impl_ty), - _ => - RcvrMatchesIfSubtype(impl_ty) - }; - - let candidates = if is_extension { - &mut self.extension_candidates - } else { - &mut self.inherent_candidates - }; + // Determine the receiver type that the method itself expects. + let xform_self_ty = + self.xform_self_ty(&method, &impl_substs); - candidates.push(Candidate { - rcvr_match_condition: condition, + self.inherent_candidates.push(Candidate { + xform_self_ty: xform_self_ty, rcvr_substs: impl_substs, origin: MethodStatic(method.def_id), method_ty: method, @@ -855,7 +814,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { fn search_for_autoderefd_method(&self, self_ty: ty::t, autoderefs: uint) - -> Option { + -> Option { // Hacky. For overloaded derefs, there may be an adjustment // added to the expression from the outside context, so we do not store // an explicit adjustment, but rather we hardwire the single deref @@ -868,8 +827,10 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { let adjustment = Some((self.self_expr.unwrap().id, ty::AdjustDerefRef(auto_deref_ref))); match self.search_for_method(self_ty) { - None => None, - Some(method) => { + None => { + None + } + Some(Ok(method)) => { debug!("(searching for autoderef'd method) writing \ adjustment {} for {}", adjustment, self.ty_to_string(self_ty)); match adjustment { @@ -878,7 +839,10 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } None => {} } - Some(method) + Some(Ok(method)) + } + Some(Err(error)) => { + Some(Err(error)) } } } @@ -942,7 +906,9 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { // Takes an [T] - an unwrapped DST pointer (either ~ or &) // [T] to &[T] or &&[T] (note that we started with a &[T] or ~[T] which has // been implicitly derefed). - fn auto_slice_vec(&self, ty: ty::t, autoderefs: uint) -> Option { + fn auto_slice_vec(&self, ty: ty::t, autoderefs: uint) + -> Option + { let tcx = self.tcx(); debug!("auto_slice_vec {}", ppaux::ty_to_string(tcx, ty)); @@ -973,7 +939,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } // [T, ..len] -> [T] or &[T] or &&[T] - fn auto_unsize_vec(&self, ty: ty::t, autoderefs: uint, len: uint) -> Option { + fn auto_unsize_vec(&self, ty: ty::t, autoderefs: uint, len: uint) -> Option { let tcx = self.tcx(); debug!("auto_unsize_vec {}", ppaux::ty_to_string(tcx, ty)); @@ -1009,7 +975,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { }) } - fn auto_slice_str(&self, autoderefs: uint) -> Option { + fn auto_slice_str(&self, autoderefs: uint) -> Option { let tcx = self.tcx(); debug!("auto_slice_str"); @@ -1031,7 +997,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } // Coerce Box/&Trait instances to &Trait. - fn auto_slice_trait(&self, ty: ty::t, autoderefs: uint) -> Option { + fn auto_slice_trait(&self, ty: ty::t, autoderefs: uint) -> Option { debug!("auto_slice_trait"); match ty::get(ty).sty { ty_trait(box ty::TyTrait { @@ -1055,7 +1021,8 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { fn search_for_autofatptrd_method(&self, self_ty: ty::t, autoderefs: uint) - -> Option { + -> Option + { /*! * Searches for a candidate by converting things like * `~[]` to `&[]`. @@ -1082,7 +1049,8 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } fn search_for_autoptrd_method(&self, self_ty: ty::t, autoderefs: uint) - -> Option { + -> Option + { /*! * * Converts any type `T` to `&M T` where `M` is an @@ -1116,12 +1084,13 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } fn search_for_some_kind_of_autorefd_method( - &self, - kind: |Region, ast::Mutability| -> ty::AutoRef, - autoderefs: uint, - mutbls: &[ast::Mutability], - mk_autoref_ty: |ast::Mutability, ty::Region| -> ty::t) - -> Option { + &self, + kind: |Region, ast::Mutability| -> ty::AutoRef, + autoderefs: uint, + mutbls: &[ast::Mutability], + mk_autoref_ty: |ast::Mutability, ty::Region| -> ty::t) + -> Option + { // Hacky. For overloaded derefs, there may be an adjustment // added to the expression from the outside context, so we do not store // an explicit adjustment, but rather we hardwire the single deref @@ -1164,7 +1133,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { None } - fn search_for_method(&self, rcvr_ty: ty::t) -> Option { + fn search_for_method(&self, rcvr_ty: ty::t) -> Option { debug!("search_for_method(rcvr_ty={})", self.ty_to_string(rcvr_ty)); let _indenter = indenter(); @@ -1181,49 +1150,26 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } debug!("searching extension candidates"); - self.consider_candidates(rcvr_ty, self.extension_candidates.as_slice()) + self.consider_extension_candidates(rcvr_ty) } fn consider_candidates(&self, rcvr_ty: ty::t, candidates: &[Candidate]) - -> Option { + -> Option { let relevant_candidates = self.filter_candidates(rcvr_ty, candidates); if relevant_candidates.len() == 0 { return None; } - if self.report_statics == ReportStaticMethods { - // lookup should only be called with ReportStaticMethods if a regular lookup failed - assert!(relevant_candidates.iter() - .all(|c| { - c.method_ty.explicit_self == ty::StaticExplicitSelfCategory - })); - - self.tcx().sess.fileline_note(self.span, - "found defined static methods, maybe a `self` is missing?"); - - for (idx, candidate) in relevant_candidates.iter().enumerate() { - self.report_candidate(idx, &candidate.origin); - } - - // return something so we don't get errors for every mutability - return Some(MethodCallee { - origin: relevant_candidates[0].origin.clone(), - ty: ty::mk_err(), - substs: subst::Substs::empty() - }); - } - if relevant_candidates.len() > 1 { - span_err!(self.tcx().sess, self.span, E0034, - "multiple applicable methods in scope"); - for (idx, candidate) in relevant_candidates.iter().enumerate() { - self.report_candidate(idx, &candidate.origin); - } + let sources = relevant_candidates.iter() + .map(|candidate| candidate.to_source()) + .collect(); + return Some(Err(Ambiguity(sources))); } - Some(self.confirm_candidate(rcvr_ty, &relevant_candidates[0])) + Some(Ok(self.confirm_candidate(rcvr_ty, &relevant_candidates[0]))) } fn filter_candidates(&self, rcvr_ty: ty::t, candidates: &[Candidate]) -> Vec { @@ -1250,13 +1196,110 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { _ => false } }) { - relevant_candidates.push(candidate_a.clone()); + relevant_candidates.push((*candidate_a).clone()); } } relevant_candidates } + fn consider_extension_candidates(&self, rcvr_ty: ty::t) + -> Option + { + let mut selcx = traits::SelectionContext::new(self.infcx(), + &self.fcx.inh.param_env, + self.fcx); + + let extension_evaluations: Vec<_> = + self.extension_candidates.iter() + .map(|ext| self.probe_extension_candidate(&mut selcx, rcvr_ty, ext)) + .collect(); + + // How many traits can apply? + let applicable_evaluations_count = + extension_evaluations.iter() + .filter(|eval| eval.may_apply()) + .count(); + + // Determine whether there are multiple traits that could apply. + if applicable_evaluations_count > 1 { + let sources = + self.extension_candidates.iter() + .zip(extension_evaluations.iter()) + .filter(|&(_, eval)| eval.may_apply()) + .map(|(ext, _)| ext.to_source()) + .collect(); + return Some(Err(Ambiguity(sources))); + } + + // Determine whether there are no traits that could apply. + if applicable_evaluations_count == 0 { + return None; + } + + // Exactly one trait applies. It itself could *still* be ambiguous thanks + // to coercions. + let applicable_evaluation = extension_evaluations.iter() + .position(|eval| eval.may_apply()) + .unwrap(); + let match_data = match extension_evaluations[applicable_evaluation] { + traits::MethodMatched(data) => data, + traits::MethodAmbiguous(ref impl_def_ids) => { + let sources = impl_def_ids.iter().map(|&d| ImplSource(d)).collect(); + return Some(Err(Ambiguity(sources))); + } + traits::MethodDidNotMatch => { + self.bug("Method did not match and yet may_apply() is true") + } + }; + + let extension = &self.extension_candidates[applicable_evaluation]; + + debug!("picked extension={}", extension.repr(self.tcx())); + + // We have to confirm the method match. This will cause the type variables + // in the obligation to be appropriately unified based on the subtyping/coercion + // between `rcvr_ty` and `extension.xform_self_ty`. + selcx.confirm_method_match(rcvr_ty, extension.xform_self_ty, + &extension.obligation, match_data); + + // Finally, construct the candidate, now that everything is + // known, and confirm *that*. Note that whatever we pick + // (impl, whatever) we can always use the same kind of origin + // (trait-based method dispatch). + let candidate = Candidate { + xform_self_ty: extension.xform_self_ty, + rcvr_substs: extension.obligation.trait_ref.substs.clone(), + method_ty: extension.method_ty.clone(), + origin: MethodTypeParam(MethodParam{trait_ref: extension.obligation.trait_ref.clone(), + method_num: extension.method_num}) + }; + + // Confirming the candidate will do the final work of + // instantiating late-bound variables, unifying things, and + // registering trait obligations (including + // `extension.obligation`, which should be a requirement of + // the `Self` trait). + let callee = self.confirm_candidate(rcvr_ty, &candidate); + + select_fcx_obligations_where_possible(self.fcx); + + Some(Ok(callee)) + } + + fn probe_extension_candidate(&self, + selcx: &mut traits::SelectionContext, + rcvr_ty: ty::t, + candidate: &ExtensionCandidate) + -> traits::MethodMatchResult + { + debug!("probe_extension_candidate(rcvr_ty={}, candidate.obligation={})", + rcvr_ty.repr(self.tcx()), + candidate.obligation.repr(self.tcx())); + + selcx.evaluate_method_obligation(rcvr_ty, candidate.xform_self_ty, &candidate.obligation) + } + fn confirm_candidate(&self, rcvr_ty: ty::t, candidate: &Candidate) -> MethodCallee { @@ -1273,12 +1316,17 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { self.ty_to_string(rcvr_ty), candidate.repr(self.tcx())); - self.enforce_object_limitations(candidate); - self.enforce_drop_trait_limitations(candidate); + let mut rcvr_substs = candidate.rcvr_substs.clone(); + + if !self.enforce_object_limitations(candidate) { + // Here we change `Self` from `Trait` to `err` in the case that + // this is an illegal object method. This is necessary to prevent + // the user from getting strange, derivative errors when the method + // takes an argument/return-type of type `Self` etc. + rcvr_substs.types.get_mut_slice(SelfSpace)[0] = ty::mk_err(); + } - // static methods should never have gotten this far: - assert!(candidate.method_ty.explicit_self != - ty::StaticExplicitSelfCategory); + self.enforce_drop_trait_limitations(candidate); // Determine the values for the generic parameters of the method. // If they were not explicitly supplied, just construct fresh @@ -1310,7 +1358,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { self.span, candidate.method_ty.generics.regions.get_slice(subst::FnSpace)); - let all_substs = candidate.rcvr_substs.clone().with_method(m_types, m_regions); + let all_substs = rcvr_substs.with_method(m_types, m_regions); let ref bare_fn_ty = candidate.method_ty.fty; @@ -1319,33 +1367,14 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { bare_fn_ty.repr(tcx), all_substs.repr(tcx)); - let fn_sig = &bare_fn_ty.sig; - let inputs = match candidate.origin { - MethodTraitObject(..) => { - // For annoying reasons, we've already handled the - // substitution of self for object calls. - let args = fn_sig.inputs.slice_from(1).iter().map(|t| { - t.subst(tcx, &all_substs) - }); - Some(fn_sig.inputs[0]).into_iter().chain(args).collect() - } - _ => fn_sig.inputs.subst(tcx, &all_substs) - }; - let fn_sig = ty::FnSig { - binder_id: fn_sig.binder_id, - inputs: inputs, - output: fn_sig.output.subst(tcx, &all_substs), - variadic: fn_sig.variadic - }; + let fn_sig = bare_fn_ty.sig.subst(tcx, &all_substs); debug!("after subst, fty={}", fn_sig.repr(tcx)); // Replace any bound regions that appear in the function // signature with region variables - let (_, fn_sig) = replace_late_bound_regions_in_fn_sig( - tcx, &fn_sig, - |br| self.fcx.infcx().next_region_var( - infer::LateBoundRegion(self.span, br))); + let fn_sig = + self.replace_late_bound_regions_with_fresh_var(fn_sig.binder_id, &fn_sig); let transformed_self_ty = fn_sig.inputs[0]; let fty = ty::mk_bare_fn(tcx, ty::BareFnTy { sig: fn_sig, @@ -1371,10 +1400,35 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } } - self.fcx.add_obligations_for_parameters( - traits::ObligationCause::misc(self.span), - &all_substs, - &candidate.method_ty.generics); + // FIXME(DST). Super hack. For a method on a trait object + // `Trait`, the generic signature requires that + // `Self:Trait`. Since, for an object, we bind `Self` to the + // type `Trait`, this leads to an obligation + // `Trait:Trait`. Until such time we DST is fully implemented, + // that obligation is not necessarily satisfied. (In the + // future, it would be.) + // + // To sidestep this, we overwrite the binding for `Self` with + // `err` (just for trait objects) when we generate the + // obligations. This causes us to generate the obligation + // `err:Trait`, and the error type is considered to implement + // all traits, so we're all good. Hack hack hack. + match candidate.origin { + MethodTraitObject(..) => { + let mut temp_substs = all_substs.clone(); + temp_substs.types.get_mut_slice(SelfSpace)[0] = ty::mk_err(); + self.fcx.add_obligations_for_parameters( + traits::ObligationCause::misc(self.span), + &temp_substs, + &candidate.method_ty.generics); + } + _ => { + self.fcx.add_obligations_for_parameters( + traits::ObligationCause::misc(self.span), + &all_substs, + &candidate.method_ty.generics); + } + } MethodCallee { origin: candidate.origin.clone(), @@ -1480,7 +1534,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } } - fn enforce_object_limitations(&self, candidate: &Candidate) { + fn enforce_object_limitations(&self, candidate: &Candidate) -> bool { /*! * There are some limitations to calling functions through an * object, because (a) the self type is not known @@ -1493,7 +1547,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { MethodStatic(..) | MethodTypeParam(..) | MethodStaticUnboxedClosure(..) => { - return; // not a call to a trait instance + return true; // not a call to a trait instance } MethodTraitObject(..) => {} } @@ -1504,6 +1558,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { self.span, "cannot call a method without a receiver \ through an object"); + return false; } ty::ByValueExplicitSelfCategory | @@ -1512,51 +1567,50 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } // reason (a) above - let check_for_self_ty = |ty| { + let check_for_self_ty = |ty| -> bool { if ty::type_has_self(ty) { span_err!(self.tcx().sess, self.span, E0038, "cannot call a method whose type contains a \ self-type through an object"); - true - } else { false + } else { + true } }; let ref sig = candidate.method_ty.fty.sig; - let mut found_self_ty = false; - for &input_ty in sig.inputs.iter() { - if check_for_self_ty(input_ty) { - found_self_ty = true; - break; + for &input_ty in sig.inputs[1..].iter() { + if !check_for_self_ty(input_ty) { + return false; } } - if !found_self_ty { - check_for_self_ty(sig.output); + if !check_for_self_ty(sig.output) { + return false; } if candidate.method_ty.generics.has_type_params(subst::FnSpace) { // reason (b) above span_err!(self.tcx().sess, self.span, E0039, "cannot call a generic method through an object"); + return false; } + + true } fn enforce_drop_trait_limitations(&self, candidate: &Candidate) { // No code can call the finalize method explicitly. - let bad; - match candidate.origin { + let bad = match candidate.origin { MethodStatic(method_id) => { - bad = self.tcx().destructors.borrow().contains(&method_id); + self.tcx().destructors.borrow().contains(&method_id) + } + MethodStaticUnboxedClosure(_) => { + false } - MethodStaticUnboxedClosure(_) => bad = false, - // FIXME: does this properly enforce this on everything now - // that self has been merged in? -sully MethodTypeParam(MethodParam { trait_ref: ref trait_ref, .. }) | MethodTraitObject(MethodObject { trait_ref: ref trait_ref, .. }) => { - bad = self.tcx().destructor_for_type.borrow() - .contains_key(&trait_ref.def_id); + Some(trait_ref.def_id) == self.tcx().lang_items.drop_trait() } - } + }; if bad { span_err!(self.tcx().sess, self.span, E0040, @@ -1570,213 +1624,141 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { debug!("is_relevant(rcvr_ty={}, candidate={})", self.ty_to_string(rcvr_ty), candidate.repr(self.tcx())); - return match candidate.method_ty.explicit_self { - StaticExplicitSelfCategory => { - debug!("(is relevant?) explicit self is static"); - self.report_statics == ReportStaticMethods - } - - ByValueExplicitSelfCategory => { - debug!("(is relevant?) explicit self is by-value"); - match ty::get(rcvr_ty).sty { - ty::ty_uniq(typ) => { - match ty::get(typ).sty { - ty::ty_trait(box ty::TyTrait { - def_id: self_did, - .. - }) => { - rcvr_matches_object(self_did, candidate) || - rcvr_matches_ty(self.fcx, - rcvr_ty, - candidate) - } - _ => { - rcvr_matches_ty(self.fcx, rcvr_ty, candidate) - } - } - } - _ => rcvr_matches_ty(self.fcx, rcvr_ty, candidate) - } - } - - ByReferenceExplicitSelfCategory(_, m) => { - debug!("(is relevant?) explicit self is a region"); - match ty::get(rcvr_ty).sty { - ty::ty_rptr(_, mt) => { - match ty::get(mt.ty).sty { - ty::ty_trait(box ty::TyTrait { def_id: self_did, .. }) => { - mutability_matches(mt.mutbl, m) && - rcvr_matches_object(self_did, candidate) - } - _ => mutability_matches(mt.mutbl, m) && - rcvr_matches_ty(self.fcx, mt.ty, candidate) - } - } - - _ => false - } - } - - ByBoxExplicitSelfCategory => { - debug!("(is relevant?) explicit self is a unique pointer"); - match ty::get(rcvr_ty).sty { - ty::ty_uniq(typ) => { - match ty::get(typ).sty { - ty::ty_trait(box ty::TyTrait { def_id: self_did, .. }) => { - rcvr_matches_object(self_did, candidate) - } - _ => rcvr_matches_ty(self.fcx, typ, candidate), - } - } + infer::can_mk_subty(self.infcx(), rcvr_ty, candidate.xform_self_ty).is_ok() + } - _ => false - } - } - }; + fn infcx(&'a self) -> &'a infer::InferCtxt<'a, 'tcx> { + &self.fcx.inh.infcx + } - fn rcvr_matches_object(self_did: ast::DefId, - candidate: &Candidate) -> bool { - match candidate.rcvr_match_condition { - RcvrMatchesIfObject(desired_did) => { - self_did == desired_did - } - RcvrMatchesIfSubtype(_) | RcvrMatchesIfEqtype(_) => { - false - } - } - } + fn tcx(&self) -> &'a ty::ctxt<'tcx> { + self.fcx.tcx() + } - fn rcvr_matches_ty(fcx: &FnCtxt, - rcvr_ty: ty::t, - candidate: &Candidate) -> bool { - match candidate.rcvr_match_condition { - RcvrMatchesIfObject(_) => { - false - } - RcvrMatchesIfSubtype(of_type) => { - fcx.can_mk_subty(rcvr_ty, of_type).is_ok() - } - RcvrMatchesIfEqtype(of_type) => { - fcx.can_mk_eqty(rcvr_ty, of_type).is_ok() - } - } - } + fn ty_to_string(&self, t: ty::t) -> String { + self.fcx.infcx().ty_to_string(t) + } - fn mutability_matches(self_mutbl: ast::Mutability, - candidate_mutbl: ast::Mutability) - -> bool { - //! True if `self_mutbl <: candidate_mutbl` - self_mutbl == candidate_mutbl - } + fn bug(&self, s: &str) -> ! { + self.tcx().sess.span_bug(self.span, s) } - fn report_candidate(&self, idx: uint, origin: &MethodOrigin) { - match *origin { - MethodStatic(impl_did) => { - let did = if self.report_statics == ReportStaticMethods { - // If we're reporting statics, we want to report the trait - // definition if possible, rather than an impl - match ty::trait_item_of_item(self.tcx(), impl_did) { - None | Some(TypeTraitItemId(_)) => { - debug!("(report candidate) No trait method \ - found"); - impl_did - } - Some(MethodTraitItemId(trait_did)) => { - debug!("(report candidate) Found trait ref"); - trait_did - } - } - } else { - // If it is an instantiated default method, use the original - // default method for error reporting. - match provided_source(self.tcx(), impl_did) { - None => impl_did, - Some(did) => did - } - }; - self.report_static_candidate(idx, did) - } - MethodStaticUnboxedClosure(did) => { - self.report_static_candidate(idx, did) - } - MethodTypeParam(ref mp) => { - self.report_param_candidate(idx, mp.trait_ref.def_id) + fn has_applicable_self(&self, method: &ty::Method) -> bool { + // "fast track" -- check for usage of sugar + match method.explicit_self { + StaticExplicitSelfCategory => { + // fallthrough } - MethodTraitObject(ref mo) => { - self.report_trait_candidate(idx, mo.trait_ref.def_id) + ByValueExplicitSelfCategory | + ByReferenceExplicitSelfCategory(..) | + ByBoxExplicitSelfCategory => { + return true; } } - } - fn report_static_candidate(&self, idx: uint, did: DefId) { - let span = if did.krate == ast::LOCAL_CRATE { - self.tcx().map.span(did.node) - } else { - self.span - }; - span_note!(self.tcx().sess, span, - "candidate #{} is `{}`", - idx + 1u, ty::item_path_str(self.tcx(), did)); - } - - fn report_param_candidate(&self, idx: uint, did: DefId) { - span_note!(self.tcx().sess, self.span, - "candidate #{} derives from the bound `{}`", - idx + 1u, ty::item_path_str(self.tcx(), did)); + // FIXME -- check for types that deref to `Self`, + // like `Rc` and so on. + // + // Note also that the current code will break if this type + // includes any of the type parameters defined on the method + // -- but this could be overcome. + return false; } - fn report_trait_candidate(&self, idx: uint, did: DefId) { - span_note!(self.tcx().sess, self.span, - "candidate #{} derives from the type of the receiver, \ - which is the trait `{}`", - idx + 1u, ty::item_path_str(self.tcx(), did)); + fn record_static_candidate(&mut self, source: CandidateSource) { + self.static_candidates.push(source); } - fn infcx(&'a self) -> &'a infer::InferCtxt<'a, 'tcx> { - &self.fcx.inh.infcx + fn xform_self_ty(&self, method: &Rc, substs: &subst::Substs) -> ty::t { + let xform_self_ty = method.fty.sig.inputs[0].subst(self.tcx(), substs); + self.replace_late_bound_regions_with_fresh_var(method.fty.sig.binder_id, &xform_self_ty) } - fn tcx(&self) -> &'a ty::ctxt<'tcx> { - self.fcx.tcx() + fn replace_late_bound_regions_with_fresh_var(&self, binder_id: ast::NodeId, value: &T) -> T + where T : TypeFoldable + Repr + { + let (_, value) = replace_late_bound_regions( + self.fcx.tcx(), + binder_id, + value, + |br| self.fcx.infcx().next_region_var(infer::LateBoundRegion(self.span, br))); + value } +} - fn ty_to_string(&self, t: ty::t) -> String { - self.fcx.infcx().ty_to_string(t) - } +fn trait_method(tcx: &ty::ctxt, + trait_def_id: ast::DefId, + method_name: ast::Name) + -> Option<(uint, Rc)> +{ + /*! + * 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 did_to_string(&self, did: DefId) -> String { - ty::item_path_str(self.tcx(), did) - } + let trait_items = ty::trait_items(tcx, trait_def_id); + trait_items + .iter() + .enumerate() + .find(|&(_, ref item)| item.ident().name == method_name) + .and_then(|(idx, item)| item.as_opt_method().map(|m| (idx, m))) +} - fn bug(&self, s: &str) -> ! { - self.tcx().sess.span_bug(self.span, s) - } +fn impl_method(tcx: &ty::ctxt, + impl_def_id: ast::DefId, + method_name: ast::Name) + -> Option> +{ + let impl_items = tcx.impl_items.borrow(); + let impl_items = impl_items.find(&impl_def_id).unwrap(); + impl_items + .iter() + .map(|&did| ty::impl_or_trait_item(tcx, did.def_id())) + .find(|m| m.ident().name == method_name) + .and_then(|item| item.as_opt_method()) } impl Repr for Candidate { fn repr(&self, tcx: &ty::ctxt) -> String { - format!("Candidate(rcvr_ty={}, rcvr_substs={}, method_ty={}, \ - origin={})", - self.rcvr_match_condition.repr(tcx), + format!("Candidate(rcvr_ty={}, rcvr_substs={}, method_ty={}, origin={})", + self.xform_self_ty.repr(tcx), self.rcvr_substs.repr(tcx), self.method_ty.repr(tcx), self.origin) } } -impl Repr for RcvrMatchCondition { +impl Repr for ExtensionCandidate { fn repr(&self, tcx: &ty::ctxt) -> String { - match *self { - RcvrMatchesIfObject(d) => { - format!("RcvrMatchesIfObject({})", d.repr(tcx)) + format!("ExtensionCandidate(obligation={}, xform_self_ty={}, method_ty={}, method_num={})", + self.obligation.repr(tcx), + self.xform_self_ty.repr(tcx), + self.method_ty.repr(tcx), + self.method_num) + } +} + +impl Candidate { + fn to_source(&self) -> CandidateSource { + match self.origin { + MethodStatic(def_id) => { + ImplSource(def_id) + } + MethodStaticUnboxedClosure(..) => { + fail!("MethodStaticUnboxedClosure only used in trans") } - RcvrMatchesIfSubtype(t) => { - format!("RcvrMatchesIfSubtype({})", t.repr(tcx)) + MethodTypeParam(ref param) => { + TraitSource(param.trait_ref.def_id) } - RcvrMatchesIfEqtype(t) => { - format!("RcvrMatchesIfEqtype({})", t.repr(tcx)) + MethodTraitObject(ref obj) => { + TraitSource(obj.trait_ref.def_id) } } } } + +impl ExtensionCandidate { + fn to_source(&self) -> CandidateSource { + TraitSource(self.obligation.trait_ref.def_id) + } +} diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index b9ad4a5f72a2a..5f7b31e573ade 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -77,6 +77,7 @@ type parameter). */ +use driver::session::Session; use middle::const_eval; use middle::def; use middle::lang_items::IteratorItem; @@ -2924,46 +2925,24 @@ fn check_expr_with_unifier(fcx: &FnCtxt, fcx.expr_ty(&*rcvr)); let tps = tps.iter().map(|ast_ty| fcx.to_ty(&**ast_ty)).collect::>(); - let fn_ty = match method::lookup(fcx, expr, &*rcvr, + let fn_ty = match method::lookup(fcx, + expr, + &*rcvr, method_name.node.name, - expr_t, tps.as_slice(), + expr_t, + tps.as_slice(), DontDerefArgs, CheckTraitsAndInherentMethods, - AutoderefReceiver, IgnoreStaticMethods) { - Some(method) => { + AutoderefReceiver) { + Ok(method) => { let method_ty = method.ty; let method_call = MethodCall::expr(expr.id); fcx.inh.method_map.borrow_mut().insert(method_call, method); method_ty } - None => { - debug!("(checking method call) failing expr is {}", expr.id); - - fcx.type_error_message(method_name.span, - |actual| { - format!("type `{}` does not implement any \ - method in scope named `{}`", - actual, - token::get_ident(method_name.node)) - }, - expr_t, - None); - - // Add error type for the result + Err(error) => { + method::report_error(fcx, method_name.span, expr_t, method_name.node.name, error); fcx.write_error(expr.id); - - // Check for potential static matches (missing self parameters) - method::lookup(fcx, - expr, - &*rcvr, - method_name.node.name, - expr_t, - tps.as_slice(), - DontDerefArgs, - CheckTraitsAndInherentMethods, - DontAutoderefReceiver, - ReportStaticMethods); - ty::mk_err() } }; @@ -3466,9 +3445,8 @@ fn check_expr_with_unifier(fcx: &FnCtxt, tps.as_slice(), DontDerefArgs, CheckTraitsAndInherentMethods, - AutoderefReceiver, - IgnoreStaticMethods) { - Some(_) => { + AutoderefReceiver) { + Ok(_) => { fcx.type_error_message( field.span, |actual| { @@ -3481,7 +3459,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, "maybe a missing `()` to call it? If not, try an anonymous function."); } - None => { + Err(_) => { fcx.type_error_message( expr.span, |actual| { diff --git a/src/libsyntax/ast_map/mod.rs b/src/libsyntax/ast_map/mod.rs index 60e4db405d766..b82a4a0b99718 100644 --- a/src/libsyntax/ast_map/mod.rs +++ b/src/libsyntax/ast_map/mod.rs @@ -536,6 +536,14 @@ impl<'ast> Map<'ast> { .unwrap_or_else(|| fail!("AstMap.span: could not find span for id {}", id)) } + pub fn def_id_span(&self, def_id: DefId, fallback: Span) -> Span { + if def_id.krate == LOCAL_CRATE { + self.span(def_id.node) + } else { + fallback + } + } + pub fn node_to_string(&self, id: NodeId) -> String { node_id_to_string(self, id) }