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:
Niko Matsakis 2014-10-09 17:19:50 -04:00
parent 79d056f94b
commit 389ef6601d
31 changed files with 1425 additions and 755 deletions

View File

@ -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)) })

View File

@ -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, &param_env, infcx.tcx, ObligationCause::dummy(),
impl2_def_id, impl1_self_ty) {
EvaluatedToMatch | EvaluatedToAmbiguity => true,
EvaluatedToUnmatch => false,
}
let mut selcx = SelectionContext::new(infcx, &param_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,

View File

@ -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.
#### The basic process: Inferring based on the impls we see
This process is easier if we work through some examples. Consider
the following trait:
```
trait Convert<Target> {
fn convert(&self) -> Target;
}
```
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:
```rust
impl Convert<uint> for int { ... } // int -> uint
impl Convert<int> for uint { ... } // uint -> uint
```
Now imagine there is some code like the following:
```rust
let x: int = ...;
let y = x.convert();
```
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.)
There are tests to this effect in src/test/run-pass:
traits-multidispatch-infer-convert-source-and-target.rs
traits-multidispatch-infer-convert-target.rs
#### Winnowing: Resolving ambiguities
But what happens if there are multiple impls where all the types
unify? Consider this example:
```rust
trait Get {
fn get(&self) -> Self;
}
impl Convert<uint> for int { ... }
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.
#### Skolemization
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.
Here is an example:
trait Foo { fn method() { ... }}
impl Foo for int { ... }
fn something() {
let mut x = None; // `x` has type `Option<?>`
loop {
match x {
Some(ref y) => { // `y` has type ?
y.method(); // (*)
...
}}}
impl<T:Copy> Get for T {
fn get(&self) -> T { *self }
}
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.
impl<T:Get> Get for Box<T> {
fn get(&self) -> Box<T> { box get_it(&**self) }
}
```
It is unclear how important this property is. It might be nice to drop it.
But for the time being we maintain it.
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.
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.
*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.
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).

View File

@ -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

View File

@ -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),

View File

@ -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,14 +834,21 @@ pub fn fulfill_obligation(ccx: &CrateContext,
});
match fulfill_cx.select_all_or_error(&infcx, &param_env, tcx) {
Ok(()) => { }
Err(e) => {
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",
e.repr(tcx),
errors.repr(tcx),
trait_ref.repr(tcx)).as_slice());
}
}
}
// Use skolemize to simultaneously replace all type variables with
// their bindings and replace all regions with 'static. This is

View File

@ -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

View File

@ -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(

View File

@ -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)),
}
}
}

View File

@ -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,

View File

@ -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));
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
{
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));
self.commit_if_ok(|| {
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.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,

View File

@ -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 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>,
fn skolemize(&mut self,
opt_ty: Option<ty::t>,
key: ty::InferTy,
skolemizer: |uint| -> ty::InferTy)
-> ty::t {
match o {
Some(t) => t.fold_with(self),
None => {
-> ty::t
{
match opt_ty {
Some(ty) => { return ty.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)) |

View 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.
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() { }

View File

@ -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() { }

View File

@ -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() { }

Binary file not shown.

View 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() { }

View File

@ -20,9 +20,8 @@ impl<T:Dot> Dot for Cons<T> {
}
}
fn test<T:Dot> (n:int, i:int, first:T, second:T) ->int {
match n { 0 => {first.dot(second)}
//~^ ERROR: reached the recursion limit during monomorphization
match n {
0 => {first.dot(second)}
// 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})}

View File

@ -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);
}

View 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() {}

View File

@ -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() {}

View File

@ -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
}
}

View File

@ -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() {

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View File

@ -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);
}

View 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.
// 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);
}