From 04386f4217275f3a745820af476d8af2a2b4a492 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 19 Sep 2014 16:07:31 -0400 Subject: [PATCH 01/13] Prune unused parameters. --- src/librustc/middle/typeck/check/method.rs | 8 ++--- src/librustc/middle/typeck/check/mod.rs | 36 +++++++--------------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 798e4acd2910e..9399c3a475a3c 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -174,9 +174,7 @@ pub fn lookup_in_trait<'a, 'tcx>( 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, ... . - autoderef_receiver: AutoderefReceiverFlag, - report_statics: StaticMethodsFlag) + supplied_tps: &'a [ty::t]) // The list of types X, Y, ... . -> Option { let mut lcx = LookupContext { fcx: fcx, @@ -189,8 +187,8 @@ pub fn lookup_in_trait<'a, 'tcx>( extension_candidates: Vec::new(), deref_args: check::DoDerefArgs, check_traits: CheckTraitsOnly, - autoderef_receiver: autoderef_receiver, - report_statics: report_statics, + autoderef_receiver: DontAutoderefReceiver, + report_statics: IgnoreStaticMethods, }; debug!("method lookup_in_trait(self_ty={}, self_expr={})", diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 4aea9cfa293d3..ebd3754028134 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -2117,9 +2117,7 @@ fn try_overloaded_call<'a>(fcx: &FnCtxt, method_name, function_trait, callee_type, - [], - DontAutoderefReceiver, - IgnoreStaticMethods) { + []) { None => continue, Some(method_callee) => method_callee, }; @@ -2160,7 +2158,7 @@ fn try_overloaded_deref(fcx: &FnCtxt, (PreferMutLvalue, Some(trait_did)) => { method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x), token::intern("deref_mut"), trait_did, - base_ty, [], DontAutoderefReceiver, IgnoreStaticMethods) + base_ty, []) } _ => None }; @@ -2170,7 +2168,7 @@ fn try_overloaded_deref(fcx: &FnCtxt, (None, Some(trait_did)) => { method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x), token::intern("deref"), trait_did, - base_ty, [], DontAutoderefReceiver, IgnoreStaticMethods) + base_ty, []) } (method, _) => method }; @@ -2231,9 +2229,7 @@ fn try_overloaded_slice(fcx: &FnCtxt, token::intern(method_name), trait_did, base_ty, - [], - DontAutoderefReceiver, - IgnoreStaticMethods) + []) } _ => None, } @@ -2256,9 +2252,7 @@ fn try_overloaded_slice(fcx: &FnCtxt, token::intern(method_name), trait_did, base_ty, - [], - DontAutoderefReceiver, - IgnoreStaticMethods) + []) } _ => None, } @@ -2314,9 +2308,7 @@ fn try_overloaded_index(fcx: &FnCtxt, token::intern("index_mut"), trait_did, base_ty, - [], - DontAutoderefReceiver, - IgnoreStaticMethods) + []) } _ => None, }; @@ -2330,9 +2322,7 @@ fn try_overloaded_index(fcx: &FnCtxt, token::intern("index"), trait_did, base_ty, - [], - DontAutoderefReceiver, - IgnoreStaticMethods) + []) } (method, _) => method, }; @@ -2376,9 +2366,7 @@ fn lookup_method_for_for_loop(fcx: &FnCtxt, token::intern("next"), trait_did, expr_type, - [], - DontAutoderefReceiver, - IgnoreStaticMethods); + []); // Regardless of whether the lookup succeeds, check the method arguments // so that we have *some* type for each argument. @@ -3069,13 +3057,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt, trait_did: Option, lhs: &'a ast::Expr, rhs: Option<&P>, - autoderef_receiver: AutoderefReceiverFlag, unbound_method: ||) -> ty::t { let method = match trait_did { Some(trait_did) => { method::lookup_in_trait(fcx, op_ex.span, Some(lhs), opname, - trait_did, lhs_ty, &[], autoderef_receiver, - IgnoreStaticMethods) + trait_did, lhs_ty, &[]) } None => None }; @@ -3249,7 +3235,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, } }; lookup_op_method(fcx, ex, lhs_resolved_t, token::intern(name), - trait_did, lhs_expr, Some(rhs), DontAutoderefReceiver, || { + trait_did, lhs_expr, Some(rhs), || { fcx.type_error_message(ex.span, |actual| { format!("binary operation `{}` cannot be applied to type `{}`", ast_util::binop_to_string(op), @@ -3266,7 +3252,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, rhs_expr: &ast::Expr, rhs_t: ty::t) -> ty::t { lookup_op_method(fcx, ex, rhs_t, token::intern(mname), - trait_did, rhs_expr, None, DontAutoderefReceiver, || { + trait_did, rhs_expr, None, || { fcx.type_error_message(ex.span, |actual| { format!("cannot apply unary operator `{}` to type `{}`", op_str, actual) From e6a1936d26d03759d80f2f53411c883cc3462ef4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Oct 2014 08:51:43 -0400 Subject: [PATCH 02/13] Implement "method matching". Similar to trait matching, but with the twist that we have to take the transformed self type into account, and allow for coercion between the receiver and the actual type declared on the impl. --- src/librustc/middle/traits/doc.rs | 7 + src/librustc/middle/traits/mod.rs | 18 +- src/librustc/middle/traits/select.rs | 447 +++++++++++++++++++++++++-- 3 files changed, 422 insertions(+), 50 deletions(-) diff --git a/src/librustc/middle/traits/doc.rs b/src/librustc/middle/traits/doc.rs index 742c4cb5de031..f24121d9a3a5f 100644 --- a/src/librustc/middle/traits/doc.rs +++ b/src/librustc/middle/traits/doc.rs @@ -272,4 +272,11 @@ nested obligation `int : Bar` to find out that `U=uint`. It would be good to only do *just as much* nested resolution as necessary. Currently, though, we just do a full resolution. +## Method matching + +Method dispach follows a slightly different path than normal trait +selection. This is because it must account for the transformed self +type of the receiver and various other complications. The procedure is +described in `select.rs` in the "METHOD MATCHING" section. + */ diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index f0282804540f5..76715561b0397 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -24,6 +24,8 @@ use syntax::codemap::{Span, DUMMY_SP}; pub use self::fulfill::FulfillmentContext; pub use self::select::SelectionContext; pub use self::select::SelectionCache; +pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; +pub use self::select::{MethodMatchedData}; // intentionally don't export variants pub use self::util::supertraits; pub use self::util::transitive_bounds; pub use self::util::Supertraits; @@ -219,22 +221,6 @@ pub struct VtableParamData { pub bound: Rc, } -pub fn evaluate_obligation<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, - param_env: &ty::ParameterEnvironment, - obligation: &Obligation, - typer: &Typer<'tcx>) - -> bool -{ - /*! - * Attempts to resolve the obligation given. Returns `None` if - * we are unable to resolve, either because of ambiguity or - * due to insufficient inference. - */ - - let mut selcx = select::SelectionContext::new(infcx, param_env, typer); - selcx.evaluate_obligation(obligation) -} - pub fn select_inherent_impl<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, param_env: &ty::ParameterEnvironment, typer: &Typer<'tcx>, diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index aca7054018dc6..90aabca29812c 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -62,6 +62,23 @@ pub struct SelectionCache { hashmap: RefCell, SelectionResult>>, } +pub enum MethodMatchResult { + MethodMatched(MethodMatchedData), + MethodAmbiguous(/* list of impls that could apply */ Vec), + MethodDidNotMatch, +} + +#[deriving(Show)] +pub enum MethodMatchedData { + // In the case of a precise match, we don't really need to store + // how the match was found. So don't. + PreciseMethodMatch, + + // In the case of a coercion, we need to know the precise impl so + // that we can determine the type to which things were coerced. + CoerciveMethodMatch(/* impl we matched */ ast::DefId) +} + /** * The selection process begins by considering all impls, where * clauses, and so forth that might resolve an obligation. Sometimes @@ -190,27 +207,51 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /////////////////////////////////////////////////////////////////////////// // EVALUATION // - // Tests whether an obligation can be selected or whether an impl can be - // applied to particular types. It skips the "confirmation" step and - // hence completely ignores output type parameters. + // Tests whether an obligation can be selected or whether an impl + // can be applied to particular types. It skips the "confirmation" + // step and hence completely ignores output type parameters. // // The result is "true" if the obliation *may* hold and "false" if // we can be sure it does not. - pub fn evaluate_obligation(&mut self, - obligation: &Obligation) - -> bool + pub fn evaluate_obligation_intercrate(&mut self, + obligation: &Obligation) + -> bool { /*! * Evaluates whether the obligation `obligation` can be - * satisfied (by any means). + * satisfied (by any means). This "intercrate" version allows + * for the possibility that unbound type variables may be + * instantiated with types from another crate. This is + * important for coherence. In practice this means that + * unbound type variables must always be considered ambiguous. */ - debug!("evaluate_obligation({})", + debug!("evaluate_obligation_intercrate({})", obligation.repr(self.tcx())); let stack = self.push_stack(None, obligation); - self.evaluate_stack(&stack).may_apply() + self.evaluate_stack_intercrate(&stack).may_apply() + } + + pub fn evaluate_obligation_intracrate(&mut self, + obligation: &Obligation) + -> bool + { + /*! + * Evaluates whether the obligation `obligation` can be + * satisfied (by any means). This "intracrate" version does + * not allow for the possibility that unbound type variables + * may be instantiated with types from another crate; hence, + * if there are unbound inputs but no crates locally visible, + * it considers the result to be unimplemented. + */ + + debug!("evaluate_obligation_intracrate({})", + obligation.repr(self.tcx())); + + let stack = self.push_stack(None, obligation); + self.evaluate_stack_intracrate(&stack).may_apply() } fn evaluate_builtin_bound_recursively(&mut self, @@ -246,12 +287,26 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.repr(self.tcx())); let stack = self.push_stack(previous_stack.map(|x| x), obligation); - let result = self.evaluate_stack(&stack); + + // FIXME(#17901) -- Intercrate vs intracrate resolution is a + // tricky question here. For coherence, we want + // intercrate. Also, there was a nasty cycle around impls like + // `impl Eq for Vec` (which would wind up checking + // whether `$0:Eq`, where $0 was the value substituted for + // `T`, which could then be checked against the very same + // impl). This problem is avoided by the stricter rules around + // unbound type variables by intercrate. I suspect that in the + // latter case a more fine-grained rule would suffice (i.e., + // consider it ambiguous if even 1 impl matches, no need to + // figure out which one, but call it unimplemented if 0 impls + // match). + let result = self.evaluate_stack_intercrate(&stack); + debug!("result: {}", result); result } - fn evaluate_stack(&mut self, + fn evaluate_stack_intercrate(&mut self, stack: &ObligationStack) -> EvaluationResult { @@ -259,13 +314,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // an impl. Even if there are no impls in this crate, perhaps // the type would be unified with something from another crate // that does provide an impl. - let input_types = &stack.skol_trait_ref.substs.types; + let input_types = self.input_types(&*stack.skol_trait_ref); if input_types.iter().any(|&t| ty::type_is_skolemized(t)) { - debug!("evaluate_stack({}) --> unbound argument, must be ambiguous", + debug!("evaluate_stack_intercrate({}) --> unbound argument, must be ambiguous", stack.skol_trait_ref.repr(self.tcx())); return EvaluatedToAmbig; } + self.evaluate_stack_intracrate(stack) + } + + fn evaluate_stack_intracrate(&mut self, + stack: &ObligationStack) + -> EvaluationResult + { // If there is any previous entry on the stack that precisely // matches this obligation, then we can assume that the // obligation is satisfied for now (still all other conditions @@ -290,7 +352,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .skip(1) // skip top-most frame .any(|prev| stack.skol_trait_ref == prev.skol_trait_ref) { - debug!("evaluate_stack({}) --> recursive", + debug!("evaluate_stack_intracrate({}) --> recursive", stack.skol_trait_ref.repr(self.tcx())); return EvaluatedToOk; } @@ -320,7 +382,311 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx.probe(|| { match self.match_impl(impl_def_id, obligation) { Ok(substs) => { - let vtable_impl = self.vtable_impl(impl_def_id, substs, obligation.cause, 0); + let vtable_impl = self.vtable_impl(impl_def_id, + substs, + obligation.cause, + obligation.recursion_depth + 1); + self.winnow_selection(None, VtableImpl(vtable_impl)).may_apply() + } + Err(()) => { + false + } + } + }) + } + + /////////////////////////////////////////////////////////////////////////// + // METHOD MATCHING + // + // Method matching is a variation on the normal select/evaluation + // situation. In this scenario, rather than having a full trait + // reference to select from, we start with an expression like + // `receiver.method(...)`. This means that we have `rcvr_ty`, the + // type of the receiver, and we have a possible trait that + // supplies `method`. We must determine whether the receiver is + // applicable, taking into account the transformed self type + // declared on `method`. We also must consider the possibility + // that `receiver` can be *coerced* into a suitable type (for + // example, a receiver type like `&(Any+Send)` might be coerced + // into a receiver like `&Any` to allow for method dispatch). See + // the body of `evaluate_method_obligation()` for more details on + // the algorithm. + + pub fn evaluate_method_obligation(&mut self, + rcvr_ty: ty::t, + xform_self_ty: ty::t, + obligation: &Obligation) + -> MethodMatchResult + { + /*! + * Determine whether a trait-method is applicable to a receiver of + * type `rcvr_ty`. *Does not affect the inference state.* + * + * - `rcvr_ty` -- type of the receiver + * - `xform_self_ty` -- transformed self type declared on the method, with `Self` + * to a fresh type variable + * - `obligation` -- a reference to the trait where the method is declared, with + * the input types on the trait replaced with fresh type variables + */ + + // Here is the situation. We have a trait method declared (say) like so: + // + // trait TheTrait { + // fn the_method(self: Rc, ...) { ... } + // } + // + // And then we have a call looking (say) like this: + // + // let x: Rc = ...; + // x.the_method() + // + // Now we want to decide if `TheTrait` is applicable. As a + // human, we can see that `TheTrait` is applicable if there is + // an impl for the type `Foo`. But how does the compiler know + // what impl to look for, given that our receiver has type + // `Rc`? We need to take the method's self type into + // account. + // + // On entry to this function, we have the following inputs: + // + // - `rcvr_ty = Rc` + // - `xform_self_ty = Rc<$0>` + // - `obligation = $0 as TheTrait` + // + // We do the match in two phases. The first is a *precise + // match*, which means that no coercion is required. This is + // the preferred way to match. It works by first making + // `rcvr_ty` a subtype of `xform_self_ty`. This unifies `$0` + // and `Foo`. We can then evaluate (roughly as normal) the + // trait reference `Foo as TheTrait`. + // + // If this fails, we fallback to a coercive match, described below. + + match self.infcx.probe(|| self.match_method_precise(rcvr_ty, xform_self_ty, obligation)) { + Ok(()) => { return MethodMatched(PreciseMethodMatch); } + Err(_) => { } + } + + // Coercive matches work slightly differently and cannot + // completely reuse the normal trait matching machinery + // (though they employ many of the same bits and pieces). To + // see how it works, let's continue with our previous example, + // but with the following declarations: + // + // ``` + // trait Foo : Bar { .. } + // trait Bar : Baz { ... } + // trait Baz { ... } + // impl TheTrait for Bar { + // fn the_method(self: Rc, ...) { ... } + // } + // ``` + // + // Now we see that the receiver type `Rc` is actually an + // object type. And in fact the impl we want is an impl on the + // supertrait `Rc`. The precise matching procedure won't + // find it, however, because `Rc` is not a subtype of + // `Rc` -- it is *coercible* to `Rc` (actually, such + // coercions are not yet implemented, but let's leave that + // aside for now). + // + // To handle this case, we employ a different procedure. Recall + // that our initial state is as follows: + // + // - `rcvr_ty = Rc` + // - `xform_self_ty = Rc<$0>` + // - `obligation = $0 as TheTrait` + // + // We now go through each impl and instantiate all of its type + // variables, yielding the trait reference that the impl + // provides. In our example, the impl would provide `Bar as + // TheTrait`. Next we (try to) unify the trait reference that + // the impl provides with the input obligation. This would + // unify `$0` and `Bar`. Now we can see whether the receiver + // type (`Rc`) is *coercible to* the transformed self + // type (`Rc<$0> == Rc`). In this case, the answer is + // yes, so the impl is considered a candidate. + // + // Note that there is the possibility of ambiguity here, even + // when all types are known. In our example, this might occur + // if there was *also* an impl of `TheTrait` for `Baz`. In + // this case, `Rc` would be coercible to both `Rc` + // and `Rc`. (Note that it is not a *coherence violation* + // to have impls for both `Bar` and `Baz`, despite this + // ambiguity). In this case, we report an error, listing all + // the applicable impls. The use can explicitly "up-coerce" + // to the type they want. + // + // Note that this coercion step only considers actual impls + // found in the source. This is because all the + // compiler-provided impls (such as those for unboxed + // closures) do not have relevant coercions. This simplifies + // life immensly. + + let mut impls = + self.assemble_method_candidates_from_impls(rcvr_ty, xform_self_ty, obligation); + + if impls.len() > 1 { + impls.retain(|&c| self.winnow_method_impl(c, rcvr_ty, xform_self_ty, obligation)); + } + + if impls.len() > 1 { + return MethodAmbiguous(impls); + } + + match impls.pop() { + Some(def_id) => MethodMatched(CoerciveMethodMatch(def_id)), + None => MethodDidNotMatch + } + } + + pub fn confirm_method_match(&mut self, + rcvr_ty: ty::t, + xform_self_ty: ty::t, + obligation: &Obligation, + data: MethodMatchedData) + { + /*! + * Given the successful result of a method match, this + * function "confirms" the result, which basically repeats the + * various matching operations, but outside of any snapshot so + * that their effects are committed into the inference state. + */ + + let is_ok = match data { + PreciseMethodMatch => { + self.match_method_precise(rcvr_ty, xform_self_ty, obligation).is_ok() + } + + CoerciveMethodMatch(impl_def_id) => { + self.match_method_coerce(impl_def_id, rcvr_ty, xform_self_ty, obligation).is_ok() + } + }; + + if !is_ok { + self.tcx().sess.span_bug( + obligation.cause.span, + format!("match not repeatable: {}, {}, {}, {}", + rcvr_ty.repr(self.tcx()), + xform_self_ty.repr(self.tcx()), + obligation.repr(self.tcx()), + data)[]); + } + } + + fn match_method_precise(&mut self, + rcvr_ty: ty::t, + xform_self_ty: ty::t, + obligation: &Obligation) + -> Result<(),()> + { + /*! + * Implements the *precise method match* procedure described in + * `evaluate_method_obligation()`. + */ + + self.infcx.commit_if_ok(|| { + match self.infcx.sub_types(false, infer::Misc(obligation.cause.span), + rcvr_ty, xform_self_ty) { + Ok(()) => { } + Err(_) => { return Err(()); } + } + + if self.evaluate_obligation_intracrate(obligation) { + Ok(()) + } else { + Err(()) + } + }) + } + + fn assemble_method_candidates_from_impls(&mut self, + rcvr_ty: ty::t, + xform_self_ty: ty::t, + obligation: &Obligation) + -> Vec + { + /*! + * Assembles a list of potentially applicable impls using the + * *coercive match* procedure described in + * `evaluate_method_obligation()`. + */ + + let mut candidates = Vec::new(); + + let all_impls = self.all_impls(obligation.trait_ref.def_id); + for &impl_def_id in all_impls.iter() { + self.infcx.probe(|| { + match self.match_method_coerce(impl_def_id, rcvr_ty, xform_self_ty, obligation) { + Ok(_) => { candidates.push(impl_def_id); } + Err(_) => { } + } + }); + } + + candidates + } + + fn match_method_coerce(&mut self, + impl_def_id: ast::DefId, + rcvr_ty: ty::t, + xform_self_ty: ty::t, + obligation: &Obligation) + -> Result + { + /*! + * Applies the *coercive match* procedure described in + * `evaluate_method_obligation()` to a particular impl. + */ + + // This is almost always expected to succeed. It + // causes the impl's self-type etc to be unified with + // the type variable that is shared between + // obligation/xform_self_ty. In our example, after + // this is done, the type of `xform_self_ty` would + // change from `Rc<$0>` to `Rc` (because $0 is + // unified with `Foo`). + let substs = try!(self.match_impl(impl_def_id, obligation)); + + // Next, check whether we can coerce. For now we require + // that the coercion be a no-op. + let origin = infer::Misc(obligation.cause.span); + match infer::mk_coercety(self.infcx, true, origin, + rcvr_ty, xform_self_ty) { + Ok(None) => { /* Fallthrough */ } + Ok(Some(_)) | Err(_) => { return Err(()); } + } + + Ok(substs) + } + + fn winnow_method_impl(&mut self, + impl_def_id: ast::DefId, + rcvr_ty: ty::t, + xform_self_ty: ty::t, + obligation: &Obligation) + -> bool + { + /*! + * A version of `winnow_impl` applicable to coerice method + * matching. This is basically the same as `winnow_impl` but + * it uses the method matching procedure and is specific to + * impls. + */ + + debug!("winnow_method_impl: impl_def_id={} rcvr_ty={} xform_self_ty={} obligation={}", + impl_def_id.repr(self.tcx()), + rcvr_ty.repr(self.tcx()), + xform_self_ty.repr(self.tcx()), + obligation.repr(self.tcx())); + + self.infcx.probe(|| { + match self.match_method_coerce(impl_def_id, rcvr_ty, xform_self_ty, obligation) { + Ok(substs) => { + let vtable_impl = self.vtable_impl(impl_def_id, + substs, + obligation.cause, + obligation.recursion_depth + 1); self.winnow_selection(None, VtableImpl(vtable_impl)).may_apply() } Err(()) => { @@ -456,24 +822,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Ok(None); } - // If there are *NO* candidates, that might mean either that - // there is no impl or just that we can't know anything for - // sure. + // If there are *NO* candidates, that there are no impls -- + // that we know of, anyway. Note that in the case where there + // are unbound type variables within the obligation, it might + // be the case that you could still satisfy the obligation + // from another crate by instantiating the type variables with + // a type from another crate that does have an impl. This case + // is checked for in `evaluate_obligation` (and hence users + // who might care about this case, like coherence, should use + // that function). if candidates.len() == 0 { - // Annoying edge case: if there are no impls, then there - // is no way that this trait reference is implemented, - // *unless* it contains unbound variables. In that case, - // it is possible that one of those unbound variables will - // be bound to a new type from some other crate which will - // also contain impls. - let skol_obligation_self_ty = self.infcx.skolemize(stack.obligation.self_ty()); - return if !self.contains_skolemized_types(skol_obligation_self_ty) { - debug!("0 matches, unimpl"); - Err(Unimplemented) - } else { - debug!("0 matches, ambig"); - Ok(None) - }; + return Err(Unimplemented); } // Just one candidate left. @@ -491,7 +850,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // scope. Otherwise, use the generic tcx cache, since the // result holds across all environments. if - cache_skol_trait_ref.substs.types.iter().any( + self.input_types(&**cache_skol_trait_ref).iter().any( |&t| ty::type_has_self(t) || ty::type_has_params(t)) { &self.param_env.selection_cache @@ -1291,8 +1650,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // it'll do for now until we get the new trait-bound // region skolemization working. let (_, new_signature) = - regionmanip::replace_late_bound_regions_in_fn_sig( + regionmanip::replace_late_bound_regions( self.tcx(), + closure_type.sig.binder_id, &closure_type.sig, |br| self.infcx.next_region_var( infer::LateBoundRegion(obligation.cause.span, br))); @@ -1365,6 +1725,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { trait_ref: Rc) -> Result<(),()> { + debug!("match_trait_refs: obligation={} trait_ref={}", + obligation.repr(self.tcx()), + trait_ref.repr(self.tcx())); + let origin = infer::RelateOutputImplTypes(obligation.cause.span); match self.infcx.sub_trait_refs(false, origin, @@ -1591,6 +1955,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { found_skol } + + fn input_types<'a>(&self, trait_ref: &'a ty::TraitRef) -> &'a [ty::t] { + // Select only the "input types" from a trait-reference. + trait_ref.substs.types.as_slice() + } } impl Repr for Candidate { @@ -1648,3 +2017,13 @@ impl EvaluationResult { } } } + +impl MethodMatchResult { + pub fn may_apply(&self) -> bool { + match *self { + MethodMatched(_) => true, + MethodAmbiguous(_) => true, + MethodDidNotMatch => false, + } + } +} From 5c505b75b1d0bf8ab63c26e9317fa9bc794b086e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Oct 2014 08:54:31 -0400 Subject: [PATCH 03/13] Permit DST types to unify like other types. Also: 1. stop eagerly coercing from `[T, ..n]` to `[T]` only do so if requested. 2. permit error to be interact more freely. Fixes #17178. --- src/librustc/middle/typeck/infer/coercion.rs | 4 +- src/librustc/middle/typeck/infer/combine.rs | 39 +++++--------------- src/librustc/middle/typeck/infer/sub.rs | 17 ++------- 3 files changed, 15 insertions(+), 45 deletions(-) diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs index 9f88bec7f4225..e44aa3e8221b8 100644 --- a/src/librustc/middle/typeck/infer/coercion.rs +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -72,7 +72,6 @@ use middle::typeck::infer::{CoerceResult, resolve_type, Coercion}; use middle::typeck::infer::combine::{CombineFields, Combine}; use middle::typeck::infer::sub::Sub; use middle::typeck::infer::resolve::try_resolve_tvar_shallow; -use util::common::indenter; use util::ppaux; use util::ppaux::Repr; @@ -93,7 +92,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { debug!("Coerce.tys({} => {})", a.repr(self.get_ref().infcx.tcx), b.repr(self.get_ref().infcx.tcx)); - let _indent = indenter(); // Special case: if the subtype is a sized array literal (`[T, ..n]`), // then it would get auto-borrowed to `&[T, ..n]` and then DST-ified @@ -411,7 +409,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.unpack_actual_value(ty_b, |sty_b| match (sty_a, sty_b) { - (&ty::ty_vec(t_a, Some(len)), _) => { + (&ty::ty_vec(t_a, Some(len)), &ty::ty_vec(_, None)) => { let ty = ty::mk_vec(tcx, t_a, None); Some((ty, ty::UnsizeLength(len))) } diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index 15c4830646d59..d2e062a20d2ec 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -363,28 +363,6 @@ pub fn super_fn_sigs<'tcx, C: Combine<'tcx>>(this: &C, pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C, a: ty::t, b: ty::t) -> cres { - // This is a horrible hack - historically, [T] was not treated as a type, - // so, for example, &T and &[U] should not unify. In fact the only thing - // &[U] should unify with is &[T]. We preserve that behaviour with this - // check. - fn check_ptr_to_unsized<'tcx, C: Combine<'tcx>>(this: &C, - a: ty::t, - b: ty::t, - a_inner: ty::t, - b_inner: ty::t, - result: ty::t) -> cres { - match (&ty::get(a_inner).sty, &ty::get(b_inner).sty) { - (&ty::ty_vec(_, None), &ty::ty_vec(_, None)) | - (&ty::ty_str, &ty::ty_str) | - (&ty::ty_trait(..), &ty::ty_trait(..)) => Ok(result), - (&ty::ty_vec(_, None), _) | (_, &ty::ty_vec(_, None)) | - (&ty::ty_str, _) | (_, &ty::ty_str) | - (&ty::ty_trait(..), _) | (_, &ty::ty_trait(..)) - => Err(ty::terr_sorts(expected_found(this, a, b))), - _ => Ok(result), - } - } - let tcx = this.infcx().tcx; let a_sty = &ty::get(a).sty; let b_sty = &ty::get(b).sty; @@ -402,6 +380,10 @@ pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C, a: ty::t, b: ty::t) -> cres { + Ok(ty::mk_err()) + } + // Relate integral variables to other types (&ty::ty_infer(IntVar(a_id)), &ty::ty_infer(IntVar(b_id))) => { try!(this.infcx().simple_vars(this.a_is_expected(), @@ -442,8 +424,7 @@ pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C, a: ty::t, b: ty::t) -> cres { + (&ty::ty_float(_), _) => { if ty::get(a).sty == ty::get(b).sty { Ok(a) } else { @@ -494,13 +475,13 @@ pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C, a: ty::t, b: ty::t) -> cres { - let typ = try!(this.tys(a_inner, b_inner)); - check_ptr_to_unsized(this, a, b, a_inner, b_inner, ty::mk_uniq(tcx, typ)) + let typ = try!(this.tys(a_inner, b_inner)); + Ok(ty::mk_uniq(tcx, typ)) } (&ty::ty_ptr(ref a_mt), &ty::ty_ptr(ref b_mt)) => { - let mt = try!(this.mts(a_mt, b_mt)); - check_ptr_to_unsized(this, a, b, a_mt.ty, b_mt.ty, ty::mk_ptr(tcx, mt)) + let mt = try!(this.mts(a_mt, b_mt)); + Ok(ty::mk_ptr(tcx, mt)) } (&ty::ty_rptr(a_r, ref a_mt), &ty::ty_rptr(b_r, ref b_mt)) => { @@ -516,7 +497,7 @@ pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C, a: ty::t, b: ty::t) -> cres try!(this.mts(a_mt, b_mt)) }; - check_ptr_to_unsized(this, a, b, a_mt.ty, b_mt.ty, ty::mk_rptr(tcx, r, mt)) + Ok(ty::mk_rptr(tcx, r, mt)) } (&ty::ty_vec(a_t, sz_a), &ty::ty_vec(b_t, sz_b)) => { diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index 158fda802ff00..cc9ea6528ee84 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -139,30 +139,21 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> { .relate_vars(a_id, SubtypeOf, b_id); Ok(a) } - // The vec/str check here and below is so that we don't unify - // T with [T], this is necessary so we reflect subtyping of references - // (&T does not unify with &[T]) where that in turn is to reflect - // the historical non-typedness of [T]. - (&ty::ty_infer(TyVar(_)), &ty::ty_str) | - (&ty::ty_infer(TyVar(_)), &ty::ty_vec(_, None)) => { - Err(ty::terr_sorts(expected_found(self, a, b))) - } (&ty::ty_infer(TyVar(a_id)), _) => { try!(self.fields .switch_expected() .instantiate(b, SupertypeOf, a_id)); Ok(a) } - - (&ty::ty_str, &ty::ty_infer(TyVar(_))) | - (&ty::ty_vec(_, None), &ty::ty_infer(TyVar(_))) => { - Err(ty::terr_sorts(expected_found(self, a, b))) - } (_, &ty::ty_infer(TyVar(b_id))) => { try!(self.fields.instantiate(a, SubtypeOf, b_id)); Ok(a) } + (&ty::ty_err, _) | (_, &ty::ty_err) => { + Ok(ty::mk_err()) + } + (_, &ty::ty_bot) => { Err(ty::terr_sorts(expected_found(self, a, b))) } From 1562d8cbd890d39640d98afa59424cc8002ea85a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Oct 2014 08:56:27 -0400 Subject: [PATCH 04/13] Generalize the replace-late-bound-regions function to operate over anything that is foldable, not just fn signatures. --- src/librustc/middle/typeck/check/mod.rs | 17 ++++----- .../middle/typeck/check/regionmanip.rs | 35 ++++++++++--------- src/librustc/middle/typeck/check/wf.rs | 6 ++-- src/librustc/middle/typeck/infer/mod.rs | 4 +-- src/librustc/middle/typeck/infer/sub.rs | 4 +-- 5 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index ebd3754028134..b9ad4a5f72a2a 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -97,11 +97,8 @@ use middle::typeck::astconv::{ast_region_to_region, ast_ty_to_ty}; use middle::typeck::astconv; use middle::typeck::check::_match::pat_ctxt; use middle::typeck::check::method::{AutoderefReceiver}; -use middle::typeck::check::method::{AutoderefReceiverFlag}; use middle::typeck::check::method::{CheckTraitsAndInherentMethods}; -use middle::typeck::check::method::{DontAutoderefReceiver}; -use middle::typeck::check::method::{IgnoreStaticMethods, ReportStaticMethods}; -use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig; +use middle::typeck::check::regionmanip::replace_late_bound_regions; use middle::typeck::CrateCtxt; use middle::typeck::infer::{resolve_type, force_tvar}; use middle::typeck::infer; @@ -529,7 +526,7 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, // First, we have to replace any bound regions in the fn type with free ones. // The free region references will be bound the node_id of the body block. - let (_, fn_sig) = replace_late_bound_regions_in_fn_sig(tcx, fn_sig, |br| { + let (_, fn_sig) = replace_late_bound_regions(tcx, fn_sig.binder_id, fn_sig, |br| { ty::ReFree(ty::FreeRegion {scope_id: body.id, bound_region: br}) }); @@ -1531,6 +1528,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self.inh.infcx } + pub fn sess(&self) -> &Session { + &self.tcx().sess + } + pub fn err_count_since_creation(&self) -> uint { self.ccx.tcx.sess.err_count() - self.err_count_on_creation } @@ -2890,7 +2891,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, // Replace any bound regions that appear in the function // signature with region variables - let (_, fn_sig) = replace_late_bound_regions_in_fn_sig(fcx.tcx(), fn_sig, |br| { + let (_, fn_sig) = replace_late_bound_regions(fcx.tcx(), fn_sig.binder_id, fn_sig, |br| { fcx.infcx().next_region_var(infer::LateBoundRegion(call_expr.span, br)) }); @@ -3346,8 +3347,8 @@ fn check_expr_with_unifier(fcx: &FnCtxt, match expected_sty { Some(ty::ty_closure(ref cenv)) => { let (_, sig) = - replace_late_bound_regions_in_fn_sig( - tcx, &cenv.sig, + replace_late_bound_regions( + tcx, cenv.sig.binder_id, &cenv.sig, |_| fcx.inh.infcx.fresh_bound_region(expr.id)); let onceness = match (&store, &cenv.store) { // As the closure type and onceness go, only three diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs index 5d75d590a09f9..a448a93c5178f 100644 --- a/src/librustc/middle/typeck/check/regionmanip.rs +++ b/src/librustc/middle/typeck/check/regionmanip.rs @@ -13,7 +13,7 @@ use middle::subst::{ParamSpace, Subst, Substs}; use middle::ty; use middle::ty_fold; -use middle::ty_fold::TypeFolder; +use middle::ty_fold::{TypeFolder, TypeFoldable}; use syntax::ast; @@ -23,31 +23,34 @@ use util::ppaux::Repr; // Helper functions related to manipulating region types. -pub fn replace_late_bound_regions_in_fn_sig( - tcx: &ty::ctxt, - fn_sig: &ty::FnSig, - mapf: |ty::BoundRegion| -> ty::Region) - -> (HashMap, ty::FnSig) { - debug!("replace_late_bound_regions_in_fn_sig({})", fn_sig.repr(tcx)); +pub fn replace_late_bound_regions( + tcx: &ty::ctxt, + binder_id: ast::NodeId, + value: &T, + map_fn: |ty::BoundRegion| -> ty::Region) + -> (HashMap, T) + where T : TypeFoldable + Repr +{ + debug!("replace_late_bound_regions(binder_id={}, value={})", + binder_id, value.repr(tcx)); let mut map = HashMap::new(); - let fn_sig = { - let mut f = ty_fold::RegionFolder::regions(tcx, |r| { - debug!("region r={}", r.to_string()); + let new_value = { + let mut folder = ty_fold::RegionFolder::regions(tcx, |r| { match r { - ty::ReLateBound(s, br) if s == fn_sig.binder_id => { - * match map.entry(br) { - Vacant(entry) => entry.set(mapf(br)), - Occupied(entry) => entry.into_mut(), + ty::ReLateBound(s, br) if s == binder_id => { + match map.entry(br) { + Vacant(entry) => *entry.set(map_fn(br)), + Occupied(entry) => *entry.into_mut(), } } _ => r } }); - ty_fold::super_fold_sig(&mut f, fn_sig) + value.fold_with(&mut folder) }; debug!("resulting map: {}", map); - (map, fn_sig) + (map, new_value) } pub enum WfConstraint { diff --git a/src/librustc/middle/typeck/check/wf.rs b/src/librustc/middle/typeck/check/wf.rs index 4c3cec1aff42f..dc79fd4aa328c 100644 --- a/src/librustc/middle/typeck/check/wf.rs +++ b/src/librustc/middle/typeck/check/wf.rs @@ -15,7 +15,7 @@ use middle::ty; use middle::ty_fold::{TypeFolder, TypeFoldable}; use middle::typeck::astconv::AstConv; use middle::typeck::check::{FnCtxt, Inherited, blank_fn_ctxt, vtable2, regionck}; -use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig; +use middle::typeck::check::regionmanip::replace_late_bound_regions; use middle::typeck::CrateCtxt; use util::ppaux::Repr; @@ -373,8 +373,8 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> { self.binding_count += 1; let (_, fn_sig) = - replace_late_bound_regions_in_fn_sig( - self.fcx.tcx(), fn_sig, + replace_late_bound_regions( + self.fcx.tcx(), fn_sig.binder_id, fn_sig, |br| ty::ReFree(ty::FreeRegion{scope_id: self.scope_id, bound_region: br})); diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 7c455b85707b8..a466581ef394a 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -28,7 +28,7 @@ use middle::ty::{TyVid, IntVid, FloatVid, RegionVid}; use middle::ty; use middle::ty_fold; use middle::ty_fold::{TypeFolder, TypeFoldable}; -use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig; +use middle::typeck::check::regionmanip::replace_late_bound_regions; use std::cell::{RefCell}; use std::collections::HashMap; use std::rc::Rc; @@ -962,7 +962,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { HashMap) { let (map, fn_sig) = - replace_late_bound_regions_in_fn_sig(self.tcx, fsig, |br| { + replace_late_bound_regions(self.tcx, fsig.binder_id, fsig, |br| { let rvar = self.next_region_var( BoundRegionInFnType(trace.origin.span(), br)); debug!("Bound region {} maps to {}", diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index cc9ea6528ee84..4c04bcc5236f4 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -12,7 +12,7 @@ use middle::ty::{BuiltinBounds}; use middle::ty; use middle::ty::TyVar; -use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig; +use middle::typeck::check::regionmanip::replace_late_bound_regions; use middle::typeck::infer::combine::*; use middle::typeck::infer::{cres, CresCompare}; use middle::typeck::infer::equate::Equate; @@ -189,7 +189,7 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> { // Second, we instantiate each bound region in the supertype with a // fresh concrete region. let (skol_map, b_sig) = { - replace_late_bound_regions_in_fn_sig(self.fields.infcx.tcx, b, |br| { + replace_late_bound_regions(self.fields.infcx.tcx, b.binder_id, b, |br| { let skol = self.fields.infcx.region_vars.new_skolemized(br); debug!("Bound region {} skolemized to {}", bound_region_to_string(self.fields.infcx.tcx, "", false, br), From dad2db7c83de71063b9311eb3e41bdf4027616f6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Oct 2014 09:07:45 -0400 Subject: [PATCH 05/13] 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) } From 98e237a681c798ef6850395cb8483b82fc844452 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Oct 2014 09:10:34 -0400 Subject: [PATCH 06/13] Correct case where the old version of method lookup was incorrectly matching, as far as I can tell. --- src/libcore/num/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 42ea138acd4dc..3dceb42e20653 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -1374,7 +1374,7 @@ macro_rules! checkeddiv_int_impl( if *v == 0 || (*self == $min && *v == -1) { None } else { - Some(self / *v) + Some(*self / *v) } } } @@ -1395,7 +1395,7 @@ macro_rules! checkeddiv_uint_impl( if *v == 0 { None } else { - Some(self / *v) + Some(*self / *v) } } } From df714cfda728e201dd6b2227caa425bca3e048c7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Oct 2014 09:11:10 -0400 Subject: [PATCH 07/13] The new method lookup mechanism typechecks calls against the method type declared in the trait, not in the impl. In some cases that results in tighter rules, and in some cases looser. Correct for that. --- src/librustc/middle/borrowck/graphviz.rs | 8 ++++---- src/librustc/middle/cfg/graphviz.rs | 16 ++++++++-------- ...ded-method-type-parameters-cross-crate-lib.rs | 11 ++++++++++- ...bounded-method-type-parameters-cross-crate.rs | 6 +++--- .../compile-fail/wrong-mul-method-signature.rs | 12 +++++++++--- 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/librustc/middle/borrowck/graphviz.rs b/src/librustc/middle/borrowck/graphviz.rs index aab7fe8f31edc..6c6750ad24b2e 100644 --- a/src/librustc/middle/borrowck/graphviz.rs +++ b/src/librustc/middle/borrowck/graphviz.rs @@ -142,8 +142,8 @@ impl<'a, 'tcx> dot::Labeller<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a, 't } impl<'a, 'tcx> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a, 'tcx> { - fn nodes(&self) -> dot::Nodes<'a, Node<'a>> { self.inner.nodes() } - fn edges(&self) -> dot::Edges<'a, Edge<'a>> { self.inner.edges() } - fn source(&self, edge: &Edge<'a>) -> Node<'a> { self.inner.source(edge) } - fn target(&self, edge: &Edge<'a>) -> Node<'a> { self.inner.target(edge) } + fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { self.inner.nodes() } + fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.inner.edges() } + fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { self.inner.source(edge) } + fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { self.inner.target(edge) } } diff --git a/src/librustc/middle/cfg/graphviz.rs b/src/librustc/middle/cfg/graphviz.rs index bc48e476aec07..fcd9a166c6ab1 100644 --- a/src/librustc/middle/cfg/graphviz.rs +++ b/src/librustc/middle/cfg/graphviz.rs @@ -91,19 +91,19 @@ impl<'a, 'ast> dot::Labeller<'a, Node<'a>, Edge<'a>> for LabelledCFG<'a, 'ast> { } impl<'a> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for &'a cfg::CFG { - fn nodes(&self) -> dot::Nodes<'a, Node<'a>> { + fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { let mut v = Vec::new(); self.graph.each_node(|i, nd| { v.push((i, nd)); true }); dot::maybe_owned_vec::Growable(v) } - fn edges(&self) -> dot::Edges<'a, Edge<'a>> { + fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.graph.all_edges().iter().collect() } - fn source(&self, edge: &Edge<'a>) -> Node<'a> { + fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { let i = edge.source(); (i, self.graph.node(i)) } - fn target(&self, edge: &Edge<'a>) -> Node<'a> { + fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { let i = edge.target(); (i, self.graph.node(i)) } @@ -111,9 +111,9 @@ impl<'a> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for &'a cfg::CFG { impl<'a, 'ast> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for LabelledCFG<'a, 'ast> { - fn nodes(&self) -> dot::Nodes<'a, Node<'a>> { self.cfg.nodes() } - fn edges(&self) -> dot::Edges<'a, Edge<'a>> { self.cfg.edges() } - fn source(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) } - fn target(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) } + fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { self.cfg.nodes() } + fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.cfg.edges() } + fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) } + fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) } } diff --git a/src/test/auxiliary/regions-bounded-method-type-parameters-cross-crate-lib.rs b/src/test/auxiliary/regions-bounded-method-type-parameters-cross-crate-lib.rs index 1e9fd035f44b5..000e42b970356 100644 --- a/src/test/auxiliary/regions-bounded-method-type-parameters-cross-crate-lib.rs +++ b/src/test/auxiliary/regions-bounded-method-type-parameters-cross-crate-lib.rs @@ -9,7 +9,7 @@ // except according to those terms. // Check that method bounds declared on traits/impls in a cross-crate -// scenario work. This is the libary portion of the test. +// scenario work. This is the library portion of the test. pub enum MaybeOwned<'a> { Owned(int), @@ -24,10 +24,19 @@ pub struct Inv<'a> { // invariant w/r/t 'a // trait, so I'll use that as the template for this test. pub trait IntoMaybeOwned<'a> { fn into_maybe_owned(self) -> MaybeOwned<'a>; + + // Note: without this `into_inv` method, the trait is + // contravariant w/r/t `'a`, since if you look strictly at the + // interface, it only returns `'a`. This complicates the + // downstream test since it wants invariance to force an error. + // Hence we add this method. + fn into_inv(self) -> Inv<'a>; + fn bigger_region<'b:'a>(self, b: Inv<'b>); } impl<'a> IntoMaybeOwned<'a> for Inv<'a> { fn into_maybe_owned(self) -> MaybeOwned<'a> { fail!() } + fn into_inv(self) -> Inv<'a> { fail!() } fn bigger_region<'b:'a>(self, b: Inv<'b>) { fail!() } } diff --git a/src/test/compile-fail/regions-bounded-method-type-parameters-cross-crate.rs b/src/test/compile-fail/regions-bounded-method-type-parameters-cross-crate.rs index 06f26800b5031..1705cfec6e2be 100644 --- a/src/test/compile-fail/regions-bounded-method-type-parameters-cross-crate.rs +++ b/src/test/compile-fail/regions-bounded-method-type-parameters-cross-crate.rs @@ -18,15 +18,15 @@ use lib::Inv; use lib::MaybeOwned; use lib::IntoMaybeOwned; -fn call_into_maybe_owned<'a,F:IntoMaybeOwned<'a>>(f: F) { +fn call_into_maybe_owned<'x,F:IntoMaybeOwned<'x>>(f: F) { // Exercise a code path I found to be buggy. We were not encoding // the region parameters from the receiver correctly on trait // methods. f.into_maybe_owned(); } -fn call_bigger_region<'a, 'b>(a: Inv<'a>, b: Inv<'b>) { - // Here the value provided for 'y is 'b, and hence 'b:'a does not hold. +fn call_bigger_region<'x, 'y>(a: Inv<'x>, b: Inv<'y>) { + // Here the value provided for 'y is 'y, and hence 'y:'x does not hold. a.bigger_region(b) //~ ERROR cannot infer } diff --git a/src/test/compile-fail/wrong-mul-method-signature.rs b/src/test/compile-fail/wrong-mul-method-signature.rs index e3aed148a2390..aead739d3e01a 100644 --- a/src/test/compile-fail/wrong-mul-method-signature.rs +++ b/src/test/compile-fail/wrong-mul-method-signature.rs @@ -58,7 +58,13 @@ impl Mul for Vec3 { } pub fn main() { - Vec1 { x: 1.0 } * 2.0; - Vec2 { x: 1.0, y: 2.0 } * 2.0; - Vec3 { x: 1.0, y: 2.0, z: 3.0 } * 2.0; + // Check that the usage goes from the trait declaration: + + let x: Vec1 = Vec1 { x: 1.0 } * 2.0; // this is OK + + let x: Vec2 = Vec2 { x: 1.0, y: 2.0 } * 2.0; // trait had reversed order + //~^ ERROR mismatched types + //~^^ ERROR mismatched types + + let x: i32 = Vec3 { x: 1.0, y: 2.0, z: 3.0 } * 2.0; } From 7f8ca53669e71481201892e37ec57939e5f1ec85 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Oct 2014 09:12:10 -0400 Subject: [PATCH 08/13] Test where the old infrastructure failed to detect the (applicable) impl of `FnMut` for some reason. --- src/test/compile-fail/issue-17033.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/compile-fail/issue-17033.rs b/src/test/compile-fail/issue-17033.rs index 35adb29c9495d..7590546d40a7a 100644 --- a/src/test/compile-fail/issue-17033.rs +++ b/src/test/compile-fail/issue-17033.rs @@ -8,8 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(overloaded_calls)] + fn f<'r>(p: &'r mut fn(p: &mut ())) { - p(()) //~ ERROR expected function, found `&'r mut fn(&mut ())` + p(()) //~ ERROR mismatched types: expected `&mut ()`, found `()` } fn main() {} From e09fc0370188eff0bd3e6f27f72ba4de0d1a6c24 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Oct 2014 09:12:25 -0400 Subject: [PATCH 09/13] Various minor cases where errors are reported in slightly different ways. --- src/test/compile-fail/issue-7575.rs | 19 +++++++++++++------ src/test/compile-fail/selftype-traittype.rs | 2 +- src/test/compile-fail/unique-pinned-nocopy.rs | 2 +- src/test/compile-fail/unique-vec-res.rs | 4 ++-- src/test/compile-fail/vec-res-add.rs | 4 ++-- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/test/compile-fail/issue-7575.rs b/src/test/compile-fail/issue-7575.rs index 768177785cfba..d5a1040d4b4d9 100644 --- a/src/test/compile-fail/issue-7575.rs +++ b/src/test/compile-fail/issue-7575.rs @@ -8,17 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Test the mechanism for warning about possible missing `self` declarations. + trait CtxtFn { fn f8(self, uint) -> uint; - fn f9(uint) -> uint; //~ NOTE candidate # + fn f9(uint) -> uint; //~ NOTE candidate } trait OtherTrait { - fn f9(uint) -> uint; //~ NOTE candidate # + fn f9(uint) -> uint; //~ NOTE candidate } -trait UnusedTrait { // This should never show up as a candidate - fn f9(uint) -> uint; +// Note: this trait is not implemented, but we can't really tell +// whether or not an impl would match anyhow without a self +// declaration to match against, so we wind up printing it as a +// candidate. This seems not unreasonable -- perhaps the user meant to +// implement it, after all. +trait UnusedTrait { + fn f9(uint) -> uint; //~ NOTE candidate } impl CtxtFn for uint { @@ -40,13 +47,13 @@ impl OtherTrait for uint { struct MyInt(int); impl MyInt { - fn fff(i: int) -> int { //~ NOTE candidate #1 is `MyInt::fff` + fn fff(i: int) -> int { //~ NOTE candidate i } } trait ManyImplTrait { - fn is_str() -> bool { //~ NOTE candidate #1 is + fn is_str() -> bool { //~ NOTE candidate false } } diff --git a/src/test/compile-fail/selftype-traittype.rs b/src/test/compile-fail/selftype-traittype.rs index bf7c3b261fd7d..44ee5002dce3d 100644 --- a/src/test/compile-fail/selftype-traittype.rs +++ b/src/test/compile-fail/selftype-traittype.rs @@ -14,7 +14,7 @@ trait add { } fn do_add(x: Box, y: Box) -> Box { - x.plus(y) //~ ERROR cannot call a method whose type contains a self-type through an object + x.plus(y) //~ ERROR E0038 } fn main() {} diff --git a/src/test/compile-fail/unique-pinned-nocopy.rs b/src/test/compile-fail/unique-pinned-nocopy.rs index c812b0d96a23d..0e1e66b40ce33 100644 --- a/src/test/compile-fail/unique-pinned-nocopy.rs +++ b/src/test/compile-fail/unique-pinned-nocopy.rs @@ -19,6 +19,6 @@ impl Drop for r { fn main() { let i = box r { b: true }; - let _j = i.clone(); //~ ERROR not implemented + let _j = i.clone(); //~ ERROR not implement println!("{}", i); } diff --git a/src/test/compile-fail/unique-vec-res.rs b/src/test/compile-fail/unique-vec-res.rs index 79f29c6b359d6..62fabc0b33f3d 100644 --- a/src/test/compile-fail/unique-vec-res.rs +++ b/src/test/compile-fail/unique-vec-res.rs @@ -35,8 +35,8 @@ fn main() { let r1 = vec!(box r { i: i1 }); let r2 = vec!(box r { i: i2 }); f(r1.clone(), r2.clone()); - //~^ ERROR the trait `core::clone::Clone` is not implemented - //~^^ ERROR the trait `core::clone::Clone` is not implemented + //~^ ERROR does not implement any method in scope named `clone` + //~^^ ERROR does not implement any method in scope named `clone` println!("{}", (r2, i1.get())); println!("{}", (r1, i2.get())); } diff --git a/src/test/compile-fail/vec-res-add.rs b/src/test/compile-fail/vec-res-add.rs index bfd52d69cb217..6221806642c99 100644 --- a/src/test/compile-fail/vec-res-add.rs +++ b/src/test/compile-fail/vec-res-add.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[deriving(Show)] struct r { i:int } @@ -23,7 +24,6 @@ fn main() { let i = vec!(r(0)); let j = vec!(r(1)); let k = i + j; - //~^ ERROR not implemented + //~^ ERROR binary operation `+` cannot be applied to type println!("{}", j); - //~^ ERROR not implemented } From 450263de4a93e7dd4eb863c8f4b98fc780b9c385 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Oct 2014 09:13:12 -0400 Subject: [PATCH 10/13] Tests for method resolution in the face of various ambiguities or non-ambiguities. These are mostly new tests though I also revamped (and renamed) some of the existing tests. --- .../method-ambig-one-trait-coerce.rs | 44 +++++++++++++++++ ...method-ambig-one-trait-unknown-int-type.rs | 45 +++++++++++++++++ ...=> method-ambig-two-traits-cross-crate.rs} | 9 ++-- ...=> method-ambig-two-traits-from-bounds.rs} | 4 +- ...d-ambig-two-traits-with-default-method.rs} | 10 ++-- .../compile-fail/method-commit-to-trait.rs | 33 +++++++++++++ .../method-two-trait-defer-resolution-1.rs} | 24 ++++++++-- .../method-two-trait-defer-resolution-2.rs | 48 +++++++++++++++++++ 8 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 src/test/compile-fail/method-ambig-one-trait-coerce.rs create mode 100644 src/test/compile-fail/method-ambig-one-trait-unknown-int-type.rs rename src/test/compile-fail/{ambig_impl_2_exe.rs => method-ambig-two-traits-cross-crate.rs} (74%) rename src/test/compile-fail/{ambig_impl_bounds.rs => method-ambig-two-traits-from-bounds.rs} (76%) rename src/test/compile-fail/{ambig-default-method.rs => method-ambig-two-traits-with-default-method.rs} (65%) create mode 100644 src/test/compile-fail/method-commit-to-trait.rs rename src/test/{compile-fail/ambig_impl_unify.rs => run-pass/method-two-trait-defer-resolution-1.rs} (56%) create mode 100644 src/test/run-pass/method-two-trait-defer-resolution-2.rs diff --git a/src/test/compile-fail/method-ambig-one-trait-coerce.rs b/src/test/compile-fail/method-ambig-one-trait-coerce.rs new file mode 100644 index 0000000000000..e5c3da10df8e9 --- /dev/null +++ b/src/test/compile-fail/method-ambig-one-trait-coerce.rs @@ -0,0 +1,44 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that when we pick a trait based on coercion, versus subtyping, +// we consider all possible coercions equivalent and don't try to pick +// a best one. + +trait Object { } + +trait foo { + fn foo(self) -> int; +} + +impl foo for Box { + fn foo(self) -> int {1} +} + +impl foo for Box { + fn foo(self) -> int {2} +} + +fn test1(x: Box) { + // Ambiguous because we could coerce to either impl: + x.foo(); //~ ERROR E0034 +} + +fn test2(x: Box) { + // Not ambiguous because it is a precise match: + x.foo(); +} + +fn test3(x: Box) { + // Not ambiguous because it is a precise match: + x.foo(); +} + +fn main() { } diff --git a/src/test/compile-fail/method-ambig-one-trait-unknown-int-type.rs b/src/test/compile-fail/method-ambig-one-trait-unknown-int-type.rs new file mode 100644 index 0000000000000..e211db2dcd296 --- /dev/null +++ b/src/test/compile-fail/method-ambig-one-trait-unknown-int-type.rs @@ -0,0 +1,45 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we invoking `foo()` successfully resolves to the trait `foo` +// (prompting the mismatched types error) but does not influence the choice +// of what kind of `Vec` we have, eventually leading to a type error. + +trait foo { + fn foo(&self) -> int; +} + +impl foo for Vec { + fn foo(&self) -> int {1} +} + +impl foo for Vec { + fn foo(&self) -> int {2} +} + +// This is very hokey: we have heuristics to suppress messages about +// type annotations required. But placing these two bits of code into +// distinct functions, in this order, causes us to print out both +// errors I'd like to see. + +fn m1() { + // we couldn't infer the type of the vector just based on calling foo()... + let mut x = Vec::new(); //~ ERROR type annotations required + x.foo(); +} + +fn m2() { + let mut x = Vec::new(); + + // ...but we still resolved `foo()` to the trait and hence know the return type. + let y: uint = x.foo(); //~ ERROR mismatched types +} + +fn main() { } diff --git a/src/test/compile-fail/ambig_impl_2_exe.rs b/src/test/compile-fail/method-ambig-two-traits-cross-crate.rs similarity index 74% rename from src/test/compile-fail/ambig_impl_2_exe.rs rename to src/test/compile-fail/method-ambig-two-traits-cross-crate.rs index 13cceaa71ae06..30e635149c442 100644 --- a/src/test/compile-fail/ambig_impl_2_exe.rs +++ b/src/test/compile-fail/method-ambig-two-traits-cross-crate.rs @@ -8,12 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Test an ambiguity scenario where one copy of the method is available +// from a trait imported from another crate. + // aux-build:ambig_impl_2_lib.rs extern crate ambig_impl_2_lib; use ambig_impl_2_lib::me; trait me2 { fn me(&self) -> uint; } -impl me2 for uint { fn me(&self) -> uint { *self } } //~ NOTE is `uint.me2::me` -fn main() { 1u.me(); } //~ ERROR multiple applicable methods in scope -//~^ NOTE is `ambig_impl_2_lib::uint.me::me` +impl me2 for uint { fn me(&self) -> uint { *self } } +fn main() { 1u.me(); } //~ ERROR E0034 + diff --git a/src/test/compile-fail/ambig_impl_bounds.rs b/src/test/compile-fail/method-ambig-two-traits-from-bounds.rs similarity index 76% rename from src/test/compile-fail/ambig_impl_bounds.rs rename to src/test/compile-fail/method-ambig-two-traits-from-bounds.rs index 9f26e5ae9b380..184927c01357a 100644 --- a/src/test/compile-fail/ambig_impl_bounds.rs +++ b/src/test/compile-fail/method-ambig-two-traits-from-bounds.rs @@ -12,9 +12,7 @@ trait A { fn foo(&self); } trait B { fn foo(&self); } fn foo(t: T) { - t.foo(); //~ ERROR multiple applicable methods in scope - //~^ NOTE candidate #1 derives from the bound `A` - //~^^ NOTE candidate #2 derives from the bound `B` + t.foo(); //~ ERROR E0034 } fn main() {} diff --git a/src/test/compile-fail/ambig-default-method.rs b/src/test/compile-fail/method-ambig-two-traits-with-default-method.rs similarity index 65% rename from src/test/compile-fail/ambig-default-method.rs rename to src/test/compile-fail/method-ambig-two-traits-with-default-method.rs index 56ab6d7da3f89..87efaed4e3dda 100644 --- a/src/test/compile-fail/ambig-default-method.rs +++ b/src/test/compile-fail/method-ambig-two-traits-with-default-method.rs @@ -8,12 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -trait Foo { fn method(&self) {} } //~ NOTE `Foo::method` -trait Bar { fn method(&self) {} } //~ NOTE `Bar::method` +// Test that we correctly report an ambiguity where two applicable traits +// are in scope and the method being invoked is a default method not +// defined directly in the impl. + +trait Foo { fn method(&self) {} } +trait Bar { fn method(&self) {} } impl Foo for uint {} impl Bar for uint {} fn main() { - 1u.method(); //~ ERROR multiple applicable methods in scope + 1u.method(); //~ ERROR E0034 } diff --git a/src/test/compile-fail/method-commit-to-trait.rs b/src/test/compile-fail/method-commit-to-trait.rs new file mode 100644 index 0000000000000..6e4b5e088c9fc --- /dev/null +++ b/src/test/compile-fail/method-commit-to-trait.rs @@ -0,0 +1,33 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we pick `Foo`, and also pick the `impl`, even though in +// this case the vector type `T` is not copyable. This is because +// there is no other reasonable choice. The error you see is thus +// about `T` being non-copyable, not about `Foo` being +// unimplemented. This is better for user too, since it suggests minimal +// diff requird to fix program. + +trait Object { } + +trait Foo { + fn foo(self) -> int; +} + +impl Foo for Vec { + fn foo(self) -> int {1} +} + +fn test1(x: Vec) { + x.foo(); + //~^ ERROR `core::kinds::Copy` is not implemented for the type `T` +} + +fn main() { } diff --git a/src/test/compile-fail/ambig_impl_unify.rs b/src/test/run-pass/method-two-trait-defer-resolution-1.rs similarity index 56% rename from src/test/compile-fail/ambig_impl_unify.rs rename to src/test/run-pass/method-two-trait-defer-resolution-1.rs index 67b7a5a7f3700..e4ae33c1c5055 100644 --- a/src/test/compile-fail/ambig_impl_unify.rs +++ b/src/test/run-pass/method-two-trait-defer-resolution-1.rs @@ -8,20 +8,36 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Test that we pick which version of `foo` to run based on the +// type that is (ultimately) inferred for `x`. trait foo { fn foo(&self) -> int; } impl foo for Vec { - fn foo(&self) -> int {1} //~ NOTE candidate #1 is `Vec.foo::foo` + fn foo(&self) -> int {1} } impl foo for Vec { - fn foo(&self) -> int {2} //~ NOTE candidate #2 is `Vec.foo::foo` + fn foo(&self) -> int {2} +} + +fn call_foo_uint() -> int { + let mut x = Vec::new(); + let y = x.foo(); + x.push(0u); + y +} + +fn call_foo_int() -> int { + let mut x = Vec::new(); + let y = x.foo(); + x.push(0i); + y } fn main() { - let x = Vec::new(); - x.foo(); //~ ERROR multiple applicable methods in scope + assert_eq!(call_foo_uint(), 1); + assert_eq!(call_foo_int(), 2); } diff --git a/src/test/run-pass/method-two-trait-defer-resolution-2.rs b/src/test/run-pass/method-two-trait-defer-resolution-2.rs new file mode 100644 index 0000000000000..cae783e7ea84a --- /dev/null +++ b/src/test/run-pass/method-two-trait-defer-resolution-2.rs @@ -0,0 +1,48 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we pick which version of `Foo` to run based on whether +// the type we (ultimately) inferred for `x` is copyable or not. +// +// In this case, the two versions are both impls of same trait, and +// hence we we can resolve method even without knowing yet which +// version will run (note that the `push` occurs after the call to +// `foo()`). + +trait Foo { + fn foo(&self) -> int; +} + +impl Foo for Vec { + fn foo(&self) -> int {1} +} + +impl Foo for Vec> { + fn foo(&self) -> int {2} +} + +fn call_foo_copy() -> int { + let mut x = Vec::new(); + let y = x.foo(); + x.push(0u); + y +} + +fn call_foo_other() -> int { + let mut x = Vec::new(); + let y = x.foo(); + x.push(box 0i); + y +} + +fn main() { + assert_eq!(call_foo_copy(), 1); + assert_eq!(call_foo_other(), 2); +} From 53ede4403bb79604eb378ba01f664a548a744044 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Oct 2014 09:13:22 -0400 Subject: [PATCH 11/13] Coherence tests that seemed to be missing. --- ...nket-conflicts-with-blanket-implemented.rs | 38 +++++++++++++++++++ ...et-conflicts-with-blanket-unimplemented.rs | 34 +++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs create mode 100644 src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs new file mode 100644 index 0000000000000..c0d82d35e30f4 --- /dev/null +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs @@ -0,0 +1,38 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt::Show; +use std::default::Default; + +// Test that two blanket impls conflict (at least without negative +// bounds). After all, some other crate could implement Even or Odd +// for the same type (though this crate doesn't). + +trait MyTrait { + fn get(&self) -> uint; +} + +trait Even { } + +trait Odd { } + +impl Even for int { } + +impl Odd for uint { } + +impl MyTrait for T { //~ ERROR E0119 + fn get(&self) -> uint { 0 } +} + +impl MyTrait for T { + fn get(&self) -> uint { 0 } +} + +fn main() { } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs new file mode 100644 index 0000000000000..c44844bcf0b6c --- /dev/null +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs @@ -0,0 +1,34 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt::Show; +use std::default::Default; + +// Test that two blanket impls conflict (at least without negative +// bounds). After all, some other crate could implement Even or Odd +// for the same type (though this crate doesn't implement them at all). + +trait MyTrait { + fn get(&self) -> uint; +} + +trait Even { } + +trait Odd { } + +impl MyTrait for T { //~ ERROR E0119 + fn get(&self) -> uint { 0 } +} + +impl MyTrait for T { + fn get(&self) -> uint { 0 } +} + +fn main() { } From 646ec7d9a9df7c5cd00e781e89144fa2c20b2557 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Oct 2014 11:49:06 -0400 Subject: [PATCH 12/13] Address nits by @pcwalton --- src/librustc/middle/traits/select.rs | 13 ++++--------- src/librustc/middle/ty.rs | 8 ++++++++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 90aabca29812c..f923cf1e5903b 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -314,7 +314,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // an impl. Even if there are no impls in this crate, perhaps // the type would be unified with something from another crate // that does provide an impl. - let input_types = self.input_types(&*stack.skol_trait_ref); + let input_types = stack.skol_trait_ref.input_types(); if input_types.iter().any(|&t| ty::type_is_skolemized(t)) { debug!("evaluate_stack_intercrate({}) --> unbound argument, must be ambiguous", stack.skol_trait_ref.repr(self.tcx())); @@ -521,7 +521,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // found in the source. This is because all the // compiler-provided impls (such as those for unboxed // closures) do not have relevant coercions. This simplifies - // life immensly. + // life immensely. let mut impls = self.assemble_method_candidates_from_impls(rcvr_ty, xform_self_ty, obligation); @@ -586,7 +586,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { */ self.infcx.commit_if_ok(|| { - match self.infcx.sub_types(false, infer::Misc(obligation.cause.span), + match self.infcx.sub_types(false, infer::RelateSelfType(obligation.cause.span), rcvr_ty, xform_self_ty) { Ok(()) => { } Err(_) => { return Err(()); } @@ -850,7 +850,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // scope. Otherwise, use the generic tcx cache, since the // result holds across all environments. if - self.input_types(&**cache_skol_trait_ref).iter().any( + cache_skol_trait_ref.input_types().iter().any( |&t| ty::type_has_self(t) || ty::type_has_params(t)) { &self.param_env.selection_cache @@ -1955,11 +1955,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { found_skol } - - fn input_types<'a>(&self, trait_ref: &'a ty::TraitRef) -> &'a [ty::t] { - // Select only the "input types" from a trait-reference. - trait_ref.substs.types.as_slice() - } } impl Repr for Candidate { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 71855f38ba206..a7ce93279bd83 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1254,6 +1254,14 @@ impl TraitRef { pub fn self_ty(&self) -> ty::t { self.substs.self_ty().unwrap() } + + pub fn input_types(&self) -> &[ty::t] { + // Select only the "input types" from a trait-reference. For + // now this is all the types that appear in the + // trait-reference, but it should eventually exclude + // associated types. + self.substs.types.as_slice() + } } /// When type checking, we use the `ParameterEnvironment` to track From b066d09be8454d735972e056d6b978cf48a85009 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Oct 2014 17:36:15 -0400 Subject: [PATCH 13/13] Patch up broken error messages --- src/libcore/ops.rs | 2 +- src/test/compile-fail/issue-17636.rs | 2 +- src/test/compile-fail/issue-2149.rs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 6de5f31c6d198..f6e3e7f61cf93 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -638,7 +638,7 @@ shr_impl!(uint u8 u16 u32 u64 int i8 i16 i32 i64) * ``` */ #[lang="index"] -pub trait Index { +pub trait Index { /// The method for the indexing (`Foo[Bar]`) operation fn index<'a>(&'a self, index: &Index) -> &'a Result; } diff --git a/src/test/compile-fail/issue-17636.rs b/src/test/compile-fail/issue-17636.rs index 635a184a9d30c..ad2ebff59bc51 100644 --- a/src/test/compile-fail/issue-17636.rs +++ b/src/test/compile-fail/issue-17636.rs @@ -15,5 +15,5 @@ pub fn build_archive<'a, I: MyItem<&'a (|&uint|:'a)>>(files: I) {} fn main() { build_archive(&(|_| { })); -//~^ ERROR unable to infer enough type information to locate the impl of the trait `MyItem<&|&uint| +//~^ ERROR not implemented } diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs index 81f57dd96402b..19d210f190595 100644 --- a/src/test/compile-fail/issue-2149.rs +++ b/src/test/compile-fail/issue-2149.rs @@ -18,6 +18,7 @@ impl vec_monad for Vec { let mut r = fail!(); for elt in self.iter() { r = r + f(*elt); } //~^ ERROR the type of this value must be known + //~^^ ERROR not implemented } } fn main() {