mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 15:23:46 +00:00
Implement multidispatch and conditional dispatch. Because we do not
attempt to preserve crate concatenation, this is a backwards compatible change. Conflicts: src/librustc/middle/traits/select.rs
This commit is contained in:
parent
79d056f94b
commit
389ef6601d
@ -417,6 +417,10 @@ impl<T> VecPerParamSpace<T> {
|
||||
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)) })
|
||||
|
@ -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<X,Y> 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,
|
||||
|
@ -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> { // 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<uint> for int { ... }
|
||||
#### The basic process: Inferring based on the impls we see
|
||||
|
||||
Now assume we have an obligation `int : Convert<char>`. 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<Target> {
|
||||
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<uint> for int { ... } // int -> uint
|
||||
impl Convert<int> 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<int>`)? 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<uint> 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<T:Copy> Get for T {
|
||||
fn get(&self) -> T { *self }
|
||||
}
|
||||
|
||||
impl<T:Get> Get for Box<T> {
|
||||
fn get(&self) -> Box<T> { box get_it(&**self) }
|
||||
}
|
||||
```
|
||||
|
||||
What happens when we invoke `get_it(&box 1_u16)`, for example? In this
|
||||
case, the `Self` type is `Box<u16>` -- 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<u16> : 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).
|
||||
|
@ -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<T> = Result<Option<T>, 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<N> {
|
||||
VtableParam(VtableParamData),
|
||||
|
||||
/// Successful resolution for a builtin trait.
|
||||
VtableBuiltin,
|
||||
VtableBuiltin(VtableBuiltinData<N>),
|
||||
}
|
||||
|
||||
/**
|
||||
@ -208,12 +202,17 @@ pub struct VtableImplData<N> {
|
||||
pub nested: subst::VecPerParamSpace<N>
|
||||
}
|
||||
|
||||
#[deriving(Show,Clone)]
|
||||
pub struct VtableBuiltinData<N> {
|
||||
pub nested: subst::VecPerParamSpace<N>
|
||||
}
|
||||
|
||||
/**
|
||||
* A vtable provided as a parameter by the caller. For example, in a
|
||||
* function like `fn foo<T:Eq>(...)`, 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<ty::TraitRef>,
|
||||
@ -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<N> Vtable<N> {
|
||||
pub fn iter_nested(&self) -> Items<N> {
|
||||
match *self {
|
||||
VtableImpl(ref i) => i.iter_nested(),
|
||||
VtableUnboxedClosure(_) => (&[]).iter(),
|
||||
VtableParam(_) => (&[]).iter(),
|
||||
VtableBuiltin(ref i) => i.iter_nested(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_nested<M>(&self, op: |&N| -> M) -> Vtable<M> {
|
||||
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<N> Vtable<N> {
|
||||
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<N> VtableImplData<N> {
|
||||
pub fn iter_nested(&self) -> Items<N> {
|
||||
self.nested.iter()
|
||||
}
|
||||
|
||||
pub fn map_nested<M>(&self,
|
||||
op: |&N| -> M)
|
||||
-> VtableImplData<M>
|
||||
@ -413,11 +402,23 @@ impl<N> VtableImplData<N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl EvaluationResult {
|
||||
pub fn potentially_applicable(&self) -> bool {
|
||||
match *self {
|
||||
EvaluatedToMatch | EvaluatedToAmbiguity => true,
|
||||
EvaluatedToUnmatch => false
|
||||
impl<N> VtableBuiltinData<N> {
|
||||
pub fn iter_nested(&self) -> Items<N> {
|
||||
self.nested.iter()
|
||||
}
|
||||
|
||||
pub fn map_nested<M>(&self,
|
||||
op: |&N| -> M)
|
||||
-> VtableBuiltinData<M>
|
||||
{
|
||||
VtableBuiltinData {
|
||||
nested: self.nested.map(op)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_move_nested<M>(self, op: |N| -> M) -> VtableBuiltinData<M> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -319,8 +319,8 @@ impl<N:Repr> Repr for super::Vtable<N> {
|
||||
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<N:Repr> Repr for super::VtableImplData<N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<N:Repr> Repr for super::VtableBuiltinData<N> {
|
||||
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),
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -390,13 +390,21 @@ impl<N:TypeFoldable> TypeFoldable for traits::VtableImplData<N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<N:TypeFoldable> TypeFoldable for traits::VtableBuiltinData<N> {
|
||||
fn fold_with<'tcx, F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableBuiltinData<N> {
|
||||
traits::VtableBuiltinData {
|
||||
nested: self.nested.fold_with(folder),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N:TypeFoldable> TypeFoldable for traits::Vtable<N> {
|
||||
fn fold_with<'tcx, F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::Vtable<N> {
|
||||
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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 "<generic
|
||||
@ -321,7 +313,7 @@ pub fn maybe_report_ambiguity(fcx: &FnCtxt, obligation: &Obligation) {
|
||||
self_ty.user_string(fcx.tcx())).as_slice());
|
||||
note_obligation_cause(fcx, obligation);
|
||||
}
|
||||
} else if fcx.tcx().sess.err_count() == 0 {
|
||||
} else if !fcx.tcx().sess.has_errors() {
|
||||
// Ambiguity. Coherence should have reported an error.
|
||||
fcx.tcx().sess.span_bug(
|
||||
obligation.cause.span,
|
||||
|
@ -27,18 +27,8 @@ use middle::subst::Substs;
|
||||
use middle::ty::{TyVid, IntVid, FloatVid, RegionVid};
|
||||
use middle::ty;
|
||||
use middle::ty_fold;
|
||||
use middle::ty_fold::TypeFolder;
|
||||
use middle::ty_fold::{TypeFolder, TypeFoldable};
|
||||
use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig;
|
||||
use middle::typeck::infer::coercion::Coerce;
|
||||
use middle::typeck::infer::combine::{Combine, CombineFields};
|
||||
use middle::typeck::infer::region_inference::{RegionVarBindings,
|
||||
RegionSnapshot};
|
||||
use middle::typeck::infer::resolve::{resolver};
|
||||
use middle::typeck::infer::equate::Equate;
|
||||
use middle::typeck::infer::sub::Sub;
|
||||
use middle::typeck::infer::lub::Lub;
|
||||
use middle::typeck::infer::unify::{UnificationTable};
|
||||
use middle::typeck::infer::error_reporting::ErrorReporting;
|
||||
use std::cell::{RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
@ -48,6 +38,16 @@ use syntax::codemap::Span;
|
||||
use util::common::indent;
|
||||
use util::ppaux::{bound_region_to_string, ty_to_string, trait_ref_to_string, Repr};
|
||||
|
||||
use self::coercion::Coerce;
|
||||
use self::combine::{Combine, CombineFields};
|
||||
use self::region_inference::{RegionVarBindings, RegionSnapshot};
|
||||
use self::resolve::{resolver};
|
||||
use self::equate::Equate;
|
||||
use self::sub::Sub;
|
||||
use self::lub::Lub;
|
||||
use self::unify::{UnificationTable, InferCtxtMethodsForSimplyUnifiableTypes};
|
||||
use self::error_reporting::ErrorReporting;
|
||||
|
||||
pub mod coercion;
|
||||
pub mod combine;
|
||||
pub mod doc;
|
||||
@ -503,6 +503,10 @@ pub struct CombinedSnapshot {
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
pub fn skolemize<T:TypeFoldable>(&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,
|
||||
|
@ -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<ty::InferTy, ty::t>,
|
||||
}
|
||||
|
||||
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<ty::t>,
|
||||
key: ty::InferTy,
|
||||
skolemizer: |uint| -> ty::InferTy)
|
||||
-> ty::t
|
||||
{
|
||||
match opt_ty {
|
||||
Some(ty) => { return ty.fold_with(self); }
|
||||
None => { }
|
||||
}
|
||||
|
||||
fn probe_unifiable<V:SimplyUnifiable,K:UnifyKey<Option<V>>>(&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<ty::t>,
|
||||
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)) |
|
||||
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> {
|
||||
fn get(&self) -> T;
|
||||
}
|
||||
|
||||
impl<T> MyTrait<T> for T { //~ ERROR E0119
|
||||
fn get(&self) -> T {
|
||||
fail!()
|
||||
}
|
||||
}
|
||||
|
||||
#[deriving(Clone)]
|
||||
struct MyType {
|
||||
dummy: uint
|
||||
}
|
||||
|
||||
impl MyTrait<MyType> for MyType {
|
||||
fn get(&self) -> uint { (*self).clone() }
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T:OtherTrait> 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() { }
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> 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() { }
|
BIN
src/test/compile-fail/coherence-multidispatch-tuple
Executable file
BIN
src/test/compile-fail/coherence-multidispatch-tuple
Executable file
Binary file not shown.
29
src/test/compile-fail/coherence-tuple-conflict.rs
Normal file
29
src/test/compile-fail/coherence-tuple-conflict.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> MyTrait for (T,T) { //~ ERROR E0119
|
||||
fn get(&self) -> uint { 0 }
|
||||
}
|
||||
|
||||
impl<A,B> MyTrait for (A,B) {
|
||||
fn get(&self) -> uint { self.dummy }
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -20,9 +20,8 @@ impl<T:Dot> Dot for Cons<T> {
|
||||
}
|
||||
}
|
||||
fn test<T:Dot> (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})}
|
||||
|
@ -25,6 +25,6 @@ impl Trait<&'static str> for Struct {
|
||||
|
||||
fn main() {
|
||||
let s: Box<Trait<int>> = box Struct { person: "Fred" };
|
||||
//~^ ERROR type mismatch
|
||||
//~^ ERROR the trait `Trait<int>` is not implemented for the type `Struct`
|
||||
s.f(1);
|
||||
}
|
||||
|
32
src/test/compile-fail/traits-multidispatch-bad.rs
Normal file
32
src/test/compile-fail/traits-multidispatch-bad.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Target> {
|
||||
fn convert(&self) -> Target;
|
||||
}
|
||||
|
||||
impl Convert<uint> for int {
|
||||
fn convert(&self) -> uint {
|
||||
*self as uint
|
||||
}
|
||||
}
|
||||
|
||||
fn test<T,U>(_: T, _: U)
|
||||
where T : Convert<U>
|
||||
{
|
||||
}
|
||||
|
||||
fn a() {
|
||||
test(22i, 44i); //~ ERROR not implemented
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Target> {
|
||||
fn convert(&self) -> Target;
|
||||
}
|
||||
|
||||
impl Convert<i8> for i32 {
|
||||
fn convert(&self) -> i8 {
|
||||
*self as i8
|
||||
}
|
||||
}
|
||||
|
||||
impl Convert<i16> for i32 {
|
||||
fn convert(&self) -> i16 {
|
||||
*self as i16
|
||||
}
|
||||
}
|
||||
|
||||
fn test<T,U>(_: T, _: U)
|
||||
where T : Convert<U>
|
||||
{
|
||||
}
|
||||
|
||||
fn a() {
|
||||
test(22_i32, 44); //~ ERROR unable to infer
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -15,12 +15,12 @@ trait Tr<T> {
|
||||
// these compile as if Self: Tr<U>, even tho only Self: Tr<Self or T>
|
||||
trait A: Tr<Self> {
|
||||
fn test<U>(u: U) -> Self {
|
||||
Tr::op(u) //~ ERROR type mismatch
|
||||
Tr::op(u) //~ ERROR not implemented
|
||||
}
|
||||
}
|
||||
trait B<T>: Tr<T> {
|
||||
fn test<U>(u: U) -> Self {
|
||||
Tr::op(u) //~ ERROR type mismatch
|
||||
Tr::op(u) //~ ERROR not implemented
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<T> MyTrait for (T,T) {
|
||||
fn get(&self) -> uint { 0 }
|
||||
}
|
||||
|
||||
impl<A> Foo for A { //~ NOTE conflicting implementation here
|
||||
|
||||
impl MyTrait for (uint,int) {
|
||||
fn get(&self) -> uint { 0 }
|
||||
}
|
||||
|
||||
fn main() {
|
46
src/test/run-pass/coherence-where-clause.rs
Normal file
46
src/test/run-pass/coherence-where-clause.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> 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: 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);
|
||||
}
|
40
src/test/run-pass/multidispatch1.rs
Normal file
40
src/test/run-pass/multidispatch1.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::fmt::Show;
|
||||
|
||||
trait MyTrait<T> {
|
||||
fn get(&self) -> T;
|
||||
}
|
||||
|
||||
struct MyType {
|
||||
dummy: uint
|
||||
}
|
||||
|
||||
impl MyTrait<uint> for MyType {
|
||||
fn get(&self) -> uint { self.dummy }
|
||||
}
|
||||
|
||||
impl MyTrait<u8> for MyType {
|
||||
fn get(&self) -> u8 { self.dummy as u8 }
|
||||
}
|
||||
|
||||
fn test_eq<T,M>(m: M, v: T)
|
||||
where T : Eq + Show,
|
||||
M : MyTrait<T>
|
||||
{
|
||||
assert_eq!(m.get(), v);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let value = MyType { dummy: 256 + 22 };
|
||||
test_eq::<uint, _>(value, value.dummy);
|
||||
test_eq::<u8, _>(value, value.dummy as u8);
|
||||
}
|
46
src/test/run-pass/multidispatch2.rs
Normal file
46
src/test/run-pass/multidispatch2.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> {
|
||||
fn get(&self) -> T;
|
||||
}
|
||||
|
||||
impl<T> MyTrait<T> for T
|
||||
where T : Default
|
||||
{
|
||||
fn get(&self) -> T {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
struct MyType {
|
||||
dummy: uint
|
||||
}
|
||||
|
||||
impl MyTrait<uint> for MyType {
|
||||
fn get(&self) -> uint { self.dummy }
|
||||
}
|
||||
|
||||
fn test_eq<T,M>(m: M, v: T)
|
||||
where T : Eq + Show,
|
||||
M : MyTrait<T>
|
||||
{
|
||||
assert_eq!(m.get(), v);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
test_eq(22u, 0u);
|
||||
|
||||
let value = MyType { dummy: 256 + 22 };
|
||||
test_eq(value, value.dummy);
|
||||
}
|
36
src/test/run-pass/traits-conditional-dispatch.rs
Normal file
36
src/test/run-pass/traits-conditional-dispatch.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T>, because
|
||||
// Box does not impl Copy.
|
||||
|
||||
trait Get {
|
||||
fn get(&self) -> Self;
|
||||
}
|
||||
|
||||
impl<T:Copy> Get for T {
|
||||
fn get(&self) -> T { *self }
|
||||
}
|
||||
|
||||
impl<T:Get> Get for Box<T> {
|
||||
fn get(&self) -> Box<T> { box get_it(&**self) }
|
||||
}
|
||||
|
||||
fn get_it<T:Get>(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);
|
||||
}
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Target> {
|
||||
fn convert(&self) -> Target;
|
||||
}
|
||||
|
||||
impl Convert<u32> for i16 {
|
||||
fn convert(&self) -> u32 {
|
||||
*self as u32
|
||||
}
|
||||
}
|
||||
|
||||
fn test<T,U>(_: T, _: U, t_size: uint, u_size: uint)
|
||||
where T : Convert<U>
|
||||
{
|
||||
assert_eq!(mem::size_of::<T>(), t_size);
|
||||
assert_eq!(mem::size_of::<U>(), u_size);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// T = i16, U = u32
|
||||
test(22, 44, 2, 4);
|
||||
}
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Target> {
|
||||
fn convert(&self) -> Target;
|
||||
}
|
||||
|
||||
impl Convert<u32> for i16 {
|
||||
fn convert(&self) -> u32 {
|
||||
*self as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl Convert<i16> for u32 {
|
||||
fn convert(&self) -> i16 {
|
||||
*self as i16
|
||||
}
|
||||
}
|
||||
|
||||
fn test<T,U>(_: T, _: U, t_size: uint, u_size: uint)
|
||||
where T : Convert<U>
|
||||
{
|
||||
assert_eq!(mem::size_of::<T>(), t_size);
|
||||
assert_eq!(mem::size_of::<U>(), 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user