提交 389ef660 编写于 作者: N Niko Matsakis

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
上级 79d056f9
......@@ -417,6 +417,10 @@ pub fn iter<'a>(&'a self) -> Items<'a,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, &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,
......
......@@ -57,7 +57,8 @@ fn clone_slice<T:Clone>(x: &[T]) -> Vec<T> {
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 @@ fn clone_slice<T:Clone>(x: &[T]) -> Vec<T> {
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:
trait Convert<T> { // T is output, Self is input
fn convert(&self) -> T;
}
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(); // (*)
...
}}}
}
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.
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.
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.
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<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 @@ fn something() {
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::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 @@ pub fn dummy() -> 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 @@ pub fn map_move_nested<M>(self, op: |N| -> M) -> Vtable<M> {
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 @@ pub fn map_move_nested<M>(self, op: |N| -> M) -> VtableImplData<M> {
}
}
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 @@ fn new(obligation: Obligation, code: FulfillmentErrorCode)
{
FulfillmentError { obligation: obligation, code: code }
}
pub fn is_overflow(&self) -> bool {
match self.code {
CodeAmbiguity => false,
CodeSelectionError(Overflow) => true,
CodeSelectionError(_) => false,
}
}
}
......@@ -319,8 +319,8 @@ fn repr(&self, tcx: &ty::ctxt) -> String {
super::VtableParam(ref v) =>
format!("VtableParam({})", v.repr(tcx)),
super::VtableBuiltin =>
format!("Builtin"),
super::VtableBuiltin(ref d) =>
d.repr(tcx)
}
}
}
......@@ -334,6 +334,13 @@ fn repr(&self, tcx: &ty::ctxt) -> String {
}
}
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 @@ fn repr(&self, tcx: &ty::ctxt) -> String {
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, &param_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 @@ struct List {
// 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 @@ fn fold_with<'tcx, F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableI
}
}
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::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 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 @@ pub fn sub_types(&self,
-> 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 @@ pub fn eq_types(&self,
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 @@ pub fn sub_trait_refs(&self,
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 @@ pub fn contains_unbound_type_variables(&self, typ: ty::t) -> ty::t {
}
}
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_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 }
}
fn probe_ty(&mut self, v: ty::TyVid) -> ty::t {
self.skolemize_if_none(self.infcx.type_variables.borrow().probe(v), ty::SkolemizedTy)
TypeSkolemizer {
infcx: infcx,
skolemization_count: 0,
skolemization_map: hashmap::HashMap::new(),
}
}
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(&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 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 @@ fn fold_region(&mut self, r: ty::Region) -> ty::Region {
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)) |
......
// 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() { }
// 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() { }
// 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() { }
// 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 @@ fn dot(&self, other:Cons<T>) -> int {
}
}
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 @@ fn f(&self, x: &'static str) {
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);
}
// 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() {}
// 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
}
}
......
// 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;
impl Foo for int { //~ ERROR conflicting implementations
// Test that an impl for homogeneous pairs does not conflict with a
// heterogeneous pair.
trait MyTrait {
fn get(&self) -> uint;
}
impl<A> Foo for A { //~ NOTE conflicting implementation here
impl<T> MyTrait for (T,T) {
fn get(&self) -> uint { 0 }
}
impl MyTrait for (uint,int) {
fn get(&self) -> uint { 0 }
}
fn main() {
......
// 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);
}
// 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);
}
// 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);
}
// 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);
}
// 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);
}
// 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);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册