diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index b55678e3a50..e05a3237b26 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -417,6 +417,10 @@ impl VecPerParamSpace { self.content.iter() } + pub fn as_slice(&self) -> &[T] { + self.content.as_slice() + } + pub fn all_vecs(&self, pred: |&[T]| -> bool) -> bool { let spaces = [TypeSpace, SelfSpace, FnSpace]; spaces.iter().all(|&space| { pred(self.get_slice(space)) }) diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index 080f9ff5bc7..6dc7fbf5d7c 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -10,9 +10,8 @@ /*! See `doc.rs` for high-level documentation */ -use super::{EvaluatedToMatch, EvaluatedToAmbiguity, EvaluatedToUnmatch}; -use super::{evaluate_impl}; -use super::ObligationCause; +use super::SelectionContext; +use super::Obligation; use super::util; use middle::subst; @@ -28,22 +27,26 @@ pub fn impl_can_satisfy(infcx: &InferCtxt, impl2_def_id: ast::DefId) -> bool { + debug!("impl_can_satisfy(\ + impl1_def_id={}, \ + impl2_def_id={})", + impl1_def_id.repr(infcx.tcx), + impl2_def_id.repr(infcx.tcx)); + // `impl1` provides an implementation of `Foo for Z`. let impl1_substs = util::fresh_substs_for_impl(infcx, DUMMY_SP, impl1_def_id); - let impl1_self_ty = + let impl1_trait_ref = ty::impl_trait_ref(infcx.tcx, impl1_def_id).unwrap() - .self_ty() .subst(infcx.tcx, &impl1_substs); // Determine whether `impl2` can provide an implementation for those // same types. let param_env = ty::empty_parameter_environment(); - match evaluate_impl(infcx, ¶m_env, infcx.tcx, ObligationCause::dummy(), - impl2_def_id, impl1_self_ty) { - EvaluatedToMatch | EvaluatedToAmbiguity => true, - EvaluatedToUnmatch => false, - } + let mut selcx = SelectionContext::new(infcx, ¶m_env, infcx.tcx); + let obligation = Obligation::misc(DUMMY_SP, impl1_trait_ref); + debug!("impl_can_satisfy obligation={}", obligation.repr(infcx.tcx)); + selcx.evaluate_impl(impl2_def_id, &obligation) } pub fn impl_is_local(tcx: &ty::ctxt, diff --git a/src/librustc/middle/traits/doc.rs b/src/librustc/middle/traits/doc.rs index 98db2263874..742c4cb5de0 100644 --- a/src/librustc/middle/traits/doc.rs +++ b/src/librustc/middle/traits/doc.rs @@ -57,7 +57,8 @@ Trait resolution consists of three major parts: resolved by employing an impl which matches the self type, or by using a parameter bound. In the case of an impl, Selecting one obligation can create *nested obligations* because of where clauses - on the impl itself. + on the impl itself. It may also require evaluating those nested + obligations to resolve ambiguities. - FULFILLMENT: The fulfillment code is what tracks that obligations are completely fulfilled. Basically it is a worklist of obligations @@ -100,80 +101,88 @@ candidate that is definitively applicable. In some cases, we may not know whether an impl/where-clause applies or not -- this occurs when the obligation contains unbound inference variables. -One important point is that candidate assembly considers *only the -input types* of the obligation when deciding whether an impl applies -or not. Consider the following example: +The basic idea for candidate assembly is to do a first pass in which +we identify all possible candidates. During this pass, all that we do +is try and unify the type parameters. (In particular, we ignore any +nested where clauses.) Presuming that this unification succeeds, the +impl is added as a candidate. - trait Convert { // T is output, Self is input - fn convert(&self) -> T; - } +Once this first pass is done, we can examine the set of candidates. If +it is a singleton set, then we are done: this is the only impl in +scope that could possibly apply. Otherwise, we can winnow down the set +of candidates by using where clauses and other conditions. If this +reduced set yields a single, unambiguous entry, we're good to go, +otherwise the result is considered ambiguous. - impl Convert for int { ... } +#### The basic process: Inferring based on the impls we see -Now assume we have an obligation `int : Convert`. During -candidate assembly, the impl above would be considered a definitively -applicable candidate, because it has the same self type (`int`). The -fact that the output type parameter `T` is `uint` on the impl and -`char` in the obligation is not considered. +This process is easier if we work through some examples. Consider +the following trait: -#### Skolemization +``` +trait Convert { + fn convert(&self) -> Target; +} +``` -We (at least currently) wish to guarantee "crate concatenability" -- -which basically means that you could take two crates, concatenate -them textually, and the combined crate would continue to compile. The -only real way that this relates to trait matching is with -inference. We have to be careful not to influence unbound type -variables during the selection process, basically. +This trait just has one method. It's about as simple as it gets. It +converts from the (implicit) `Self` type to the `Target` type. If we +wanted to permit conversion between `int` and `uint`, we might +implement `Convert` like so: -Here is an example: +```rust +impl Convert for int { ... } // int -> uint +impl Convert for uint { ... } // uint -> uint +``` - trait Foo { fn method() { ... }} - impl Foo for int { ... } +Now imagine there is some code like the following: - fn something() { - let mut x = None; // `x` has type `Option` - loop { - match x { - Some(ref y) => { // `y` has type ? - y.method(); // (*) - ... - }}} - } +```rust +let x: int = ...; +let y = x.convert(); +``` -The question is, can we resolve the call to `y.method()`? We don't yet -know what type `y` has. However, there is only one impl in scope, and -it is for `int`, so perhaps we could deduce that `y` *must* have type -`int` (and hence the type of `x` is `Option`)? This is actually -sound reasoning: `int` is the only type in scope that could possibly -make this program type check. However, this deduction is a bit -"unstable", though, because if we concatenated with another crate that -defined a newtype and implemented `Foo` for this newtype, then the -inference would fail, because there would be two potential impls, not -one. +The call to convert will generate a trait reference `Convert<$Y> for +int`, where `$Y` is the type variable representing the type of +`y`. When we match this against the two impls we can see, we will find +that only one remains: `Convert for int`. Therefore, we can +select this impl, which will cause the type of `$Y` to be unified to +`uint`. (Note that while assembling candidates, we do the initial +unifications in a transaction, so that they don't affect one another.) -It is unclear how important this property is. It might be nice to drop it. -But for the time being we maintain it. +There are tests to this effect in src/test/run-pass: -The way we do this is by *skolemizing* the obligation self type during -the selection process -- skolemizing means, basically, replacing all -unbound type variables with a new "skolemized" type. Each skolemized -type is basically considered "as if" it were some fresh type that is -distinct from all other types. The skolemization process also replaces -lifetimes with `'static`, see the section on lifetimes below for an -explanation. + traits-multidispatch-infer-convert-source-and-target.rs + traits-multidispatch-infer-convert-target.rs -In the example above, this means that when matching `y.method()` we -would convert the type of `y` from a type variable `?` to a skolemized -type `X`. Then, since `X` cannot unify with `int`, the match would -fail. Special code exists to check that the match failed because a -skolemized type could not be unified with another kind of type -- this is -not considered a definitive failure, but rather an ambiguous result, -since if the type variable were later to be unified with int, then this -obligation could be resolved then. +#### Winnowing: Resolving ambiguities -*Note:* Currently, method matching does not use the trait resolution -code, so if you in fact type in the example above, it may -compile. Hopefully this will be fixed in later patches. +But what happens if there are multiple impls where all the types +unify? Consider this example: + +```rust +trait Get { + fn get(&self) -> Self; +} + +impl Get for T { + fn get(&self) -> T { *self } +} + +impl Get for Box { + fn get(&self) -> Box { box get_it(&**self) } +} +``` + +What happens when we invoke `get_it(&box 1_u16)`, for example? In this +case, the `Self` type is `Box` -- that unifies with both impls, +because the first applies to all types, and the second to all +boxes. In the olden days we'd have called this ambiguous. But what we +do now is do a second *winnowing* pass that considers where clauses +and attempts to remove candidates -- in this case, the first impl only +applies if `Box : Copy`, which doesn't hold. After winnowing, +then, we are left with just one candidate, so we can proceed. There is +a test of this in `src/test/run-pass/traits-conditional-dispatch.rs`. #### Matching @@ -187,11 +196,9 @@ consider some of the nested obligations, in the case of an impl. Because of how that lifetime inference works, it is not possible to give back immediate feedback as to whether a unification or subtype relationship between lifetimes holds or not. Therefore, lifetime -matching is *not* considered during selection. This is achieved by -having the skolemization process just replace *ALL* lifetimes with -`'static`. Later, during confirmation, the non-skolemized self-type -will be unified with the type from the impl (or whatever). This may -yield lifetime constraints that will later be found to be in error (in +matching is *not* considered during selection. This is reflected in +the fact that subregion assignment is infallible. This may yield +lifetime constraints that will later be found to be in error (in contrast, the non-lifetime-constraints have already been checked during selection and can never cause an error, though naturally they may lead to other errors downstream). diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 88685101b31..fb267421245 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -17,6 +17,7 @@ use middle::subst; use middle::ty; use middle::typeck::infer::InferCtxt; use std::rc::Rc; +use std::slice::Items; use syntax::ast; use syntax::codemap::{Span, DUMMY_SP}; @@ -123,13 +124,6 @@ pub enum FulfillmentErrorCode { */ pub type SelectionResult = Result, SelectionError>; -#[deriving(PartialEq,Eq,Show)] -pub enum EvaluationResult { - EvaluatedToMatch, - EvaluatedToAmbiguity, - EvaluatedToUnmatch -} - /** * Given the successful resolution of an obligation, the `Vtable` * indicates where the vtable comes from. Note that while we call this @@ -186,7 +180,7 @@ pub enum Vtable { VtableParam(VtableParamData), /// Successful resolution for a builtin trait. - VtableBuiltin, + VtableBuiltin(VtableBuiltinData), } /** @@ -208,12 +202,17 @@ pub struct VtableImplData { pub nested: subst::VecPerParamSpace } +#[deriving(Show,Clone)] +pub struct VtableBuiltinData { + pub nested: subst::VecPerParamSpace +} + /** * A vtable provided as a parameter by the caller. For example, in a * function like `fn foo(...)`, if the `eq()` method is invoked * on an instance of `T`, the vtable would be of type `VtableParam`. */ -#[deriving(Clone)] +#[deriving(PartialEq,Eq,Clone)] pub struct VtableParamData { // In the above example, this would `Eq` pub bound: Rc, @@ -223,7 +222,7 @@ pub fn evaluate_obligation<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, param_env: &ty::ParameterEnvironment, obligation: &Obligation, typer: &Typer<'tcx>) - -> EvaluationResult + -> bool { /*! * Attempts to resolve the obligation given. Returns `None` if @@ -235,29 +234,6 @@ pub fn evaluate_obligation<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, selcx.evaluate_obligation(obligation) } -pub fn evaluate_impl<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, - param_env: &ty::ParameterEnvironment, - typer: &Typer<'tcx>, - cause: ObligationCause, - impl_def_id: ast::DefId, - self_ty: ty::t) - -> EvaluationResult -{ - /*! - * Tests whether the impl `impl_def_id` can be applied to the self - * type `self_ty`. This is similar to "selection", but simpler: - * - * - It does not take a full trait-ref as input, so it skips over - * the "confirmation" step which would reconcile output type - * parameters. - * - It returns an `EvaluationResult`, which is a tri-value return - * (yes/no/unknown). - */ - - let mut selcx = select::SelectionContext::new(infcx, param_env, typer); - selcx.evaluate_impl(impl_def_id, cause, self_ty) -} - pub fn select_inherent_impl<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, param_env: &ty::ParameterEnvironment, typer: &Typer<'tcx>, @@ -372,12 +348,21 @@ impl ObligationCause { } impl Vtable { + pub fn iter_nested(&self) -> Items { + match *self { + VtableImpl(ref i) => i.iter_nested(), + VtableUnboxedClosure(_) => (&[]).iter(), + VtableParam(_) => (&[]).iter(), + VtableBuiltin(ref i) => i.iter_nested(), + } + } + pub fn map_nested(&self, op: |&N| -> M) -> Vtable { match *self { VtableImpl(ref i) => VtableImpl(i.map_nested(op)), VtableUnboxedClosure(d) => VtableUnboxedClosure(d), VtableParam(ref p) => VtableParam((*p).clone()), - VtableBuiltin => VtableBuiltin, + VtableBuiltin(ref i) => VtableBuiltin(i.map_nested(op)), } } @@ -386,12 +371,16 @@ impl Vtable { VtableImpl(i) => VtableImpl(i.map_move_nested(op)), VtableUnboxedClosure(d) => VtableUnboxedClosure(d), VtableParam(p) => VtableParam(p), - VtableBuiltin => VtableBuiltin, + VtableBuiltin(i) => VtableBuiltin(i.map_move_nested(op)), } } } impl VtableImplData { + pub fn iter_nested(&self) -> Items { + self.nested.iter() + } + pub fn map_nested(&self, op: |&N| -> M) -> VtableImplData @@ -413,11 +402,23 @@ impl VtableImplData { } } -impl EvaluationResult { - pub fn potentially_applicable(&self) -> bool { - match *self { - EvaluatedToMatch | EvaluatedToAmbiguity => true, - EvaluatedToUnmatch => false +impl VtableBuiltinData { + pub fn iter_nested(&self) -> Items { + self.nested.iter() + } + + pub fn map_nested(&self, + op: |&N| -> M) + -> VtableBuiltinData + { + VtableBuiltinData { + nested: self.nested.map(op) + } + } + + pub fn map_move_nested(self, op: |N| -> M) -> VtableBuiltinData { + VtableBuiltinData { + nested: self.nested.map_move(op) } } } @@ -428,4 +429,12 @@ impl FulfillmentError { { FulfillmentError { obligation: obligation, code: code } } + + pub fn is_overflow(&self) -> bool { + match self.code { + CodeAmbiguity => false, + CodeSelectionError(Overflow) => true, + CodeSelectionError(_) => false, + } + } } diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index b86fabccf93..64931b49435 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -9,28 +9,29 @@ // except according to those terms. /*! See `doc.rs` for high-level documentation */ +#![allow(dead_code)] // FIXME -- just temporarily +use super::{ErrorReported}; use super::{Obligation, ObligationCause}; -use super::{EvaluationResult, EvaluatedToMatch, - EvaluatedToAmbiguity, EvaluatedToUnmatch}; use super::{SelectionError, Unimplemented, Overflow, OutputTypeParameterMismatch}; use super::{Selection}; use super::{SelectionResult}; use super::{VtableBuiltin, VtableImpl, VtableParam, VtableUnboxedClosure}; -use super::{VtableImplData, VtableParamData}; +use super::{VtableImplData, VtableParamData, VtableBuiltinData}; use super::{util}; use middle::mem_categorization::Typer; use middle::subst::{Subst, Substs, VecPerParamSpace}; use middle::ty; -use middle::ty_fold::TypeFoldable; use middle::typeck::check::regionmanip; use middle::typeck::infer; use middle::typeck::infer::{InferCtxt, TypeSkolemizer}; +use middle::ty_fold::TypeFoldable; use std::cell::RefCell; use std::collections::hashmap::HashMap; use std::rc::Rc; +use std::result; use syntax::ast; use util::ppaux::Repr; @@ -38,31 +39,28 @@ pub struct SelectionContext<'cx, 'tcx:'cx> { infcx: &'cx InferCtxt<'cx, 'tcx>, param_env: &'cx ty::ParameterEnvironment, typer: &'cx Typer<'tcx>+'cx, + + /// Skolemizer used specifically for skolemizing entries on the + /// obligation stack. This ensures that all entries on the stack + /// at one time will have the same set of skolemized entries, + /// which is important for checking for trait bounds that + /// recursively require themselves. skolemizer: TypeSkolemizer<'cx, 'tcx>, } // A stack that walks back up the stack frame. struct ObligationStack<'prev> { obligation: &'prev Obligation, - skol_obligation_self_ty: ty::t, + + /// Trait ref from `obligation` but skolemized with the + /// selection-context's skolemizer. Used to check for recursion. + skol_trait_ref: Rc, + previous: Option<&'prev ObligationStack<'prev>> } pub struct SelectionCache { - hashmap: RefCell>>, -} - -#[deriving(Hash,Eq,PartialEq)] -struct CacheKey { - trait_def_id: ast::DefId, - skol_obligation_self_ty: ty::t, -} - -#[deriving(PartialEq,Eq)] -enum MatchResult { - Matched(T), - AmbiguousMatch, - NoMatch + hashmap: RefCell, SelectionResult>>, } /** @@ -91,21 +89,30 @@ enum MatchResult { * clauses can give additional information (like, the types of output * parameters) that would have to be inferred from the impl. */ -#[deriving(Clone)] +#[deriving(PartialEq,Eq,Show,Clone)] enum Candidate { - MatchedBuiltinCandidate, - AmbiguousBuiltinCandidate, - MatchedParamCandidate(VtableParamData), - AmbiguousParamCandidate, - Impl(ImplCandidate), - MatchedUnboxedClosureCandidate(/* closure */ ast::DefId), + BuiltinCandidate(ty::BuiltinBound), + ParamCandidate(VtableParamData), + ImplCandidate(ast::DefId), + UnboxedClosureCandidate(/* closure */ ast::DefId), ErrorCandidate, } -#[deriving(Clone)] -enum ImplCandidate { - MatchedImplCandidate(ast::DefId), - AmbiguousImplCandidate(ast::DefId), +struct CandidateSet { + vec: Vec, + ambiguous: bool +} + +enum BuiltinBoundConditions { + If(Vec), + ParameterBuiltin, + AmbiguousBuiltin +} + +enum EvaluationResult { + EvaluatedToOk, + EvaluatedToErr, + EvaluatedToAmbig, } impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { @@ -150,10 +157,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("select({})", obligation.repr(self.tcx())); - let stack = self.new_stack(obligation); + let stack = self.push_stack(None, obligation); match try!(self.candidate_from_obligation(&stack)) { None => Ok(None), - Some(candidate) => self.confirm_candidate(obligation, candidate), + Some(candidate) => Ok(Some(try!(self.confirm_candidate(obligation, candidate)))), } } @@ -167,22 +174,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { impl_def_id.repr(self.tcx()), obligation_self_ty.repr(self.tcx())); - match self.candidate_from_impl(impl_def_id, + match self.match_inherent_impl(impl_def_id, obligation_cause, obligation_self_ty) { - Some(MatchedImplCandidate(impl_def_id)) => { - let vtable_impl = - try!(self.confirm_inherent_impl_candidate( - impl_def_id, - obligation_cause, - obligation_self_ty, - 0)); + Ok(substs) => { + let vtable_impl = self.vtable_impl(impl_def_id, substs, obligation_cause, 0); Ok(Some(vtable_impl)) } - Some(AmbiguousImplCandidate(_)) => { - Ok(None) - } - None => { + Err(()) => { Err(Unimplemented) } } @@ -194,10 +193,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // 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) - -> EvaluationResult + -> bool { /*! * Evaluates whether the obligation `obligation` can be @@ -207,12 +209,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("evaluate_obligation({})", obligation.repr(self.tcx())); - let stack = self.new_stack(obligation); - match self.candidate_from_obligation(&stack) { - Ok(Some(c)) => c.to_evaluation_result(), - Ok(None) => EvaluatedToAmbiguity, - Err(_) => EvaluatedToUnmatch, - } + let stack = self.push_stack(None, obligation); + self.evaluate_stack(&stack).may_apply() } fn evaluate_builtin_bound_recursively(&mut self, @@ -228,22 +226,42 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { bound, previous_stack.obligation.recursion_depth + 1, ty); - let obligation = match obligation { - Ok(ob) => ob, - _ => return EvaluatedToMatch - }; - self.evaluate_obligation_recursively(previous_stack, &obligation) + match obligation { + Ok(obligation) => { + self.evaluate_obligation_recursively(Some(previous_stack), &obligation) + } + Err(ErrorReported) => { + EvaluatedToOk + } + } } fn evaluate_obligation_recursively(&mut self, - previous_stack: &ObligationStack, + previous_stack: Option<&ObligationStack>, obligation: &Obligation) -> EvaluationResult { debug!("evaluate_obligation_recursively({})", obligation.repr(self.tcx())); + let stack = self.push_stack(previous_stack.map(|x| x), obligation); + self.evaluate_stack(&stack) + } + + fn evaluate_stack(&mut self, + stack: &ObligationStack) + -> EvaluationResult + { + // Whenever any of the types are unbound, there can always be + // 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; + if input_types.iter().any(|&t| ty::type_is_skolemized(t)) { + return EvaluatedToAmbig; + } + // 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 @@ -256,30 +274,32 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `Option>>` is `Send`, and in turn // `Option>>` is `Send` if `Box>` is // `Send`. + // + // Note that we do this comparison using the `skol_trait_ref` + // fields. Because these have all been skolemized using + // `self.skolemizer`, we can be sure that (a) this will not + // affect the inferencer state and (b) that if we see two + // skolemized types with the same index, they refer to the + // same unbound type variable. if - previous_stack.iter() - .filter(|e| e.obligation.trait_ref.def_id == obligation.trait_ref.def_id) - .find(|e| self.match_self_types(obligation.cause, - e.skol_obligation_self_ty, - obligation.self_ty()) == Matched(())) - .is_some() + stack.iter() + .skip(1) // skip top-most frame + .any(|prev| stack.skol_trait_ref == prev.skol_trait_ref) { - return EvaluatedToMatch; + return EvaluatedToOk; } - let stack = self.push_stack(previous_stack, obligation); - match self.candidate_from_obligation(&stack) { - Ok(Some(c)) => c.to_evaluation_result(), - Ok(None) => EvaluatedToAmbiguity, - Err(_) => EvaluatedToUnmatch, + match self.candidate_from_obligation(stack) { + Ok(Some(c)) => self.winnow_candidate(stack, &c), + Ok(None) => EvaluatedToAmbig, + Err(_) => EvaluatedToErr, } } pub fn evaluate_impl(&mut self, impl_def_id: ast::DefId, - obligation_cause: ObligationCause, - obligation_self_ty: ty::t) - -> EvaluationResult + obligation: &Obligation) + -> bool { /*! * Evaluates whether the impl with id `impl_def_id` could be @@ -287,16 +307,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { * used either for trait or inherent impls. */ - debug!("evaluate_impl(impl_def_id={}, obligation_self_ty={})", + debug!("evaluate_impl(impl_def_id={}, obligation={})", impl_def_id.repr(self.tcx()), - obligation_self_ty.repr(self.tcx())); + obligation.repr(self.tcx())); - match self.candidate_from_impl(impl_def_id, - obligation_cause, - obligation_self_ty) { - Some(c) => c.to_evaluation_result(), - None => EvaluatedToUnmatch, - } + 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); + self.winnow_selection(None, VtableImpl(vtable_impl)).may_apply() + } + Err(()) => { + false + } + } + }) } /////////////////////////////////////////////////////////////////////////// @@ -310,16 +335,30 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { stack: &ObligationStack) -> SelectionResult { - debug!("candidate_from_obligation({})", + // Watch out for overflow. This intentionally bypasses (and does + // not update) the cache. + let recursion_limit = self.infcx.tcx.sess.recursion_limit.get(); + if stack.obligation.recursion_depth >= recursion_limit { + debug!("{} --> overflow (limit={})", + stack.obligation.repr(self.tcx()), + recursion_limit); + return Err(Overflow) + } + + // Check the cache. Note that we skolemize the trait-ref + // separately rather than using `stack.skol_trait_ref` -- this + // is because we want the unbound variables to be replaced + // with fresh skolemized types starting from index 0. + let cache_skol_trait_ref = + self.infcx.skolemize(stack.obligation.trait_ref.clone()); + debug!("candidate_from_obligation(cache_skol_trait_ref={}, obligation={})", + cache_skol_trait_ref.repr(self.tcx()), stack.repr(self.tcx())); - // First, check the cache. - match self.check_candidate_cache(stack.obligation, stack.skol_obligation_self_ty) { + match self.check_candidate_cache(cache_skol_trait_ref.clone()) { Some(c) => { - debug!("check_candidate_cache(obligation={}, skol_obligation_self_ty={}, \ - candidate={})", - stack.obligation.trait_ref.def_id, - stack.skol_obligation_self_ty.repr(self.tcx()), + debug!("CACHE HIT: cache_skol_trait_ref={}, candidate={}", + cache_skol_trait_ref.repr(self.tcx()), c.repr(self.tcx())); return c; } @@ -327,29 +366,93 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // If no match, compute result and insert into cache. - let result = self.pick_candidate(stack); - self.insert_candidate_cache(stack.obligation, - stack.skol_obligation_self_ty, - result.clone()); - result + let candidate = self.candidate_from_obligation_no_cache(stack); + debug!("CACHE MISS: cache_skol_trait_ref={}, candidate={}", + cache_skol_trait_ref.repr(self.tcx()), candidate.repr(self.tcx())); + self.insert_candidate_cache(cache_skol_trait_ref, candidate.clone()); + candidate } - fn pick_candidate(&mut self, - stack: &ObligationStack) - -> SelectionResult + fn candidate_from_obligation_no_cache(&mut self, + stack: &ObligationStack) + -> SelectionResult { - if ty::type_is_error(stack.skol_obligation_self_ty) { + if ty::type_is_error(stack.obligation.self_ty()) { return Ok(Some(ErrorCandidate)); } - let mut candidates = try!(self.assemble_candidates(stack)); + let candidate_set = try!(self.assemble_candidates(stack)); + + if candidate_set.ambiguous { + debug!("candidate set contains ambig"); + return Ok(None); + } + + let mut candidates = candidate_set.vec; debug!("assembled {} candidates for {}", candidates.len(), stack.repr(self.tcx())); - // Examine candidates to determine outcome. Ideally we will - // have exactly one candidate that is definitively applicable. + // At this point, we know that each of the entries in the + // candidate set is *individually* applicable. Now we have to + // figure out if they contain mutual incompatibilities. This + // frequently arises if we have an unconstrained input type -- + // for example, we are looking for $0:Eq where $0 is some + // unconstrained type variable. In that case, we'll get a + // candidate which assumes $0 == int, one that assumes $0 == + // uint, etc. This spells an ambiguity. + // If there is more than one candidate, first winnow them down + // by considering extra conditions (nested obligations and so + // forth). We don't winnow if there is exactly one + // candidate. This is a relatively minor distinction but it + // can lead to better inference and error-reporting. An + // example would be if there was an impl: + // + // impl Vec { fn push_clone(...) { ... } } + // + // and we were to see some code `foo.push_clone()` where `boo` + // is a `Vec` and `Bar` does not implement `Clone`. If + // we were to winnow, we'd wind up with zero candidates. + // Instead, we select the right impl now but report `Bar does + // not implement Clone`. + if candidates.len() > 1 { + candidates.retain(|c| self.winnow_candidate(stack, c).may_apply()) + } + + // If there are STILL multiple candidate, we can further reduce + // the list by dropping duplicates. + if candidates.len() > 1 { + let mut i = 0; + while i < candidates.len() { + let is_dup = + range(0, candidates.len()) + .filter(|&j| i != j) + .any(|j| self.candidate_should_be_dropped_in_favor_of(stack, + &candidates[i], + &candidates[j])); + if is_dup { + debug!("Dropping candidate #{}/#{}: {}", + i, candidates.len(), candidates[i].repr(self.tcx())); + candidates.swap_remove(i); + } else { + debug!("Retaining candidate #{}/#{}", + i, candidates.len()); + i += 1; + } + } + } + + // If there are *STILL* multiple candidates, give up and + // report ambiguiuty. + if candidates.len() > 1 { + debug!("multiple matches, ambig"); + 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 candidates.len() == 0 { // Annoying edge case: if there are no impls, then there // is no way that this trait reference is implemented, @@ -357,33 +460,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // 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. - return if !self.contains_skolemized_types(stack.skol_obligation_self_ty) { + 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) - } - } else if candidates.len() > 1 { - // Ambiguity. Possibly we should report back more - // information on the potential candidates so we can give - // a better error message. - debug!("multiple matches, ambig"); - Ok(None) - } else { - let candidate = candidates.pop().unwrap(); - Ok(Some(candidate)) + }; } + + // Just one candidate left. + let candidate = candidates.pop().unwrap(); + Ok(Some(candidate)) } fn pick_candidate_cache(&self, - _obligation: &Obligation, - skol_obligation_self_ty: ty::t) + cache_skol_trait_ref: &Rc) -> &SelectionCache { + // If the trait refers to any parameters in scope, then use + // the cache of the param-environment. This is because the + // result will depend on the where clauses that are in + // scope. Otherwise, use the generic tcx cache, since the + // result holds across all environments. if - ty::type_has_self(skol_obligation_self_ty) || - ty::type_has_params(skol_obligation_self_ty) + cache_skol_trait_ref.substs.types.iter().any( + |&t| ty::type_has_self(t) || ty::type_has_params(t)) { &self.param_env.selection_cache } else { @@ -392,95 +495,59 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } fn check_candidate_cache(&mut self, - obligation: &Obligation, - skol_obligation_self_ty: ty::t) + cache_skol_trait_ref: Rc) -> Option> { - let cache = self.pick_candidate_cache(obligation, skol_obligation_self_ty); - let cache_key = CacheKey::new(obligation.trait_ref.def_id, - skol_obligation_self_ty); + let cache = self.pick_candidate_cache(&cache_skol_trait_ref); let hashmap = cache.hashmap.borrow(); - hashmap.find(&cache_key).map(|c| (*c).clone()) + hashmap.find(&cache_skol_trait_ref).map(|c| (*c).clone()) } fn insert_candidate_cache(&mut self, - obligation: &Obligation, - skol_obligation_self_ty: ty::t, + cache_skol_trait_ref: Rc, candidate: SelectionResult) { - debug!("insert_candidate_cache(obligation={}, skol_obligation_self_ty={}, candidate={})", - obligation.trait_ref.def_id, - skol_obligation_self_ty.repr(self.tcx()), - candidate.repr(self.tcx())); - - let cache = self.pick_candidate_cache(obligation, skol_obligation_self_ty); - let cache_key = CacheKey::new(obligation.trait_ref.def_id, - skol_obligation_self_ty); + let cache = self.pick_candidate_cache(&cache_skol_trait_ref); let mut hashmap = cache.hashmap.borrow_mut(); - hashmap.insert(cache_key, candidate); + hashmap.insert(cache_skol_trait_ref, candidate); } fn assemble_candidates(&mut self, stack: &ObligationStack) - -> Result, SelectionError> + -> Result { // Check for overflow. - let ObligationStack { obligation, skol_obligation_self_ty, .. } = *stack; + let ObligationStack { obligation, .. } = *stack; - let recursion_limit = self.infcx.tcx.sess.recursion_limit.get(); - if obligation.recursion_depth >= recursion_limit { - debug!("{} --> overflow", stack.obligation.repr(self.tcx())); - return Err(Overflow); - } - - let mut candidates = Vec::new(); + let mut candidates = CandidateSet { + vec: Vec::new(), + ambiguous: false + }; // Other bounds. Consider both in-scope bounds from fn decl // and applicable impls. There is a certain set of precedence rules here. - // Where clauses have highest precedence. - try!(self.assemble_candidates_from_caller_bounds( - obligation, - skol_obligation_self_ty, - &mut candidates)); + match self.tcx().lang_items.to_builtin_kind(obligation.trait_ref.def_id) { + Some(bound) => { + try!(self.assemble_builtin_bound_candidates(bound, stack, &mut candidates)); + } - // In the special case of builtin bounds, consider the "compiler-supplied" impls. - if candidates.len() == 0 { - match self.tcx().lang_items.to_builtin_kind(obligation.trait_ref.def_id) { - Some(bound) => { - try!(self.assemble_builtin_bound_candidates(bound, stack, &mut candidates)); - } - - None => { } + None => { + // For the time being, we ignore user-defined impls for builtin-bounds. + // (And unboxed candidates only apply to the Fn/FnMut/etc traits.) + try!(self.assemble_unboxed_candidates(obligation, &mut candidates)); + try!(self.assemble_candidates_from_impls(obligation, &mut candidates)); } } - // In the special case of fn traits and synthesized unboxed - // closure types, consider the compiler-supplied impls. Note - // that this is exclusive with the builtin bound case above. - if candidates.len() == 0 { - try!(self.assemble_unboxed_candidates( - obligation, - skol_obligation_self_ty, - &mut candidates)); - } - - // Finally, consider the actual impls found in the program. - if candidates.len() == 0 { - try!(self.assemble_candidates_from_impls( - obligation, - skol_obligation_self_ty, - &mut candidates)); - } - + try!(self.assemble_candidates_from_caller_bounds(obligation, &mut candidates)); Ok(candidates) } fn assemble_candidates_from_caller_bounds(&mut self, obligation: &Obligation, - skol_obligation_self_ty: ty::t, - candidates: &mut Vec) + candidates: &mut CandidateSet) -> Result<(),SelectionError> { /*! @@ -491,68 +558,63 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { * Never affects inference environment. */ - debug!("assemble_candidates_from_caller_bounds({}, {})", - obligation.repr(self.tcx()), - skol_obligation_self_ty.repr(self.tcx())); + debug!("assemble_candidates_from_caller_bounds({})", + obligation.repr(self.tcx())); - for caller_obligation in self.param_env.caller_obligations.iter() { - // Skip over obligations that don't apply to - // `self_ty`. - let caller_bound = &caller_obligation.trait_ref; - let caller_self_ty = caller_bound.substs.self_ty().unwrap(); - debug!("caller_obligation={}, caller_self_ty={}", - caller_obligation.repr(self.tcx()), - self.infcx.ty_to_string(caller_self_ty)); - match self.match_self_types(obligation.cause, - caller_self_ty, - skol_obligation_self_ty) { - AmbiguousMatch => { - debug!("-> AmbiguousMatch"); - candidates.push(AmbiguousParamCandidate); - return Ok(()); - } - NoMatch => { - debug!("-> NoMatch"); - continue; - } - Matched(()) => { } - } + let caller_trait_refs: Vec> = + self.param_env.caller_obligations.iter() + .map(|o| o.trait_ref.clone()) + .collect(); - // Search through the trait (and its supertraits) to - // see if it matches the def-id we are looking for. - let caller_bound = (*caller_bound).clone(); - for bound in util::transitive_bounds(self.tcx(), &[caller_bound]) { - debug!("-> check bound={}", bound.repr(self.tcx())); - if bound.def_id == obligation.trait_ref.def_id { - // If so, we're done! - debug!("-> MatchedParamCandidate({})", bound.repr(self.tcx())); - let vtable_param = VtableParamData { bound: bound }; - candidates.push(MatchedParamCandidate(vtable_param)); - return Ok(()); - } - } - } + let all_bounds = + util::transitive_bounds( + self.tcx(), caller_trait_refs.as_slice()); + + let matching_bounds = + all_bounds.filter( + |bound| self.infcx.probe( + || self.match_trait_refs(obligation, + (*bound).clone())).is_ok()); + + let param_candidates = + matching_bounds.map( + |bound| ParamCandidate(VtableParamData { bound: bound })); + + candidates.vec.extend(param_candidates); Ok(()) } fn assemble_unboxed_candidates(&mut self, obligation: &Obligation, - skol_obligation_self_ty: ty::t, - candidates: &mut Vec) + candidates: &mut CandidateSet) -> Result<(),SelectionError> { /*! * Check for the artificial impl that the compiler will create * for an obligation like `X : FnMut<..>` where `X` is an * unboxed closure type. + * + * Note: the type parameters on an unboxed closure candidate + * are modeled as *output* type parameters and hence do not + * affect whether this trait is a match or not. They will be + * unified during the confirmation step. */ - let closure_def_id = match ty::get(skol_obligation_self_ty).sty { + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + let closure_def_id = match ty::get(self_ty).sty { ty::ty_unboxed_closure(id, _) => id, + ty::ty_infer(ty::TyVar(_)) => { + candidates.ambiguous = true; + return Ok(()); + } _ => { return Ok(()); } }; + debug!("assemble_unboxed_candidates: self_ty={} obligation={}", + self_ty.repr(self.tcx()), + obligation.repr(self.tcx())); + let tcx = self.tcx(); let fn_traits = [ (ty::FnUnboxedClosureKind, tcx.lang_items.fn_trait()), @@ -584,7 +646,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { continue; } - candidates.push(MatchedUnboxedClosureCandidate(closure_def_id)); + candidates.vec.push(UnboxedClosureCandidate(closure_def_id)); } Ok(()) @@ -592,8 +654,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn assemble_candidates_from_impls(&mut self, obligation: &Obligation, - skol_obligation_self_ty: ty::t, - candidates: &mut Vec) + candidates: &mut CandidateSet) -> Result<(), SelectionError> { /*! @@ -603,39 +664,106 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let all_impls = self.all_impls(obligation.trait_ref.def_id); for &impl_def_id in all_impls.iter() { self.infcx.probe(|| { - match self.candidate_from_impl(impl_def_id, - obligation.cause, - skol_obligation_self_ty) { - Some(c) => { - candidates.push(Impl(c)); + match self.match_impl(impl_def_id, obligation) { + Ok(_) => { + candidates.vec.push(ImplCandidate(impl_def_id)); } - - None => { } + Err(()) => { } } }); } Ok(()) } - fn candidate_from_impl(&mut self, - impl_def_id: ast::DefId, - obligation_cause: ObligationCause, - skol_obligation_self_ty: ty::t) - -> Option + /////////////////////////////////////////////////////////////////////////// + // WINNOW + // + // Winnowing is the process of attempting to resolve ambiguity by + // probing further. During the winnowing process, we unify all + // type variables (ignoring skolemization) and then we also + // attempt to evaluate recursive bounds to see if they are + // satisfied. + + fn winnow_candidate(&mut self, + stack: &ObligationStack, + candidate: &Candidate) + -> EvaluationResult { - match self.match_impl_self_types(impl_def_id, - obligation_cause, - skol_obligation_self_ty) { - Matched(_) => { - Some(MatchedImplCandidate(impl_def_id)) - } + /*! + * Further evaluate `candidate` to decide whether all type parameters match + * and whether nested obligations are met. Returns true if `candidate` remains + * viable after this further scrutiny. + */ - AmbiguousMatch => { - Some(AmbiguousImplCandidate(impl_def_id)) + debug!("winnow_candidate: candidate={}", candidate.repr(self.tcx())); + self.infcx.probe(|| { + let candidate = (*candidate).clone(); + match self.confirm_candidate(stack.obligation, candidate) { + Ok(selection) => self.winnow_selection(Some(stack), selection), + Err(_) => EvaluatedToErr, } + }) + } - NoMatch => { - None + fn winnow_selection(&mut self, + stack: Option<&ObligationStack>, + selection: Selection) + -> EvaluationResult + { + let mut result = EvaluatedToOk; + for obligation in selection.iter_nested() { + match self.evaluate_obligation_recursively(stack, obligation) { + EvaluatedToErr => { return EvaluatedToErr; } + EvaluatedToAmbig => { result = EvaluatedToAmbig; } + EvaluatedToOk => { } + } + } + result + } + + fn candidate_should_be_dropped_in_favor_of(&mut self, + stack: &ObligationStack, + candidate_i: &Candidate, + candidate_j: &Candidate) + -> bool + { + /*! + * Returns true if `candidate_i` should be dropped in favor of `candidate_j`. + * This is generally true if either: + * - candidate i and candidate j are equivalent; or, + * - candidate i is a where clause bound and candidate j is a concrete impl, + * and the concrete impl is applicable to the types in the where clause bound. + * + * The last case basically occurs with blanket impls like + * `impl Foo for T`. In that case, a bound like `T:Foo` is + * kind of an "false" ambiguity -- both are applicable to any + * type, but in fact coherence requires that the bound will + * always be resolved to the impl anyway. + */ + + match (candidate_i, candidate_j) { + (&ParamCandidate(ref vt), &ImplCandidate(impl_def_id)) => { + debug!("Considering whether to drop param {} in favor of impl {}", + candidate_i.repr(self.tcx()), + candidate_j.repr(self.tcx())); + + self.infcx.probe(|| { + let impl_substs = + self.rematch_impl(impl_def_id, stack.obligation); + let impl_trait_ref = + ty::impl_trait_ref(self.tcx(), impl_def_id).unwrap(); + let impl_trait_ref = + impl_trait_ref.subst(self.tcx(), &impl_substs); + let origin = + infer::RelateOutputImplTypes(stack.obligation.cause.span); + self.infcx + .sub_trait_refs(false, origin, + impl_trait_ref, vt.bound.clone()) + .is_ok() + }) + } + _ => { + *candidate_i == *candidate_j } } } @@ -652,80 +780,68 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn assemble_builtin_bound_candidates(&mut self, bound: ty::BuiltinBound, stack: &ObligationStack, - candidates: &mut Vec) + candidates: &mut CandidateSet) -> Result<(),SelectionError> { - // Copy -- owned, dtor, managed, marker, &mut -- only INTERIOR? - // Sized -- str, [T], Trait -- but only INTERIOR - // Send -- managed data, nonsend annot, borrowed data -- REACHABILITY - // Sync -- non-sync marker trait -- REACHABILITY + // FIXME -- To be more like a normal impl, we should just + // ignore the nested cases here, and instead generate nested + // obligations in `confirm_candidate`. However, this doesn't + // work because we require handling the recursive cases to + // avoid infinite cycles (that is, with recursive types, + // sometimes `Foo : Copy` only holds if `Foo : Copy`). - // Ideally, we'd only have to examine the immediate fields. - // But think this through carefully I guess. - - enum WhenOk<'a> { - Always, - Unknown, - Never, - If(ty::t), - IfAll(&'a [ty::t]), - IfTrue(bool) - } - - let ok = |this: &mut SelectionContext, w: WhenOk| { - let r = match w { - Always => EvaluatedToMatch, - Unknown => EvaluatedToAmbiguity, - Never => EvaluatedToUnmatch, - IfTrue(true) => EvaluatedToMatch, - IfTrue(false) => EvaluatedToUnmatch, - If(ty) => this.evaluate_builtin_bound_recursively(bound, stack, ty), - IfAll(tys) => { - let mut result = EvaluatedToMatch; - for &ty in tys.iter() { - match this.evaluate_builtin_bound_recursively(bound, stack, ty) { - EvaluatedToMatch => { } - EvaluatedToAmbiguity => { - result = EvaluatedToAmbiguity; - } - EvaluatedToUnmatch => { - result = EvaluatedToUnmatch; - break; - } - } - } - result + match self.builtin_bound(bound, stack.obligation.self_ty()) { + Ok(If(nested)) => { + debug!("builtin_bound: bound={} nested={}", + bound.repr(self.tcx()), + nested.repr(self.tcx())); + let data = self.vtable_builtin_data(stack.obligation, bound, nested); + match self.winnow_selection(Some(stack), VtableBuiltin(data)) { + EvaluatedToOk => { Ok(candidates.vec.push(BuiltinCandidate(bound))) } + EvaluatedToAmbig => { Ok(candidates.ambiguous = true) } + EvaluatedToErr => { Err(Unimplemented) } } - }; - - match r { - EvaluatedToMatch => Ok(candidates.push(MatchedBuiltinCandidate)), - EvaluatedToAmbiguity => Ok(candidates.push(AmbiguousBuiltinCandidate)), - EvaluatedToUnmatch => Err(Unimplemented) } - }; + Ok(ParameterBuiltin) => { Ok(()) } + Ok(AmbiguousBuiltin) => { Ok(candidates.ambiguous = true) } + Err(e) => { Err(e) } + } + } - return match ty::get(stack.skol_obligation_self_ty).sty { - ty::ty_uint(_) | ty::ty_int(_) | ty::ty_infer(ty::SkolemizedIntTy(_)) | - ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_float(_) | - ty::ty_bare_fn(_) | ty::ty_char => { + fn builtin_bound(&mut self, + bound: ty::BuiltinBound, + self_ty: ty::t) + -> Result + { + let self_ty = self.infcx.shallow_resolve(self_ty); + return match ty::get(self_ty).sty { + ty::ty_infer(ty::IntVar(_)) | + ty::ty_infer(ty::FloatVar(_)) | + ty::ty_uint(_) | + ty::ty_int(_) | + ty::ty_nil | + ty::ty_bot | + ty::ty_bool | + ty::ty_float(_) | + ty::ty_bare_fn(_) | + ty::ty_char => { // safe for everything - ok(self, Always) + Ok(If(Vec::new())) } ty::ty_uniq(referent_ty) => { // Box match bound { ty::BoundCopy => { - ok(self, Never) + Err(Unimplemented) } ty::BoundSized => { - ok(self, Always) + Ok(If(Vec::new())) } ty::BoundSync | ty::BoundSend => { - ok(self, If(referent_ty)) + Ok(If(vec![referent_ty])) } } } @@ -734,12 +850,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match bound { ty::BoundCopy | ty::BoundSized => { - ok(self, Always) + Ok(If(Vec::new())) } ty::BoundSync | ty::BoundSend => { - ok(self, If(referent_ty)) + Ok(If(vec![referent_ty])) } } } @@ -750,16 +866,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // proc: Equivalent to `Box` match bound { ty::BoundCopy => { - ok(self, Never) + Err(Unimplemented) } ty::BoundSized => { - ok(self, Always) + Ok(If(Vec::new())) } ty::BoundSync | ty::BoundSend => { - ok(self, IfTrue(c.bounds.builtin_bounds.contains_elem(bound))) + if c.bounds.builtin_bounds.contains_elem(bound) { + Ok(If(Vec::new())) + } else { + Err(Unimplemented) + } } } } @@ -767,19 +887,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // ||: Equivalent to `&FnMut` or `&mut FnMut` or something like that. match bound { ty::BoundCopy => { - ok(self, match mutbl { - ast::MutMutable => Never, // &mut T is affine - ast::MutImmutable => Always, // &T is copyable - }) + match mutbl { + ast::MutMutable => Err(Unimplemented), // &mut T is affine + ast::MutImmutable => Ok(If(Vec::new())), // &T is copyable + } } ty::BoundSized => { - ok(self, Always) + Ok(If(Vec::new())) } ty::BoundSync | ty::BoundSend => { - ok(self, IfTrue(c.bounds.builtin_bounds.contains_elem(bound))) + if c.bounds.builtin_bounds.contains_elem(bound) { + Ok(If(Vec::new())) + } else { + Err(Unimplemented) + } } } } @@ -789,10 +913,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::ty_trait(box ty::TyTrait { bounds, .. }) => { match bound { ty::BoundSized => { - ok(self, Never) + Err(Unimplemented) } ty::BoundCopy | ty::BoundSync | ty::BoundSend => { - ok(self, IfTrue(bounds.builtin_bounds.contains_elem(bound))) + if bounds.builtin_bounds.contains_elem(bound) { + Ok(If(Vec::new())) + } else { + Err(Unimplemented) + } } } } @@ -801,14 +929,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // &mut T or &T match bound { ty::BoundCopy => { - ok(self, match mutbl { - ast::MutMutable => Never, // &mut T is affine and hence never `Copy` - ast::MutImmutable => Always, // &T is copyable - }) + match mutbl { + // &mut T is affine and hence never `Copy` + ast::MutMutable => Err(Unimplemented), + + // &T is always copyable + ast::MutImmutable => Ok(If(Vec::new())), + } } ty::BoundSized => { - ok(self, Always) + Ok(If(Vec::new())) } ty::BoundSync | @@ -829,7 +960,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // we haven't finished running inference -- in // other words, there's a kind of // chicken-and-egg problem. - ok(self, If(referent_ty)) + Ok(If(vec![referent_ty])) } } } @@ -839,18 +970,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match bound { ty::BoundCopy => { match *len { - Some(_) => ok(self, If(element_ty)), // [T, ..n] is copy iff T is copy - None => ok(self, Never), // [T] is unsized and hence affine + Some(_) => Ok(If(vec![element_ty])), // [T, ..n] is copy iff T is copy + None => Err(Unimplemented), // [T] is unsized and hence affine } } ty::BoundSized => { - ok(self, IfTrue(len.is_some())) + if len.is_some() { + Ok(If(Vec::new())) + } else { + Err(Unimplemented) + } } ty::BoundSync | ty::BoundSend => { - ok(self, If(element_ty)) + Ok(If(vec![element_ty])) } } } @@ -860,19 +995,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match bound { ty::BoundSync | ty::BoundSend => { - ok(self, Always) + Ok(If(Vec::new())) } ty::BoundCopy | ty::BoundSized => { - ok(self, Never) + Err(Unimplemented) } } } ty::ty_tup(ref tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet - ok(self, IfAll(tys.as_slice())) + Ok(If(tys.to_owned())) } ty::ty_unboxed_closure(def_id, _) => { @@ -885,18 +1020,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // captures are by value. Really what we ought to do // is reserve judgement and then intertwine this // analysis with closure inference. - // - // FIXME -- this is wrong with respect to - // skolemization. We want to skolemize the types of - // the variables, but to do THAT we need the ability - // to "start" the skolemization numbering from a - // higher point. Perhaps this just means creating a - // single skolemizer and then using it again here? assert_eq!(def_id.krate, ast::LOCAL_CRATE); match self.tcx().freevars.borrow().find(&def_id.node) { None => { // No upvars. - ok(self, Always) + Ok(If(Vec::new())) } Some(freevars) => { @@ -905,12 +1033,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .iter() .map(|freevar| { let freevar_def_id = freevar.def.def_id(); - let freevar_ty = self.typer.node_ty(freevar_def_id.node) - .unwrap_or(ty::mk_err()); - freevar_ty.fold_with(&mut self.skolemizer) + self.typer.node_ty(freevar_def_id.node) + .unwrap_or(ty::mk_err()) }) .collect(); - ok(self, IfAll(tys.as_slice())) + Ok(If(tys)) } } } @@ -921,7 +1048,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .iter() .map(|f| f.mt.ty) .collect(); - nominal(self, bound, def_id, types, ok) + nominal(self, bound, def_id, types) } ty::ty_enum(def_id, ref substs) => { @@ -931,7 +1058,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .flat_map(|variant| variant.args.iter()) .map(|&ty| ty) .collect(); - nominal(self, bound, def_id, types, ok) + nominal(self, bound, def_id, types) } ty::ty_param(_) => { @@ -939,36 +1066,35 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // particular bound if there is a where clause telling // us that it does, and that case is handled by // `assemble_candidates_from_caller_bounds()`. - ok(self, Never) + Ok(ParameterBuiltin) } - ty::ty_infer(ty::SkolemizedTy(_)) => { - // Skolemized types represent unbound type - // variables. They might or might not have applicable - // impls and so forth, depending on what those type - // variables wind up being bound to. - ok(self, Unknown) + ty::ty_infer(ty::TyVar(_)) => { + // Unbound type variable. Might or might not have + // applicable impls and so forth, depending on what + // those type variables wind up being bound to. + Ok(AmbiguousBuiltin) + } + + ty::ty_err => { + Ok(If(Vec::new())) } ty::ty_open(_) | - ty::ty_infer(ty::TyVar(_)) | - ty::ty_infer(ty::IntVar(_)) | - ty::ty_infer(ty::FloatVar(_)) | - ty::ty_err => { - self.tcx().sess.span_bug( - stack.obligation.cause.span, + ty::ty_infer(ty::SkolemizedTy(_)) | + ty::ty_infer(ty::SkolemizedIntTy(_)) => { + self.tcx().sess.bug( format!( - "asked to compute contents of unexpected type: {}", - stack.skol_obligation_self_ty.repr(self.tcx())).as_slice()); + "asked to assemble builtin bounds of unexpected type: {}", + self_ty.repr(self.tcx())).as_slice()); } }; fn nominal(this: &mut SelectionContext, bound: ty::BuiltinBound, def_id: ast::DefId, - types: Vec, - ok: |&mut SelectionContext, WhenOk| -> Result<(),SelectionError>) - -> Result<(),SelectionError> + types: Vec) + -> Result { // First check for markers and other nonsense. let tcx = this.tcx(); @@ -978,7 +1104,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Some(def_id) == tcx.lang_items.no_send_bound() || Some(def_id) == tcx.lang_items.managed_bound() { - return ok(this, Never); + return Err(Unimplemented); } } @@ -988,7 +1114,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Some(def_id) == tcx.lang_items.managed_bound() || ty::has_dtor(tcx, def_id) { - return ok(this, Never); + return Err(Unimplemented); } } @@ -997,21 +1123,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Some(def_id) == tcx.lang_items.no_sync_bound() || Some(def_id) == tcx.lang_items.managed_bound() { - return ok(this, Never); + return Err(Unimplemented); } else if Some(def_id) == tcx.lang_items.unsafe_type() { // FIXME(#13231) -- we currently consider `UnsafeCell` // to always be sync. This is allow for types like `Queue` // and `Mutex`, where `Queue : Sync` is `T : Send`. - return ok(this, Always); + return Ok(If(Vec::new())); } } ty::BoundSized => { } } - ok(this, IfAll(types.as_slice())) + Ok(If(types)) } } @@ -1025,38 +1151,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_candidate(&mut self, obligation: &Obligation, candidate: Candidate) - -> SelectionResult + -> Result { debug!("confirm_candidate({}, {})", obligation.repr(self.tcx()), candidate.repr(self.tcx())); match candidate { - AmbiguousBuiltinCandidate | - AmbiguousParamCandidate | - Impl(AmbiguousImplCandidate(_)) => { - Ok(None) + // FIXME -- see assemble_builtin_bound_candidates() + BuiltinCandidate(_) | + ErrorCandidate => { + Ok(VtableBuiltin(VtableBuiltinData { nested: VecPerParamSpace::empty() })) } - ErrorCandidate | - MatchedBuiltinCandidate => { - Ok(Some(VtableBuiltin)) + ParamCandidate(param) => { + Ok(VtableParam( + try!(self.confirm_param_candidate(obligation, param)))) } - MatchedParamCandidate(param) => { - Ok(Some(VtableParam( - try!(self.confirm_param_candidate(obligation, param))))) + ImplCandidate(impl_def_id) => { + let vtable_impl = + try!(self.confirm_impl_candidate(obligation, impl_def_id)); + Ok(VtableImpl(vtable_impl)) } - Impl(MatchedImplCandidate(impl_def_id)) => { - let vtable_impl = try!(self.confirm_impl_candidate(obligation, - impl_def_id)); - Ok(Some(VtableImpl(vtable_impl))) - } - - MatchedUnboxedClosureCandidate(closure_def_id) => { + UnboxedClosureCandidate(closure_def_id) => { try!(self.confirm_unboxed_closure_candidate(obligation, closure_def_id)); - Ok(Some(VtableUnboxedClosure(closure_def_id))) + Ok(VtableUnboxedClosure(closure_def_id)) } } } @@ -1076,6 +1197,52 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(param) } + fn confirm_builtin_candidate(&mut self, + obligation: &Obligation, + bound: ty::BuiltinBound) + -> Result,SelectionError> + { + debug!("confirm_builtin_candidate({})", + obligation.repr(self.tcx())); + + match try!(self.builtin_bound(bound, obligation.self_ty())) { + If(nested) => Ok(self.vtable_builtin_data(obligation, bound, nested)), + AmbiguousBuiltin | + ParameterBuiltin => { + self.tcx().sess.span_bug( + obligation.cause.span, + format!("builtin bound for {} was ambig", + obligation.repr(self.tcx())).as_slice()); + } + } + } + + fn vtable_builtin_data(&mut self, + obligation: &Obligation, + bound: ty::BuiltinBound, + nested: Vec) + -> VtableBuiltinData + { + let obligations = + result::collect( + nested + .iter() + .map(|&t| { + util::obligation_for_builtin_bound( + self.tcx(), + obligation.cause, + bound, + obligation.recursion_depth + 1, + t) + })); + let obligations = match obligations { + Ok(o) => o, + Err(ErrorReported) => Vec::new() + }; + let obligations = VecPerParamSpace::new(obligations, Vec::new(), Vec::new()); + VtableBuiltinData { nested: obligations } + } + fn confirm_impl_candidate(&mut self, obligation: &Obligation, impl_def_id: ast::DefId) @@ -1085,55 +1252,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.repr(self.tcx()), impl_def_id.repr(self.tcx())); - // For a non-inhernet impl, we begin the same way as an - // inherent impl, by matching the self-type and assembling - // list of nested obligations. - let vtable_impl = - try!(self.confirm_inherent_impl_candidate( - impl_def_id, - obligation.cause, - obligation.trait_ref.self_ty(), - obligation.recursion_depth)); - - // But then we must also match the output types. - let () = try!(self.confirm_impl_vtable(impl_def_id, - obligation.cause, - obligation.trait_ref.clone(), - &vtable_impl.substs)); - Ok(vtable_impl) + // First, create the substitutions by matching the impl again, + // this time not in a probe. + let substs = self.rematch_impl(impl_def_id, obligation); + Ok(self.vtable_impl(impl_def_id, substs, obligation.cause, obligation.recursion_depth + 1)) } - fn confirm_inherent_impl_candidate(&mut self, - impl_def_id: ast::DefId, - obligation_cause: ObligationCause, - obligation_self_ty: ty::t, - obligation_recursion_depth: uint) - -> Result, - SelectionError> + fn vtable_impl(&mut self, + impl_def_id: ast::DefId, + substs: Substs, + cause: ObligationCause, + recursion_depth: uint) + -> VtableImplData { - let substs = match self.match_impl_self_types(impl_def_id, - obligation_cause, - obligation_self_ty) { - Matched(substs) => substs, - AmbiguousMatch | NoMatch => { - self.tcx().sess.bug( - format!("Impl {} was matchable against {} but now is not", - impl_def_id.repr(self.tcx()), - obligation_self_ty.repr(self.tcx())) - .as_slice()); - } - }; - let impl_obligations = - self.impl_obligations(obligation_cause, - obligation_recursion_depth, + self.impl_obligations(cause, + recursion_depth, impl_def_id, &substs); - let vtable_impl = VtableImplData { impl_def_id: impl_def_id, - substs: substs, - nested: impl_obligations }; - - Ok(vtable_impl) + VtableImplData { impl_def_id: impl_def_id, + substs: substs, + nested: impl_obligations } } fn confirm_unboxed_closure_candidate(&mut self, @@ -1189,11 +1328,65 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // run inside of a `probe()` so that their side-effects are // contained. - fn match_impl_self_types(&mut self, - impl_def_id: ast::DefId, - obligation_cause: ObligationCause, - obligation_self_ty: ty::t) - -> MatchResult + fn rematch_impl(&mut self, + impl_def_id: ast::DefId, + obligation: &Obligation) + -> Substs + { + match self.match_impl(impl_def_id, obligation) { + Ok(substs) => { + substs + } + Err(()) => { + self.tcx().sess.bug( + format!("Impl {} was matchable against {} but now is not", + impl_def_id.repr(self.tcx()), + obligation.repr(self.tcx())) + .as_slice()); + } + } + } + + fn match_impl(&mut self, + impl_def_id: ast::DefId, + obligation: &Obligation) + -> Result + { + let impl_substs = util::fresh_substs_for_impl(self.infcx, + obligation.cause.span, + impl_def_id); + + let impl_trait_ref = ty::impl_trait_ref(self.tcx(), + impl_def_id).unwrap(); + let impl_trait_ref = impl_trait_ref.subst(self.tcx(), + &impl_substs); + + match self.match_trait_refs(obligation, impl_trait_ref) { + Ok(()) => Ok(impl_substs), + Err(()) => Err(()) + } + } + + fn match_trait_refs(&mut self, + obligation: &Obligation, + trait_ref: Rc) + -> Result<(),()> + { + let origin = infer::RelateOutputImplTypes(obligation.cause.span); + match self.infcx.sub_trait_refs(false, + origin, + trait_ref, + obligation.trait_ref.clone()) { + Ok(()) => Ok(()), + Err(_) => Err(()), + } + } + + fn match_inherent_impl(&mut self, + impl_def_id: ast::DefId, + obligation_cause: ObligationCause, + obligation_self_ty: ty::t) + -> Result { /*! * Determines whether the self type declared against @@ -1226,17 +1419,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match self.match_self_types(obligation_cause, impl_self_ty, obligation_self_ty) { - Matched(()) => { + Ok(()) => { debug!("Matched impl_substs={}", impl_substs.repr(self.tcx())); - Matched(impl_substs) + Ok(impl_substs) } - AmbiguousMatch => { - debug!("AmbiguousMatch"); - AmbiguousMatch - } - NoMatch => { + Err(()) => { debug!("NoMatch"); - NoMatch + Err(()) } } } @@ -1249,7 +1438,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // The self type the obligation is for: required_self_ty: ty::t) - -> MatchResult<()> + -> Result<(),()> { // FIXME(#5781) -- equating the types is stronger than // necessary. Should consider variance of trait w/r/t Self. @@ -1259,21 +1448,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { origin, provided_self_ty, required_self_ty) { - Ok(()) => Matched(()), - Err(ty::terr_sorts(ty::expected_found{expected: t1, found: t2})) => { - // This error occurs when there is an unresolved type - // variable in the `required_self_ty` that was forced - // to unify with a non-type-variable. That basically - // means we don't know enough to say with certainty - // whether there is a match or not -- it depends on - // how that type variable is ultimately resolved. - if ty::type_is_skolemized(t1) || ty::type_is_skolemized(t2) { - AmbiguousMatch - } else { - NoMatch - } - } - Err(_) => NoMatch, + Ok(()) => Ok(()), + Err(_) => Err(()), } } @@ -1363,29 +1539,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /////////////////////////////////////////////////////////////////////////// // Miscellany - fn new_stack<'o>(&mut self, obligation: &'o Obligation) -> ObligationStack<'o> { - let skol_obligation_self_ty = - obligation.self_ty().fold_with(&mut self.skolemizer); - - ObligationStack { - obligation: obligation, - skol_obligation_self_ty: skol_obligation_self_ty, - previous: None - } - } - - fn push_stack<'o>(&self, - previous_stack: &'o ObligationStack<'o>, - obligation: &'o Obligation) - -> ObligationStack<'o> + fn push_stack<'o,'s:'o>(&mut self, + previous_stack: Option<&'s ObligationStack<'s>>, + obligation: &'o Obligation) + -> ObligationStack<'o> { - // No need to skolemize obligation.self_ty, because we - // guarantee the self-type for all recursive obligations are - // already skolemized. + let skol_trait_ref = obligation.trait_ref.fold_with(&mut self.skolemizer); + ObligationStack { obligation: obligation, - skol_obligation_self_ty: obligation.self_ty(), - previous: Some(previous_stack) + skol_trait_ref: skol_trait_ref, + previous: previous_stack.map(|p| p), // FIXME variance } } @@ -1436,57 +1600,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } -impl Candidate { - fn to_evaluation_result(&self) -> EvaluationResult { - match *self { - Impl(ref i) => i.to_evaluation_result(), - - ErrorCandidate | - MatchedUnboxedClosureCandidate(..) | - MatchedBuiltinCandidate | - MatchedParamCandidate(..) => { - EvaluatedToMatch - } - - AmbiguousBuiltinCandidate | - AmbiguousParamCandidate => { - EvaluatedToAmbiguity - } - } - } -} - -impl ImplCandidate { - fn to_evaluation_result(&self) -> EvaluationResult { - match *self { - MatchedImplCandidate(..) => EvaluatedToMatch, - AmbiguousImplCandidate(..) => EvaluatedToAmbiguity - } - } -} - impl Repr for Candidate { fn repr(&self, tcx: &ty::ctxt) -> String { match *self { ErrorCandidate => format!("ErrorCandidate"), - MatchedBuiltinCandidate => format!("MatchedBuiltinCandidate"), - AmbiguousBuiltinCandidate => format!("AmbiguousBuiltinCandidate"), - MatchedUnboxedClosureCandidate(c) => format!("MatchedUnboxedClosureCandidate({})", c), - MatchedParamCandidate(ref r) => format!("MatchedParamCandidate({})", - r.repr(tcx)), - AmbiguousParamCandidate => format!("AmbiguousParamCandidate"), - Impl(ref i) => i.repr(tcx) - } - } -} - -impl Repr for ImplCandidate { - fn repr(&self, tcx: &ty::ctxt) -> String { - match *self { - MatchedImplCandidate(ref d) => format!("MatchedImplCandidate({})", - d.repr(tcx)), - AmbiguousImplCandidate(ref d) => format!("AmbiguousImplCandidate({})", - d.repr(tcx)), + BuiltinCandidate(b) => format!("BuiltinCandidate({})", b), + UnboxedClosureCandidate(c) => format!("MatchedUnboxedClosureCandidate({})", c), + ParamCandidate(ref a) => format!("ParamCandidate({})", a.repr(tcx)), + ImplCandidate(a) => format!("ImplCandidate({})", a.repr(tcx)), } } } @@ -1521,20 +1642,16 @@ impl<'o> Iterator<&'o ObligationStack<'o>> for Option<&'o ObligationStack<'o>> { impl<'o> Repr for ObligationStack<'o> { fn repr(&self, tcx: &ty::ctxt) -> String { - format!("ObligationStack({}, {})", - self.obligation.repr(tcx), - self.skol_obligation_self_ty.repr(tcx)) + format!("ObligationStack({})", + self.obligation.repr(tcx)) } } -impl CacheKey { - pub fn new(trait_def_id: ast::DefId, - skol_obligation_self_ty: ty::t) - -> CacheKey - { - CacheKey { - trait_def_id: trait_def_id, - skol_obligation_self_ty: skol_obligation_self_ty +impl EvaluationResult { + fn may_apply(&self) -> bool { + match *self { + EvaluatedToOk | EvaluatedToAmbig => true, + EvaluatedToErr => false, } } } diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index ab5bbf4363f..31266ff199f 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -319,8 +319,8 @@ impl Repr for super::Vtable { super::VtableParam(ref v) => format!("VtableParam({})", v.repr(tcx)), - super::VtableBuiltin => - format!("Builtin"), + super::VtableBuiltin(ref d) => + d.repr(tcx) } } } @@ -334,6 +334,13 @@ impl Repr for super::VtableImplData { } } +impl Repr for super::VtableBuiltinData { + fn repr(&self, tcx: &ty::ctxt) -> String { + format!("VtableBuiltin(nested={})", + self.nested.repr(tcx)) + } +} + impl Repr for super::VtableParamData { fn repr(&self, tcx: &ty::ctxt) -> String { format!("VtableParam(bound={})", @@ -344,12 +351,12 @@ impl Repr for super::VtableParamData { impl Repr for super::SelectionError { fn repr(&self, tcx: &ty::ctxt) -> String { match *self { - super::Unimplemented => - format!("Unimplemented"), - super::Overflow => format!("Overflow"), + super::Unimplemented => + format!("Unimplemented"), + super::OutputTypeParameterMismatch(ref t, ref e) => format!("OutputTypeParameterMismatch({}, {})", t.repr(tcx), diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 7daee22e614..9c1fcaa9ce8 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -800,10 +800,18 @@ pub fn fulfill_obligation(ccx: &CrateContext, let selection = match selcx.select(&obligation) { Ok(Some(selection)) => selection, Ok(None) => { - tcx.sess.span_bug( + // Ambiguity can happen when monomorphizing during trans + // expands to some humongo type that never occurred + // statically -- this humongo type can then overflow, + // leading to an ambiguous result. So report this as an + // overflow bug, since I believe this is the only case + // where ambiguity can result. + debug!("Encountered ambiguity selecting `{}` during trans, \ + presuming due to overflow", + trait_ref.repr(tcx)); + ccx.sess().span_fatal( span, - format!("Encountered ambiguity selecting `{}` during trans", - trait_ref.repr(tcx)).as_slice()) + "reached the recursion limit during monomorphization"); } Err(e) => { tcx.sess.span_bug( @@ -826,12 +834,19 @@ pub fn fulfill_obligation(ccx: &CrateContext, }); match fulfill_cx.select_all_or_error(&infcx, ¶m_env, tcx) { Ok(()) => { } - Err(e) => { - tcx.sess.span_bug( - span, - format!("Encountered errors `{}` fulfilling `{}` during trans", - e.repr(tcx), - trait_ref.repr(tcx)).as_slice()); + Err(errors) => { + if errors.iter().all(|e| e.is_overflow()) { + // See Ok(None) case above. + ccx.sess().span_fatal( + span, + "reached the recursion limit during monomorphization"); + } else { + tcx.sess.span_bug( + span, + format!("Encountered errors `{}` fulfilling `{}` during trans", + errors.repr(tcx), + trait_ref.repr(tcx)).as_slice()); + } } } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index a2b7cb159d9..9112a250932 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -247,7 +247,7 @@ static FLAGS_NONE: c_uint = 0; // Public Interface of debuginfo module //=----------------------------------------------------------------------------- -#[deriving(Copy, Show, Hash, Eq, PartialEq, Clone)] +#[deriving(Show, Hash, Eq, PartialEq, Clone)] struct UniqueTypeId(ast::Name); // The TypeMap is where the CrateDebugContext holds the type metadata nodes diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 85c43f3f281..eb53fe2d673 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -561,7 +561,7 @@ pub fn get_vtable(bcx: Block, DUMMY_SP, trait_ref.clone()); match vtable { - traits::VtableBuiltin => { + traits::VtableBuiltin(_) => { Vec::new().into_iter() } traits::VtableImpl( diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index 9dda4d15350..e94764b0d00 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -390,13 +390,21 @@ impl TypeFoldable for traits::VtableImplData { } } +impl TypeFoldable for traits::VtableBuiltinData { + fn fold_with<'tcx, F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableBuiltinData { + traits::VtableBuiltinData { + nested: self.nested.fold_with(folder), + } + } +} + impl TypeFoldable for traits::Vtable { fn fold_with<'tcx, F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::Vtable { match *self { traits::VtableImpl(ref v) => traits::VtableImpl(v.fold_with(folder)), traits::VtableUnboxedClosure(d) => traits::VtableUnboxedClosure(d), traits::VtableParam(ref p) => traits::VtableParam(p.fold_with(folder)), - traits::VtableBuiltin => traits::VtableBuiltin, + traits::VtableBuiltin(ref d) => traits::VtableBuiltin(d.fold_with(folder)), } } } diff --git a/src/librustc/middle/typeck/check/vtable2.rs b/src/librustc/middle/typeck/check/vtable2.rs index 1d765c6c7c6..546ad86bc36 100644 --- a/src/librustc/middle/typeck/check/vtable2.rs +++ b/src/librustc/middle/typeck/check/vtable2.rs @@ -10,8 +10,7 @@ use middle::subst::{SelfSpace}; use middle::traits; -use middle::traits::{SelectionError, Overflow, - OutputTypeParameterMismatch, Unimplemented}; +use middle::traits::{SelectionError, OutputTypeParameterMismatch, Overflow, Unimplemented}; use middle::traits::{Obligation, obligation_for_builtin_bound}; use middle::traits::{FulfillmentError, CodeSelectionError, CodeAmbiguity}; use middle::traits::{ObligationCause}; @@ -233,6 +232,16 @@ pub fn report_selection_error(fcx: &FnCtxt, error: &SelectionError) { match *error { + Overflow => { + let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation); + fcx.tcx().sess.span_err( + obligation.cause.span, + format!( + "overflow evaluating the trait `{}` for the type `{}`", + trait_ref.user_string(fcx.tcx()), + self_ty.user_string(fcx.tcx())).as_slice()); + note_obligation_cause(fcx, obligation); + } Unimplemented => { let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation); if !ty::type_is_error(self_ty) { @@ -245,9 +254,6 @@ pub fn report_selection_error(fcx: &FnCtxt, note_obligation_cause(fcx, obligation); } } - Overflow => { - report_overflow(fcx, obligation); - } OutputTypeParameterMismatch(ref expected_trait_ref, ref e) => { let expected_trait_ref = fcx.infcx().resolve_type_vars_in_trait_ref_if_possible( @@ -269,21 +275,6 @@ pub fn report_selection_error(fcx: &FnCtxt, } } -pub fn report_overflow(fcx: &FnCtxt, obligation: &Obligation) { - let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation); - if ty::type_is_error(self_ty) { - fcx.tcx().sess.span_err( - obligation.cause.span, - format!( - "could not locate an impl of the trait `{}` for \ - the type `{}` due to overflow; possible cyclic \ - dependency between impls", - trait_ref.user_string(fcx.tcx()), - self_ty.user_string(fcx.tcx())).as_slice()); - note_obligation_cause(fcx, obligation); - } -} - pub fn maybe_report_ambiguity(fcx: &FnCtxt, obligation: &Obligation) { // Unable to successfully determine, probably means // insufficient type information, but could mean @@ -294,8 +285,9 @@ pub fn maybe_report_ambiguity(fcx: &FnCtxt, obligation: &Obligation) { trait_ref.repr(fcx.tcx()), self_ty.repr(fcx.tcx()), obligation.repr(fcx.tcx())); - if ty::type_is_error(self_ty) { - } else if ty::type_needs_infer(self_ty) { + let all_types = &trait_ref.substs.types; + if all_types.iter().any(|&t| ty::type_is_error(t)) { + } else if all_types.iter().any(|&t| ty::type_needs_infer(t)) { // This is kind of a hack: it frequently happens that some earlier // error prevents types from being fully inferred, and then we get // a bunch of uninteresting errors saying something like " InferCtxt<'a, 'tcx> { + pub fn skolemize(&self, t: T) -> T { + t.fold_with(&mut self.skolemizer()) + } + pub fn skolemizer<'a>(&'a self) -> TypeSkolemizer<'a, 'tcx> { skolemize::TypeSkolemizer::new(self) } @@ -630,11 +634,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { -> ures { debug!("sub_types({} <: {})", a.repr(self.tcx), b.repr(self.tcx)); - let trace = TypeTrace { - origin: origin, - values: Types(expected_found(a_is_expected, a, b)) - }; - self.sub(a_is_expected, trace).tys(a, b).to_ures() + self.commit_if_ok(|| { + let trace = TypeTrace { + origin: origin, + values: Types(expected_found(a_is_expected, a, b)) + }; + self.sub(a_is_expected, trace).tys(a, b).to_ures() + }) } pub fn eq_types(&self, @@ -644,11 +650,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { b: ty::t) -> ures { - let trace = TypeTrace { - origin: origin, - values: Types(expected_found(a_is_expected, a, b)) - }; - self.equate(a_is_expected, trace).tys(a, b).to_ures() + self.commit_if_ok(|| { + let trace = TypeTrace { + origin: origin, + values: Types(expected_found(a_is_expected, a, b)) + }; + self.equate(a_is_expected, trace).tys(a, b).to_ures() + }) } pub fn sub_trait_refs(&self, @@ -661,13 +669,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { debug!("sub_trait_refs({} <: {})", a.repr(self.tcx), b.repr(self.tcx)); - let trace = TypeTrace { - origin: origin, - values: TraitRefs(expected_found(a_is_expected, - a.clone(), b.clone())) - }; - let suber = self.sub(a_is_expected, trace); - suber.trait_refs(&*a, &*b).to_ures() + self.commit_if_ok(|| { + let trace = TypeTrace { + origin: origin, + values: TraitRefs(expected_found(a_is_expected, + a.clone(), b.clone())) + }; + self.sub(a_is_expected, trace).trait_refs(&*a, &*b).to_ures() + }) } } @@ -789,6 +798,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + pub fn shallow_resolve(&self, typ: ty::t) -> ty::t { + match ty::get(typ).sty { + ty::ty_infer(ty::TyVar(v)) => { + self.type_variables.borrow() + .probe(v) + .unwrap_or(typ) + } + + ty::ty_infer(ty::IntVar(v)) => { + self.probe_var(v) + .unwrap_or(typ) + } + + ty::ty_infer(ty::FloatVar(v)) => { + self.probe_var(v) + .unwrap_or(typ) + } + + _ => { + typ + } + } + } + pub fn resolve_type_vars_if_possible(&self, typ: ty::t) -> ty::t { match resolve_type(self, None, diff --git a/src/librustc/middle/typeck/infer/skolemize.rs b/src/librustc/middle/typeck/infer/skolemize.rs index 54ece395be9..1b3290c8b5a 100644 --- a/src/librustc/middle/typeck/infer/skolemize.rs +++ b/src/librustc/middle/typeck/infer/skolemize.rs @@ -12,17 +12,18 @@ * Skolemization is the process of replacing unknown variables with * fresh types. The idea is that the type, after skolemization, * contains no inference variables but instead contains either a value - * for each variable (if the variable had already fresh "arbitrary" - * types wherever a variable would have been. + * for each variable or fresh "arbitrary" types wherever a variable + * would have been. * - * Skolemization is used wherever we want to test what the type - * inferencer knows "so far". The primary place it is used right now - * is in the trait matching algorithm, which needs to be able to test - * whether an `impl` self type matches some other type X -- *without* - * affecting `X`. That means if that if the type `X` is in fact an - * unbound type variable, we want the match to be regarded as - * ambiguous, because depending on what type that type variable is - * ultimately assigned, the match may or may not succeed. + * Skolemization is used primarily to get a good type for inserting + * into a cache. The result summarizes what the type inferencer knows + * "so far". The primary place it is used right now is in the trait + * matching algorithm, which needs to be able to cache whether an + * `impl` self type matches some other type X -- *without* affecting + * `X`. That means if that if the type `X` is in fact an unbound type + * variable, we want the match to be regarded as ambiguous, because + * depending on what type that type variable is ultimately assigned, + * the match may or may not succeed. * * Note that you should be careful not to allow the output of * skolemization to leak to the user in error messages or in any other @@ -43,39 +44,45 @@ use middle::ty; use middle::ty_fold; use middle::ty_fold::TypeFoldable; use middle::ty_fold::TypeFolder; +use std::collections::hashmap; use super::InferCtxt; use super::unify::InferCtxtMethodsForSimplyUnifiableTypes; -use super::unify::SimplyUnifiable; -use super::unify::UnifyKey; pub struct TypeSkolemizer<'a, 'tcx:'a> { infcx: &'a InferCtxt<'a, 'tcx>, - skolemization_count: uint + skolemization_count: uint, + skolemization_map: hashmap::HashMap, } impl<'a, 'tcx> TypeSkolemizer<'a, 'tcx> { pub fn new<'tcx>(infcx: &'a InferCtxt<'a, 'tcx>) -> TypeSkolemizer<'a, 'tcx> { - TypeSkolemizer { infcx: infcx, skolemization_count: 0 } + TypeSkolemizer { + infcx: infcx, + skolemization_count: 0, + skolemization_map: hashmap::HashMap::new(), + } } - fn probe_ty(&mut self, v: ty::TyVid) -> ty::t { - self.skolemize_if_none(self.infcx.type_variables.borrow().probe(v), ty::SkolemizedTy) - } + fn skolemize(&mut self, + opt_ty: Option, + key: ty::InferTy, + skolemizer: |uint| -> ty::InferTy) + -> ty::t + { + match opt_ty { + Some(ty) => { return ty.fold_with(self); } + None => { } + } - fn probe_unifiable>>(&mut self, k: K) -> ty::t { - self.skolemize_if_none(self.infcx.probe_var(k), ty::SkolemizedIntTy) - } - - fn skolemize_if_none(&mut self, o: Option, - skolemizer: |uint| -> ty::InferTy) - -> ty::t { - match o { - Some(t) => t.fold_with(self), - None => { + match self.skolemization_map.entry(key) { + hashmap::Occupied(entry) => *entry.get(), + hashmap::Vacant(entry) => { let index = self.skolemization_count; self.skolemization_count += 1; - ty::mk_infer(self.tcx(), skolemizer(index)) + let t = ty::mk_infer(self.infcx.tcx, skolemizer(index)); + entry.set(t); + t } } } @@ -108,15 +115,21 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeSkolemizer<'a, 'tcx> { fn fold_ty(&mut self, t: ty::t) -> ty::t { match ty::get(t).sty { ty::ty_infer(ty::TyVar(v)) => { - self.probe_ty(v) + self.skolemize(self.infcx.type_variables.borrow().probe(v), + ty::TyVar(v), + ty::SkolemizedTy) } ty::ty_infer(ty::IntVar(v)) => { - self.probe_unifiable(v) + self.skolemize(self.infcx.probe_var(v), + ty::IntVar(v), + ty::SkolemizedIntTy) } ty::ty_infer(ty::FloatVar(v)) => { - self.probe_unifiable(v) + self.skolemize(self.infcx.probe_var(v), + ty::FloatVar(v), + ty::SkolemizedIntTy) } ty::ty_infer(ty::SkolemizedTy(c)) | diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs new file mode 100644 index 00000000000..578de06b747 --- /dev/null +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs @@ -0,0 +1,36 @@ +// 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 a blank impl for all T conflicts with an impl for some +// specific T, even when there are multiple type parameters involved. + +trait MyTrait { + fn get(&self) -> T; +} + +impl MyTrait for T { //~ ERROR E0119 + fn get(&self) -> T { + fail!() + } +} + +#[deriving(Clone)] +struct MyType { + dummy: uint +} + +impl MyTrait for MyType { + fn get(&self) -> uint { (*self).clone() } +} + +fn main() { } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs new file mode 100644 index 00000000000..9db322a5517 --- /dev/null +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.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. + +// Test that a blank impl for all T:PartialEq conflicts with an impl for some +// specific T when T:PartialEq. + +trait OtherTrait { + fn noop(&self); +} + +trait MyTrait { + fn get(&self) -> uint; +} + +impl MyTrait for T { //~ ERROR E0119 + fn get(&self) -> uint { 0 } +} + +struct MyType { + dummy: uint +} + +impl MyTrait for MyType { + fn get(&self) -> uint { self.dummy } +} + +impl OtherTrait for MyType { + fn noop(&self) { } +} + +fn main() { } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs new file mode 100644 index 00000000000..936025385bb --- /dev/null +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs @@ -0,0 +1,33 @@ +// 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 a blank impl for all T conflicts with an impl for some +// specific T. + +trait MyTrait { + fn get(&self) -> uint; +} + +impl MyTrait for T { //~ ERROR E0119 + fn get(&self) -> uint { 0 } +} + +struct MyType { + dummy: uint +} + +impl MyTrait for MyType { + fn get(&self) -> uint { self.dummy } +} + +fn main() { } diff --git a/src/test/compile-fail/conflicting-implementations-aux.rs b/src/test/compile-fail/coherence-cross-crate-conflict.rs similarity index 100% rename from src/test/compile-fail/conflicting-implementations-aux.rs rename to src/test/compile-fail/coherence-cross-crate-conflict.rs diff --git a/src/test/compile-fail/coherence-multidispatch-tuple b/src/test/compile-fail/coherence-multidispatch-tuple new file mode 100755 index 00000000000..279de3d0255 Binary files /dev/null and b/src/test/compile-fail/coherence-multidispatch-tuple differ diff --git a/src/test/compile-fail/coherence-tuple-conflict.rs b/src/test/compile-fail/coherence-tuple-conflict.rs new file mode 100644 index 00000000000..92fa725cb1e --- /dev/null +++ b/src/test/compile-fail/coherence-tuple-conflict.rs @@ -0,0 +1,29 @@ +// 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 a blank impl for all T conflicts with an impl for some +// specific T. + +trait MyTrait { + fn get(&self) -> uint; +} + +impl MyTrait for (T,T) { //~ ERROR E0119 + fn get(&self) -> uint { 0 } +} + +impl MyTrait for (A,B) { + fn get(&self) -> uint { self.dummy } +} + +fn main() { } diff --git a/src/test/compile-fail/recursion.rs b/src/test/compile-fail/recursion.rs index c99ec5187b0..d6a2fb75fa2 100644 --- a/src/test/compile-fail/recursion.rs +++ b/src/test/compile-fail/recursion.rs @@ -20,9 +20,8 @@ impl Dot for Cons { } } fn test (n:int, i:int, first:T, second:T) ->int { - //~^ ERROR: reached the recursion limit during monomorphization - match n { - 0 => {first.dot(second)} + match n { 0 => {first.dot(second)} + //~^ ERROR: reached the recursion limit during monomorphization // Error message should be here. It should be a type error // to instantiate `test` at a type other than T. (See #4287) _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})} diff --git a/src/test/compile-fail/trait-coercion-generic-bad.rs b/src/test/compile-fail/trait-coercion-generic-bad.rs index 74982946d7d..fcfd0799977 100644 --- a/src/test/compile-fail/trait-coercion-generic-bad.rs +++ b/src/test/compile-fail/trait-coercion-generic-bad.rs @@ -25,6 +25,6 @@ impl Trait<&'static str> for Struct { fn main() { let s: Box> = box Struct { person: "Fred" }; - //~^ ERROR type mismatch + //~^ ERROR the trait `Trait` is not implemented for the type `Struct` s.f(1); } diff --git a/src/test/compile-fail/traits-multidispatch-bad.rs b/src/test/compile-fail/traits-multidispatch-bad.rs new file mode 100644 index 00000000000..f5ce904a4bb --- /dev/null +++ b/src/test/compile-fail/traits-multidispatch-bad.rs @@ -0,0 +1,32 @@ +// 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. + +// Test that we detect an illegal combination of types. + +trait Convert { + fn convert(&self) -> Target; +} + +impl Convert for int { + fn convert(&self) -> uint { + *self as uint + } +} + +fn test(_: T, _: U) +where T : Convert +{ +} + +fn a() { + test(22i, 44i); //~ ERROR not implemented +} + +fn main() {} diff --git a/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs b/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs new file mode 100644 index 00000000000..9ceae41d1a4 --- /dev/null +++ b/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs @@ -0,0 +1,39 @@ +// 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. + +// Check that we get an error in a multidisptach scenario where the +// set of impls is ambiguous. + +trait Convert { + fn convert(&self) -> Target; +} + +impl Convert for i32 { + fn convert(&self) -> i8 { + *self as i8 + } +} + +impl Convert for i32 { + fn convert(&self) -> i16 { + *self as i16 + } +} + +fn test(_: T, _: U) +where T : Convert +{ +} + +fn a() { + test(22_i32, 44); //~ ERROR unable to infer +} + +fn main() {} diff --git a/src/test/compile-fail/type-params-in-different-spaces-2.rs b/src/test/compile-fail/type-params-in-different-spaces-2.rs index 580aea65185..d1bbda932cb 100644 --- a/src/test/compile-fail/type-params-in-different-spaces-2.rs +++ b/src/test/compile-fail/type-params-in-different-spaces-2.rs @@ -15,12 +15,12 @@ trait Tr { // these compile as if Self: Tr, even tho only Self: Tr trait A: Tr { fn test(u: U) -> Self { - Tr::op(u) //~ ERROR type mismatch + Tr::op(u) //~ ERROR not implemented } } trait B: Tr { fn test(u: U) -> Self { - Tr::op(u) //~ ERROR type mismatch + Tr::op(u) //~ ERROR not implemented } } diff --git a/src/test/compile-fail/conflicting-implementations.rs b/src/test/run-pass/coherence-multidispatch-tuple.rs similarity index 53% rename from src/test/compile-fail/conflicting-implementations.rs rename to src/test/run-pass/coherence-multidispatch-tuple.rs index b5a04491111..04a69bbf3a2 100644 --- a/src/test/compile-fail/conflicting-implementations.rs +++ b/src/test/run-pass/coherence-multidispatch-tuple.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -8,15 +8,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -trait Foo { +use std::fmt::Show; +use std::default::Default; + +// Test that an impl for homogeneous pairs does not conflict with a +// heterogeneous pair. + +trait MyTrait { + fn get(&self) -> uint; } -impl Foo for int { //~ ERROR conflicting implementations - +impl MyTrait for (T,T) { + fn get(&self) -> uint { 0 } } -impl Foo for A { //~ NOTE conflicting implementation here - +impl MyTrait for (uint,int) { + fn get(&self) -> uint { 0 } } fn main() { diff --git a/src/test/run-pass/coherence-where-clause.rs b/src/test/run-pass/coherence-where-clause.rs new file mode 100644 index 00000000000..faec0c50280 --- /dev/null +++ b/src/test/run-pass/coherence-where-clause.rs @@ -0,0 +1,46 @@ +// 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; + +trait MyTrait { + fn get(&self) -> Self; +} + +impl MyTrait for T + where T : Default +{ + fn get(&self) -> T { + Default::default() + } +} + +#[deriving(Clone,Show,PartialEq)] +struct MyType { + dummy: uint +} + +impl MyTrait for MyType { + fn get(&self) -> MyType { (*self).clone() } +} + +fn test_eq(m: M, n: M) +where M : MyTrait + Show + PartialEq +{ + assert_eq!(m.get(), n); +} + +pub fn main() { + test_eq(0u, 0u); + + let value = MyType { dummy: 256 + 22 }; + test_eq(value, value); +} diff --git a/src/test/run-pass/multidispatch1.rs b/src/test/run-pass/multidispatch1.rs new file mode 100644 index 00000000000..76c87f5d4c5 --- /dev/null +++ b/src/test/run-pass/multidispatch1.rs @@ -0,0 +1,40 @@ +// 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; + +trait MyTrait { + fn get(&self) -> T; +} + +struct MyType { + dummy: uint +} + +impl MyTrait for MyType { + fn get(&self) -> uint { self.dummy } +} + +impl MyTrait for MyType { + fn get(&self) -> u8 { self.dummy as u8 } +} + +fn test_eq(m: M, v: T) +where T : Eq + Show, + M : MyTrait +{ + assert_eq!(m.get(), v); +} + +pub fn main() { + let value = MyType { dummy: 256 + 22 }; + test_eq::(value, value.dummy); + test_eq::(value, value.dummy as u8); +} diff --git a/src/test/run-pass/multidispatch2.rs b/src/test/run-pass/multidispatch2.rs new file mode 100644 index 00000000000..13131be93c8 --- /dev/null +++ b/src/test/run-pass/multidispatch2.rs @@ -0,0 +1,46 @@ +// 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; + +trait MyTrait { + fn get(&self) -> T; +} + +impl MyTrait for T + where T : Default +{ + fn get(&self) -> T { + Default::default() + } +} + +struct MyType { + dummy: uint +} + +impl MyTrait for MyType { + fn get(&self) -> uint { self.dummy } +} + +fn test_eq(m: M, v: T) +where T : Eq + Show, + M : MyTrait +{ + assert_eq!(m.get(), v); +} + +pub fn main() { + test_eq(22u, 0u); + + let value = MyType { dummy: 256 + 22 }; + test_eq(value, value.dummy); +} diff --git a/src/test/run-pass/traits-conditional-dispatch.rs b/src/test/run-pass/traits-conditional-dispatch.rs new file mode 100644 index 00000000000..a94f73c2b6d --- /dev/null +++ b/src/test/run-pass/traits-conditional-dispatch.rs @@ -0,0 +1,36 @@ +// 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. + +// Test that we are able to resolve conditional dispatch. Here, the +// blanket impl for T:Copy coexists with an impl for Box, because +// Box does not impl Copy. + +trait Get { + fn get(&self) -> Self; +} + +impl Get for T { + fn get(&self) -> T { *self } +} + +impl Get for Box { + fn get(&self) -> Box { box get_it(&**self) } +} + +fn get_it(t: &T) -> T { + (*t).get() +} + +fn main() { + assert_eq!(get_it(&1_u32), 1_u32); + assert_eq!(get_it(&1_u16), 1_u16); + assert_eq!(get_it(&Some(1_u16)), Some(1_u16)); + assert_eq!(get_it(&box 1i), box 1i); +} diff --git a/src/test/run-pass/traits-multidispatch-infer-convert-source-and-target.rs b/src/test/run-pass/traits-multidispatch-infer-convert-source-and-target.rs new file mode 100644 index 00000000000..c10029791df --- /dev/null +++ b/src/test/run-pass/traits-multidispatch-infer-convert-source-and-target.rs @@ -0,0 +1,35 @@ +// 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. + +// Test that if there is one impl we can infer everything. + +use std::mem; + +trait Convert { + fn convert(&self) -> Target; +} + +impl Convert for i16 { + fn convert(&self) -> u32 { + *self as u32 + } +} + +fn test(_: T, _: U, t_size: uint, u_size: uint) +where T : Convert +{ + assert_eq!(mem::size_of::(), t_size); + assert_eq!(mem::size_of::(), u_size); +} + +fn main() { + // T = i16, U = u32 + test(22, 44, 2, 4); +} diff --git a/src/test/run-pass/traits-multidispatch-infer-convert-target.rs b/src/test/run-pass/traits-multidispatch-infer-convert-target.rs new file mode 100644 index 00000000000..54515f3b0d7 --- /dev/null +++ b/src/test/run-pass/traits-multidispatch-infer-convert-target.rs @@ -0,0 +1,46 @@ +// 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. + +// Test that we can infer the Target based on the Self or vice versa. + +use std::mem; + +trait Convert { + fn convert(&self) -> Target; +} + +impl Convert for i16 { + fn convert(&self) -> u32 { + *self as u32 + } +} + +impl Convert for u32 { + fn convert(&self) -> i16 { + *self as i16 + } +} + +fn test(_: T, _: U, t_size: uint, u_size: uint) +where T : Convert +{ + assert_eq!(mem::size_of::(), t_size); + assert_eq!(mem::size_of::(), u_size); +} + +fn main() { + // T = i16, U = u32 + test(22_i16, 44, 2, 4); + test(22, 44_u32, 2, 4); + + // T = u32, U = i16 + test(22_u32, 44, 4, 2); + test(22, 44_i16, 4, 2); +}