From dad2db7c83de71063b9311eb3e41bdf4027616f6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Oct 2014 09:07:45 -0400 Subject: [PATCH] Change method lookup infrastructure to use the trait methods. Instead of tracking individual candidates per impl, we just track one candidate for the extension trait itself, and let the trait resolution handle walking the individual impls and so forth. Also change the interface to report back a richer notion of error. --- src/librustc/middle/ty.rs | 11 + src/librustc/middle/typeck/check/method.rs | 1234 ++++++++++---------- src/librustc/middle/typeck/check/mod.rs | 48 +- src/libsyntax/ast_map/mod.rs | 8 + 4 files changed, 640 insertions(+), 661 deletions(-) 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) }