Guts of the new trait matching algorithm, not yet in use

This commit is contained in:
Niko Matsakis 2014-09-12 10:53:35 -04:00
parent 713cf373c1
commit c5754f3971
15 changed files with 2856 additions and 106 deletions

View File

@ -108,6 +108,7 @@ pub mod middle {
pub mod save;
pub mod stability;
pub mod subst;
pub mod traits;
pub mod trans;
pub mod ty;
pub mod ty_fold;

View File

@ -85,6 +85,17 @@ impl LanguageItems {
}
}
pub fn from_builtin_kind(&self, bound: ty::BuiltinBound)
-> Result<ast::DefId, String>
{
match bound {
ty::BoundSend => self.require(SendTraitLangItem),
ty::BoundSized => self.require(SizedTraitLangItem),
ty::BoundCopy => self.require(CopyTraitLangItem),
ty::BoundSync => self.require(SyncTraitLangItem),
}
}
pub fn to_builtin_kind(&self, id: ast::DefId) -> Option<ty::BuiltinBound> {
if Some(id) == self.send_trait() {
Some(ty::BoundSend)

View File

@ -333,6 +333,16 @@ impl<T> VecPerParamSpace<T> {
}
}
fn new_internal(content: Vec<T>, type_limit: uint, self_limit: uint)
-> VecPerParamSpace<T>
{
VecPerParamSpace {
type_limit: type_limit,
self_limit: self_limit,
content: content,
}
}
pub fn sort(t: Vec<T>, space: |&T| -> ParamSpace) -> VecPerParamSpace<T> {
let mut result = VecPerParamSpace::empty();
for t in t.move_iter() {
@ -448,13 +458,17 @@ impl<T> VecPerParamSpace<T> {
}
pub fn map<U>(&self, pred: |&T| -> U) -> VecPerParamSpace<U> {
// FIXME (#15418): this could avoid allocating the intermediate
// Vec's, but note that the values of type_limit and self_limit
// also need to be kept in sync during construction.
VecPerParamSpace::new(
self.get_slice(TypeSpace).iter().map(|p| pred(p)).collect(),
self.get_slice(SelfSpace).iter().map(|p| pred(p)).collect(),
self.get_slice(FnSpace).iter().map(|p| pred(p)).collect())
let result = self.iter().map(pred).collect();
VecPerParamSpace::new_internal(result,
self.type_limit,
self.self_limit)
}
pub fn map_move<U>(self, pred: |T| -> U) -> VecPerParamSpace<U> {
let (t, s, f) = self.split();
VecPerParamSpace::new(t.move_iter().map(|p| pred(p)).collect(),
s.move_iter().map(|p| pred(p)).collect(),
f.move_iter().map(|p| pred(p)).collect())
}
pub fn map_rev<U>(&self, pred: |&T| -> U) -> VecPerParamSpace<U> {

View File

@ -0,0 +1,268 @@
// 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.
/*!
# TRAIT RESOLUTION
This document describes the general process and points out some non-obvious
things.
## Major concepts
Trait resolution is the process of pairing up an impl with each
reference to a trait. So, for example, if there is a generic function like:
fn clone_slice<T:Clone>(x: &[T]) -> Vec<T> { ... }
and then a call to that function:
let v: Vec<int> = clone_slice([1, 2, 3].as_slice())
it is the job of trait resolution to figure out (in which case)
whether there exists an impl of `int : Clone`
Note that in some cases, like generic functions, we may not be able to
find a specific impl, but we can figure out that the caller must
provide an impl. To see what I mean, consider the body of `clone_slice`:
fn clone_slice<T:Clone>(x: &[T]) -> Vec<T> {
let mut v = Vec::new();
for e in x.iter() {
v.push((*e).clone()); // (*)
}
}
The line marked `(*)` is only legal if `T` (the type of `*e`)
implements the `Clone` trait. Naturally, since we don't know what `T`
is, we can't find the specific impl; but based on the bound `T:Clone`,
we can say that there exists an impl which the caller must provide.
We use the term *obligation* to refer to a trait reference in need of
an impl.
## Overview
Trait resolution consists of three major parts:
- SELECTION: Deciding how to resolve a specific obligation. For
example, selection might decide that a specific obligation can be
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.
- FULFILLMENT: The fulfillment code is what tracks that obligations
are completely fulfilled. Basically it is a worklist of obligations
to be selected: once selection is successful, the obligation is
removed from the worklist and any nested obligations are enqueued.
- COHERENCE: The coherence checks are intended to ensure that there
are never overlapping impls, where two impls could be used with
equal precedence.
## Selection
Selection is the process of deciding whether an obligation can be
resolved and, if so, how it is to be resolved (via impl, where clause, etc).
The main interface is the `select()` function, which takes an obligation
and returns a `SelectionResult`. There are three possible outcomes:
- `Ok(Some(selection))` -- yes, the obligation can be resolved, and
`selection` indicates how. If the impl was resolved via an impl,
then `selection` may also indicate nested obligations that are required
by the impl.
- `Ok(None)` -- we are not yet sure whether the obligation can be
resolved or not. This happens most commonly when the obligation
contains unbound type variables.
- `Err(err)` -- the obligation definitely cannot be resolved due to a
type error, or because there are no impls that could possibly apply,
etc.
The basic algorithm for selection is broken into two big phases:
candidate assembly and confirmation.
### Candidate assembly
Searches for impls/where-clauses/etc that might
possibly be used to satisfy the obligation. Each of those is called
a candidate. To avoid ambiguity, we want to find exactly one
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:
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.
#### Matching
The subroutines that decide whether a particular impl/where-clause/etc
applies to a particular obligation. At the moment, this amounts to
unifying the self types, but in the future we may also recursively
consider some of the nested obligations, in the case of an impl.
#### Lifetimes and selection
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
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).
#### Where clauses
Besides an impl, the other major way to resolve an obligation is via a
where clause. The selection process is always given a *parameter
environment* which contains a list of where clauses, which are
basically obligations that can assume are satisfiable. We will iterate
over that list and check whether our current obligation can be found
in that list, and if so it is considered satisfied. More precisely, we
want to check whether there is a where-clause obligation that is for
the same trait (or some subtrait) and for which the self types match,
using the definition of *matching* given above.
Consider this simple example:
trait A1 { ... }
trait A2 : A1 { ... }
trait B { ... }
fn foo<X:A2+B> { ... }
Clearly we can use methods offered by `A1`, `A2`, or `B` within the
body of `foo`. In each case, that will incur an obligation like `X :
A1` or `X : A2`. The parameter environment will contain two
where-clauses, `X : A2` and `X : B`. For each obligation, then, we
search this list of where-clauses. To resolve an obligation `X:A1`,
we would note that `X:A2` implies that `X:A1`.
### Confirmation
Confirmation unifies the output type parameters of the trait with the
values found in the obligation, possibly yielding a type error. If we
return to our example of the `Convert` trait from the previous
section, confirmation is where an error would be reported, because the
impl specified that `T` would be `uint`, but the obligation reported
`char`. Hence the result of selection would be an error.
### Selection during translation
During type checking, we do not store the results of trait selection.
We simply wish to verify that trait selection will succeed. Then
later, at trans time, when we have all concrete types available, we
can repeat the trait selection. In this case, we do not consider any
where-clauses to be in scope. We know that therefore each resolution
will resolve to a particular impl.
One interesting twist has to do with nested obligations. In general, in trans,
we only need to do a "shallow" selection for an obligation. That is, we wish to
identify which impl applies, but we do not (yet) need to decide how to select
any nested obligations. Nonetheless, we *do* currently do a complete resolution,
and that is because it can sometimes inform the results of type inference. That is,
we do not have the full substitutions in terms of the type varibales of the impl available
to us, so we must run trait selection to figure everything out.
Here is an example:
trait Foo { ... }
impl<U,T:Bar<U>> Foo for Vec<T> { ... }
impl Bar<uint> for int { ... }
After one shallow round of selection for an obligation like `Vec<int>
: Foo`, we would know which impl we want, and we would know that
`T=int`, but we do not know the type of `U`. We must select the
nested obligation `int : Bar<U>` to find out that `U=uint`.
It would be good to only do *just as much* nested resolution as
necessary. Currently, though, we just do a full resolution.
*/

View File

@ -0,0 +1,250 @@
// 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 middle::ty;
use middle::typeck::infer::{InferCtxt, skolemize};
use util::nodemap::DefIdMap;
use util::ppaux::Repr;
use super::Ambiguity;
use super::Obligation;
use super::FulfillmentError;
use super::SelectionError;
use super::select::SelectionContext;
use super::Unimplemented;
/**
* The fulfillment context is used to drive trait resolution. It
* consists of a list of obligations that must be (eventually)
* satisfied. The job is to track which are satisfied, which yielded
* errors, and which are still pending. At any point, users can call
* `select_where_possible`, and the fulfilment context will try to do
* selection, retaining only those obligations that remain
* ambiguous. This may be helpful in pushing type inference
* along. Once all type inference constraints have been generated, the
* method `select_all_or_error` can be used to report any remaining
* ambiguous cases as errors.
*/
pub struct FulfillmentContext {
// A list of all obligations that have been registered with this
// fulfillment context.
trait_obligations: Vec<Obligation>,
// For semi-hacky reasons (see FIXME below) we keep the builtin
// trait obligations segregated.
builtin_obligations: Vec<Obligation>,
}
impl FulfillmentContext {
pub fn new() -> FulfillmentContext {
FulfillmentContext {
trait_obligations: Vec::new(),
builtin_obligations: Vec::new()
}
}
pub fn register_obligation(&mut self,
tcx: &ty::ctxt,
obligation: Obligation)
{
debug!("register_obligation({})", obligation.repr(tcx));
match tcx.lang_items.to_builtin_kind(obligation.trait_ref.def_id) {
Some(_) => {
self.builtin_obligations.push(obligation);
}
None => {
self.trait_obligations.push(obligation);
}
}
}
pub fn select_all_or_error(&mut self,
infcx: &InferCtxt,
param_env: &ty::ParameterEnvironment,
unboxed_closures: &DefIdMap<ty::UnboxedClosure>)
-> Result<(),Vec<FulfillmentError>>
{
try!(self.select_where_possible(infcx, param_env,
unboxed_closures));
// Anything left is ambiguous.
let errors: Vec<FulfillmentError> =
self.trait_obligations
.iter()
.map(|o| FulfillmentError::new((*o).clone(), Ambiguity))
.collect();
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn select_where_possible(&mut self,
infcx: &InferCtxt,
param_env: &ty::ParameterEnvironment,
unboxed_closures: &DefIdMap<ty::UnboxedClosure>)
-> Result<(),Vec<FulfillmentError>>
{
let tcx = infcx.tcx;
let selcx = SelectionContext::new(infcx, param_env,
unboxed_closures);
debug!("select_where_possible({} obligations) start",
self.trait_obligations.len());
let mut errors = Vec::new();
loop {
let count = self.trait_obligations.len();
debug!("select_where_possible({} obligations) iteration",
count);
let mut selections = Vec::new();
// First pass: walk each obligation, retaining
// only those that we cannot yet process.
self.trait_obligations.retain(|obligation| {
match selcx.select(obligation) {
Ok(None) => {
true
}
Ok(Some(s)) => {
selections.push(s);
false
}
Err(selection_err) => {
debug!("obligation: {} error: {}",
obligation.repr(tcx),
selection_err.repr(tcx));
errors.push(FulfillmentError::new(
(*obligation).clone(),
SelectionError(selection_err)));
false
}
}
});
if self.trait_obligations.len() == count {
// Nothing changed.
break;
}
// Now go through all the successful ones,
// registering any nested obligations for the future.
for selection in selections.move_iter() {
selection.map_move_nested(
|o| self.register_obligation(tcx, o));
}
}
debug!("select_where_possible({} obligations, {} errors) done",
self.trait_obligations.len(),
errors.len());
if errors.len() == 0 {
Ok(())
} else {
Err(errors)
}
}
pub fn check_builtin_bound_obligations(
&self,
infcx: &InferCtxt)
-> Result<(),Vec<FulfillmentError>>
{
let tcx = infcx.tcx;
let mut errors = Vec::new();
debug!("check_builtin_bound_obligations");
for obligation in self.builtin_obligations.iter() {
debug!("obligation={}", obligation.repr(tcx));
let def_id = obligation.trait_ref.def_id;
let bound = match tcx.lang_items.to_builtin_kind(def_id) {
Some(bound) => { bound }
None => { continue; }
};
let unskol_self_ty = obligation.self_ty();
// Skolemize the self-type so that it no longer contains
// inference variables. Note that this also replaces
// regions with 'static. You might think that this is not
// ok, because checking whether something is `Send`
// implies checking whether it is 'static: that's true,
// but in fact the region bound is fed into region
// inference separately and enforced there (and that has
// even already been done before this code executes,
// generally speaking).
let self_ty = skolemize(infcx, unskol_self_ty);
debug!("bound={} self_ty={}", bound, self_ty.repr(tcx));
if ty::type_is_error(self_ty) {
// Indicates an error that was/will-be
// reported elsewhere.
continue;
}
// Determine if builtin bound is met.
let tc = ty::type_contents(tcx, self_ty);
debug!("tc={}", tc);
let met = match bound {
ty::BoundSend => tc.is_sendable(tcx),
ty::BoundSized => tc.is_sized(tcx),
ty::BoundCopy => tc.is_copy(tcx),
ty::BoundSync => tc.is_sync(tcx),
};
if met {
continue;
}
// FIXME -- This is kind of a hack: it requently happens
// that some earlier error prevents types from being fully
// inferred, and then we get a bunch of uninteresting
// errors saying something like "<generic #0> doesn't
// implement Sized". It may even be true that we could
// just skip over all checks where the self-ty is an
// inference variable, but I was afraid that there might
// be an inference variable created, registered as an
// obligation, and then never forced by writeback, and
// hence by skipping here we'd be ignoring the fact that
// we don't KNOW the type works out. Though even that
// would probably be harmless, given that we're only
// talking about builtin traits, which are known to be
// inhabited. But in any case I just threw in this check
// for has_errors() to be sure that compilation isn't
// happening anyway. In that case, why inundate the user.
if ty::type_needs_infer(self_ty) &&
tcx.sess.has_errors()
{
debug!("skipping printout because self_ty={}",
self_ty.repr(tcx));
continue;
}
errors.push(
FulfillmentError::new(
(*obligation).clone(),
SelectionError(Unimplemented)));
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
}

View File

@ -0,0 +1,438 @@
// 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.
/*!
* Trait Resolution. See doc.rs.
*/
use middle::subst;
use middle::ty;
use middle::typeck::infer::InferCtxt;
use std::rc::Rc;
use syntax::ast;
use syntax::codemap::{Span, DUMMY_SP};
use util::nodemap::DefIdMap;
pub use self::fulfill::FulfillmentContext;
pub use self::select::SelectionContext;
pub use self::util::supertraits;
pub use self::util::transitive_bounds;
pub use self::util::Supertraits;
pub use self::util::search_trait_and_supertraits_from_bound;
mod coherence;
mod fulfill;
mod select;
mod util;
/**
* An `Obligation` represents some trait reference (e.g. `int:Eq`) for
* which the vtable must be found. The process of finding a vtable is
* called "resolving" the `Obligation`. This process consists of
* either identifying an `impl` (e.g., `impl Eq for int`) that
* provides the required vtable, or else finding a bound that is in
* scope. The eventual result is usually a `Selection` (defined below).
*/
#[deriving(Clone)]
pub struct Obligation {
pub cause: ObligationCause,
pub recursion_depth: uint,
pub trait_ref: Rc<ty::TraitRef>,
}
/**
* Why did we incur this obligation? Used for error reporting.
*/
#[deriving(Clone)]
pub struct ObligationCause {
pub span: Span,
pub code: ObligationCauseCode
}
#[deriving(Clone)]
pub enum ObligationCauseCode {
/// Not well classified or should be obvious from span.
MiscObligation,
/// In an impl of trait X for type Y, type Y must
/// also implement all supertraits of X.
ItemObligation(ast::DefId),
/// Obligation incurred due to an object cast.
ObjectCastObligation(/* Object type */ ty::t),
/// Various cases where expressions must be sized/copy/etc:
AssignmentLhsSized, // L = X implies that L is Sized
StructInitializerSized, // S { ... } must be Sized
VariableType(ast::NodeId), // Type of each variable must be Sized
RepeatVec, // [T,..n] --> T must be Copy
}
pub static DUMMY_CAUSE: ObligationCause =
ObligationCause { span: DUMMY_SP,
code: MiscObligation };
pub type Obligations = subst::VecPerParamSpace<Obligation>;
pub type Selection = Vtable<Obligation>;
#[deriving(Clone,Show)]
pub enum SelectionError {
Unimplemented,
Overflow,
OutputTypeParameterMismatch(Rc<ty::TraitRef>, ty::type_err)
}
pub struct FulfillmentError {
pub obligation: Obligation,
pub code: FulfillmentErrorCode
}
#[deriving(Clone)]
pub enum FulfillmentErrorCode {
SelectionError(SelectionError),
Ambiguity,
}
/**
* When performing resolution, it is typically the case that there
* can be one of three outcomes:
*
* - `Ok(Some(r))`: success occurred with result `r`
* - `Ok(None)`: could not definitely determine anything, usually due
* to inconclusive type inference.
* - `Err(e)`: error `e` occurred
*/
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
* a "vtable", it does not necessarily indicate dynamic dispatch at
* runtime. `Vtable` instances just tell the compiler where to find
* methods, but in generic code those methods are typically statically
* dispatched -- only when an object is constructed is a `Vtable`
* instance reified into an actual vtable.
*
* For example, the vtable may be tied to a specific impl (case A),
* or it may be relative to some bound that is in scope (case B).
*
*
* ```
* impl<T:Clone> Clone<T> for Option<T> { ... } // Impl_1
* impl<T:Clone> Clone<T> for Box<T> { ... } // Impl_2
* impl Clone for int { ... } // Impl_3
*
* fn foo<T:Clone>(concrete: Option<Box<int>>,
* param: T,
* mixed: Option<T>) {
*
* // Case A: Vtable points at a specific impl. Only possible when
* // type is concretely known. If the impl itself has bounded
* // type parameters, Vtable will carry resolutions for those as well:
* concrete.clone(); // Vtable(Impl_1, [Vtable(Impl_2, [Vtable(Impl_3)])])
*
* // Case B: Vtable must be provided by caller. This applies when
* // type is a type parameter.
* param.clone(); // VtableParam(Oblig_1)
*
* // Case C: A mix of cases A and B.
* mixed.clone(); // Vtable(Impl_1, [VtableParam(Oblig_1)])
* }
* ```
*
* ### The type parameter `N`
*
* See explanation on `VtableImpl`.
*/
#[deriving(Show,Clone)]
pub enum Vtable<N> {
/// Vtable identifying a particular impl.
VtableImpl(VtableImpl<N>),
/// Vtable automatically generated for an unboxed closure. The def
/// ID is the ID of the closure expression. This is a `VtableImpl`
/// in spirit, but the impl is generated by the compiler and does
/// not appear in the source.
VtableUnboxedClosure(ast::DefId),
/// Successful resolution to an obligation provided by the caller
/// for some type parameter.
VtableParam(VtableParam),
/// Successful resolution for a builtin trait.
VtableBuiltin,
}
/**
* Identifies a particular impl in the source, along with a set of
* substitutions from the impl's type/lifetime parameters. The
* `nested` vector corresponds to the nested obligations attached to
* the impl's type parameters.
*
* The type parameter `N` indicates the type used for "nested
* obligations" that are required by the impl. During type check, this
* is `Obligation`, as one might expect. During trans, however, this
* is `()`, because trans only requires a shallow resolution of an
* impl, and nested obligations are satisfied later.
*/
#[deriving(Clone)]
pub struct VtableImpl<N> {
pub impl_def_id: ast::DefId,
pub substs: subst::Substs,
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)]
pub struct VtableParam {
// In the above example, this would `Eq`
pub bound: Rc<ty::TraitRef>,
}
pub fn try_select_obligation(infcx: &InferCtxt,
param_env: &ty::ParameterEnvironment,
unboxed_closures: &DefIdMap<ty::UnboxedClosure>,
obligation: &Obligation)
-> SelectionResult<Selection>
{
/*!
* Attempts to select the impl/bound/etc for the obligation
* given. Returns `None` if we are unable to resolve, either
* because of ambiguity or due to insufficient inference. Note
* that selection is a shallow process and hence the result may
* contain nested obligations that must be resolved. The caller is
* responsible for ensuring that those get resolved. (But see
* `try_select_obligation_deep` below.)
*/
let selcx = select::SelectionContext::new(infcx, param_env, unboxed_closures);
selcx.select(obligation)
}
pub fn evaluate_obligation(infcx: &InferCtxt,
param_env: &ty::ParameterEnvironment,
obligation: &Obligation,
unboxed_closures: &DefIdMap<ty::UnboxedClosure>)
-> EvaluationResult
{
/*!
* Attempts to resolve the obligation given. Returns `None` if
* we are unable to resolve, either because of ambiguity or
* due to insufficient inference.
*/
let selcx = select::SelectionContext::new(infcx, param_env,
unboxed_closures);
selcx.evaluate_obligation(obligation)
}
pub fn evaluate_impl(infcx: &InferCtxt,
param_env: &ty::ParameterEnvironment,
unboxed_closures: &DefIdMap<ty::UnboxedClosure>,
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 selcx = select::SelectionContext::new(infcx, param_env, unboxed_closures);
selcx.evaluate_impl(impl_def_id, cause, self_ty)
}
pub fn select_inherent_impl(infcx: &InferCtxt,
param_env: &ty::ParameterEnvironment,
unboxed_closures: &DefIdMap<ty::UnboxedClosure>,
cause: ObligationCause,
impl_def_id: ast::DefId,
self_ty: ty::t)
-> SelectionResult<VtableImpl<Obligation>>
{
/*!
* Matches the self type of the inherent impl `impl_def_id`
* against `self_ty` and returns the resulting resolution. This
* routine may modify the surrounding type context (for example,
* it may unify variables).
*/
// This routine is only suitable for inherent impls. This is
// because it does not attempt to unify the output type parameters
// from the trait ref against the values from the obligation.
// (These things do not apply to inherent impls, for which there
// is no trait ref nor obligation.)
//
// Matching against non-inherent impls should be done with
// `try_resolve_obligation()`.
assert!(ty::impl_trait_ref(infcx.tcx, impl_def_id).is_none());
let selcx = select::SelectionContext::new(infcx, param_env,
unboxed_closures);
selcx.select_inherent_impl(impl_def_id, cause, self_ty)
}
pub fn is_orphan_impl(tcx: &ty::ctxt,
impl_def_id: ast::DefId)
-> bool
{
/*!
* True if neither the trait nor self type is local. Note that
* `impl_def_id` must refer to an impl of a trait, not an inherent
* impl.
*/
!coherence::impl_is_local(tcx, impl_def_id)
}
pub fn overlapping_impls(infcx: &InferCtxt,
impl1_def_id: ast::DefId,
impl2_def_id: ast::DefId)
-> bool
{
/*!
* True if there exist types that satisfy both of the two given impls.
*/
coherence::impl_can_satisfy(infcx, impl1_def_id, impl2_def_id) &&
coherence::impl_can_satisfy(infcx, impl2_def_id, impl1_def_id)
}
pub fn obligations_for_generics(tcx: &ty::ctxt,
cause: ObligationCause,
generics: &ty::Generics,
substs: &subst::Substs)
-> subst::VecPerParamSpace<Obligation>
{
/*!
* Given generics for an impl like:
*
* impl<A:Foo, B:Bar+Qux> ...
*
* and a substs vector like `<A=A0, B=B0>`, yields a result like
*
* [[Foo for A0, Bar for B0, Qux for B0], [], []]
*/
util::obligations_for_generics(tcx, cause, 0, generics, substs)
}
pub fn obligation_for_builtin_bound(tcx: &ty::ctxt,
cause: ObligationCause,
source_ty: ty::t,
builtin_bound: ty::BuiltinBound)
-> Obligation
{
util::obligation_for_builtin_bound(tcx, cause, builtin_bound, 0, source_ty)
}
impl Obligation {
pub fn new(cause: ObligationCause, trait_ref: Rc<ty::TraitRef>) -> Obligation {
Obligation { cause: cause,
recursion_depth: 0,
trait_ref: trait_ref }
}
pub fn misc(span: Span, trait_ref: Rc<ty::TraitRef>) -> Obligation {
Obligation::new(ObligationCause::misc(span), trait_ref)
}
pub fn self_ty(&self) -> ty::t {
self.trait_ref.self_ty()
}
}
impl ObligationCause {
pub fn new(span: Span, code: ObligationCauseCode) -> ObligationCause {
ObligationCause { span: span, code: code }
}
pub fn misc(span: Span) -> ObligationCause {
ObligationCause { span: span, code: MiscObligation }
}
}
impl<N> Vtable<N> {
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,
}
}
pub fn map_move_nested<M>(self, op: |N| -> M) -> Vtable<M> {
match self {
VtableImpl(i) => VtableImpl(i.map_move_nested(op)),
VtableUnboxedClosure(d) => VtableUnboxedClosure(d),
VtableParam(p) => VtableParam(p),
VtableBuiltin => VtableBuiltin,
}
}
}
impl<N> VtableImpl<N> {
pub fn map_nested<M>(&self,
op: |&N| -> M)
-> VtableImpl<M>
{
VtableImpl {
impl_def_id: self.impl_def_id,
substs: self.substs.clone(),
nested: self.nested.map(op)
}
}
pub fn map_move_nested<M>(self, op: |N| -> M) -> VtableImpl<M> {
let VtableImpl { impl_def_id, substs, nested } = self;
VtableImpl {
impl_def_id: impl_def_id,
substs: substs,
nested: nested.map_move(op)
}
}
}
impl EvaluationResult {
pub fn potentially_applicable(&self) -> bool {
match *self {
EvaluatedToMatch | EvaluatedToAmbiguity => true,
EvaluatedToUnmatch => false
}
}
}
impl FulfillmentError {
fn new(obligation: Obligation, code: FulfillmentErrorCode)
-> FulfillmentError
{
FulfillmentError { obligation: obligation, code: code }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,356 @@
// 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 middle::subst;
use middle::subst::{ParamSpace, Subst, Substs, VecPerParamSpace};
use middle::typeck::infer::InferCtxt;
use middle::ty;
use std::fmt;
use std::rc::Rc;
use syntax::ast;
use syntax::codemap::Span;
use util::ppaux::Repr;
use super::{Obligation, ObligationCause, VtableImpl, VtableParam};
///////////////////////////////////////////////////////////////////////////
// Supertrait iterator
pub struct Supertraits<'cx, 'tcx:'cx> {
tcx: &'cx ty::ctxt<'tcx>,
stack: Vec<SupertraitEntry>,
}
struct SupertraitEntry {
position: uint,
supertraits: Vec<Rc<ty::TraitRef>>,
}
pub fn supertraits<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
trait_ref: Rc<ty::TraitRef>)
-> Supertraits<'cx, 'tcx>
{
/*!
* Returns an iterator over the trait reference `T` and all of its
* supertrait references. May contain duplicates. In general
* the ordering is not defined.
*
* Example:
*
* ```
* trait Foo { ... }
* trait Bar : Foo { ... }
* trait Baz : Bar+Foo { ... }
* ```
*
* `supertraits(Baz)` yields `[Baz, Bar, Foo, Foo]` in some order.
*/
transitive_bounds(tcx, [trait_ref])
}
pub fn transitive_bounds<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
bounds: &[Rc<ty::TraitRef>])
-> Supertraits<'cx, 'tcx>
{
let bounds = Vec::from_fn(bounds.len(), |i| bounds[i].clone());
let entry = SupertraitEntry { position: 0, supertraits: bounds };
Supertraits { tcx: tcx, stack: vec![entry] }
}
impl<'cx, 'tcx> Supertraits<'cx, 'tcx> {
fn push(&mut self, trait_ref: &ty::TraitRef) {
let bounds = ty::bounds_for_trait_ref(self.tcx, trait_ref);
let entry = SupertraitEntry { position: 0,
supertraits: bounds.trait_bounds };
self.stack.push(entry);
}
pub fn indices(&self) -> Vec<uint> {
/*!
* Returns the path taken through the trait supertraits to
* reach the current point.
*/
self.stack.iter().map(|e| e.position).collect()
}
}
impl<'cx, 'tcx> Iterator<Rc<ty::TraitRef>> for Supertraits<'cx, 'tcx> {
fn next(&mut self) -> Option<Rc<ty::TraitRef>> {
loop {
// Extract next item from top-most stack frame, if any.
let next_trait = match self.stack.mut_last() {
None => {
// No more stack frames. Done.
return None;
}
Some(entry) => {
let p = entry.position;
if p < entry.supertraits.len() {
// Still more supertraits left in the top stack frame.
entry.position += 1;
let next_trait =
(*entry.supertraits.get(p)).clone();
Some(next_trait)
} else {
None
}
}
};
match next_trait {
Some(next_trait) => {
self.push(&*next_trait);
return Some(next_trait);
}
None => {
// Top stack frame is exhausted, pop it.
self.stack.pop();
}
}
}
}
}
// determine the `self` type, using fresh variables for all variables
// declared on the impl declaration e.g., `impl<A,B> for ~[(A,B)]`
// would return ($0, $1) where $0 and $1 are freshly instantiated type
// variables.
pub fn fresh_substs_for_impl(infcx: &InferCtxt,
span: Span,
impl_def_id: ast::DefId)
-> Substs
{
let tcx = infcx.tcx;
let impl_generics = ty::lookup_item_type(tcx, impl_def_id).generics;
infcx.fresh_substs_for_generics(span, &impl_generics)
}
impl<N> fmt::Show for VtableImpl<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VtableImpl({})", self.impl_def_id)
}
}
impl fmt::Show for VtableParam {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VtableParam(...)")
}
}
pub fn obligations_for_generics(tcx: &ty::ctxt,
cause: ObligationCause,
recursion_depth: uint,
generics: &ty::Generics,
substs: &Substs)
-> VecPerParamSpace<Obligation>
{
/*! See `super::obligations_for_generics` */
debug!("obligations_for_generics(generics={}, substs={})",
generics.repr(tcx), substs.repr(tcx));
let mut obligations = VecPerParamSpace::empty();
for def in generics.types.iter() {
push_obligations_for_param_bounds(tcx,
cause,
recursion_depth,
def.space,
def.index,
&def.bounds,
substs,
&mut obligations);
}
debug!("obligations() ==> {}", obligations.repr(tcx));
return obligations;
}
fn push_obligations_for_param_bounds(
tcx: &ty::ctxt,
cause: ObligationCause,
recursion_depth: uint,
space: subst::ParamSpace,
index: uint,
param_bounds: &ty::ParamBounds,
param_substs: &Substs,
obligations: &mut VecPerParamSpace<Obligation>)
{
let param_ty = *param_substs.types.get(space, index);
for builtin_bound in param_bounds.builtin_bounds.iter() {
obligations.push(
space,
obligation_for_builtin_bound(tcx,
cause,
builtin_bound,
recursion_depth,
param_ty));
}
for bound_trait_ref in param_bounds.trait_bounds.iter() {
let bound_trait_ref = bound_trait_ref.subst(tcx, param_substs);
obligations.push(
space,
Obligation { cause: cause,
recursion_depth: recursion_depth,
trait_ref: bound_trait_ref });
}
}
pub fn obligation_for_builtin_bound(
tcx: &ty::ctxt,
cause: ObligationCause,
builtin_bound: ty::BuiltinBound,
recursion_depth: uint,
param_ty: ty::t)
-> Obligation
{
match tcx.lang_items.from_builtin_kind(builtin_bound) {
Ok(def_id) => {
Obligation {
cause: cause,
recursion_depth: recursion_depth,
trait_ref: Rc::new(ty::TraitRef {
def_id: def_id,
substs: Substs::empty().with_self_ty(param_ty),
}),
}
}
Err(e) => {
tcx.sess.span_bug(cause.span, e.as_slice());
}
}
}
pub fn search_trait_and_supertraits_from_bound(tcx: &ty::ctxt,
caller_bound: Rc<ty::TraitRef>,
test: |ast::DefId| -> bool)
-> Option<VtableParam>
{
/*!
* Starting from a caller obligation `caller_bound` (which has
* coordinates `space`/`i` in the list of caller obligations),
* search through the trait and supertraits to find one where
* `test(d)` is true, where `d` is the def-id of the
* trait/supertrait. If any is found, return `Some(p)` where `p`
* is the path to that trait/supertrait. Else `None`.
*/
for (bound_index, bound) in
transitive_bounds(tcx, &[caller_bound]).enumerate()
{
if test(bound.def_id) {
let vtable_param = VtableParam { bound: bound };
return Some(vtable_param);
}
}
return None;
}
impl Repr for super::Obligation {
fn repr(&self, tcx: &ty::ctxt) -> String {
format!("Obligation(trait_ref={},depth={})",
self.trait_ref.repr(tcx),
self.recursion_depth)
}
}
impl<N:Repr> Repr for super::Vtable<N> {
fn repr(&self, tcx: &ty::ctxt) -> String {
match *self {
super::VtableImpl(ref v) =>
v.repr(tcx),
super::VtableUnboxedClosure(ref d) =>
format!("VtableUnboxedClosure({})",
d.repr(tcx)),
super::VtableParam(ref v) =>
format!("VtableParam({})", v.repr(tcx)),
super::VtableBuiltin =>
format!("Builtin"),
}
}
}
impl<N:Repr> Repr for super::VtableImpl<N> {
fn repr(&self, tcx: &ty::ctxt) -> String {
format!("VtableImpl(impl_def_id={}, substs={}, nested={})",
self.impl_def_id.repr(tcx),
self.substs.repr(tcx),
self.nested.repr(tcx))
}
}
impl Repr for super::VtableParam {
fn repr(&self, tcx: &ty::ctxt) -> String {
format!("VtableParam(bound={})",
self.bound.repr(tcx))
}
}
impl Repr for super::SelectionError {
fn repr(&self, tcx: &ty::ctxt) -> String {
match *self {
super::Unimplemented =>
format!("Unimplemented"),
super::Overflow =>
format!("Overflow"),
super::OutputTypeParameterMismatch(ref t, ref e) =>
format!("OutputTypeParameterMismatch({}, {})",
t.repr(tcx),
e.repr(tcx)),
}
}
}
impl Repr for super::FulfillmentError {
fn repr(&self, tcx: &ty::ctxt) -> String {
format!("FulfillmentError({},{})",
self.obligation.repr(tcx),
self.code.repr(tcx))
}
}
impl Repr for super::FulfillmentErrorCode {
fn repr(&self, tcx: &ty::ctxt) -> String {
match *self {
super::SelectionError(ref o) => o.repr(tcx),
super::Ambiguity => format!("Ambiguity")
}
}
}
impl fmt::Show for super::FulfillmentErrorCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
super::SelectionError(ref e) => write!(f, "{}", e),
super::Ambiguity => write!(f, "Ambiguity")
}
}
}
impl Repr for ty::type_err {
fn repr(&self, tcx: &ty::ctxt) -> String {
ty::type_err_to_str(tcx, self)
}
}

View File

@ -1089,7 +1089,13 @@ pub struct RegionVid {
pub enum InferTy {
TyVar(TyVid),
IntVar(IntVid),
FloatVar(FloatVid)
FloatVar(FloatVid),
SkolemizedTy(uint),
// FIXME -- once integral fallback is impl'd, we should remove
// this type. It's only needed to prevent spurious errors for
// integers whose type winds up never being constrained.
SkolemizedIntTy(uint),
}
#[deriving(Clone, Encodable, Decodable, Eq, Hash, Show)]
@ -1152,6 +1158,8 @@ impl fmt::Show for InferTy {
TyVar(ref v) => v.fmt(f),
IntVar(ref v) => v.fmt(f),
FloatVar(ref v) => v.fmt(f),
SkolemizedTy(v) => write!(f, "SkolemizedTy({})", v),
SkolemizedIntTy(v) => write!(f, "SkolemizedIntTy({})", v),
}
}
}
@ -1207,6 +1215,12 @@ impl Generics {
}
}
impl TraitRef {
pub fn self_ty(&self) -> ty::t {
self.substs.self_ty().unwrap()
}
}
/// When type checking, we use the `ParameterEnvironment` to track
/// details about the type/lifetime parameters that are in scope.
/// It primarily stores the bounds information.
@ -1235,6 +1249,14 @@ pub struct ParameterEnvironment {
/// may specify stronger requirements). This field indicates the
/// region of the callee.
pub implicit_region_bound: ty::Region,
/// Obligations that the caller must satisfy. This is basically
/// the set of bounds on the in-scope type parameters, translated
/// into Obligations.
///
/// Note: This effectively *duplicates* the `bounds` array for
/// now.
pub caller_obligations: VecPerParamSpace<traits::Obligation>,
}
impl ParameterEnvironment {
@ -1249,6 +1271,7 @@ impl ParameterEnvironment {
let method_generics = &method_ty.generics;
construct_parameter_environment(
cx,
method.span,
method_generics,
method.pe_body().id)
}
@ -1272,6 +1295,7 @@ impl ParameterEnvironment {
let method_generics = &method_ty.generics;
construct_parameter_environment(
cx,
method.span,
method_generics,
method.pe_body().id)
}
@ -1287,6 +1311,7 @@ impl ParameterEnvironment {
let fn_pty = ty::lookup_item_type(cx, fn_def_id);
construct_parameter_environment(cx,
item.span,
&fn_pty.generics,
body.id)
}
@ -1296,7 +1321,8 @@ impl ParameterEnvironment {
ast::ItemStatic(..) => {
let def_id = ast_util::local_def(id);
let pty = ty::lookup_item_type(cx, def_id);
construct_parameter_environment(cx, &pty.generics, id)
construct_parameter_environment(cx, item.span,
&pty.generics, id)
}
_ => {
cx.sess.span_bug(item.span,
@ -1328,7 +1354,14 @@ pub struct Polytype {
/// As `Polytype` but for a trait ref.
pub struct TraitDef {
/// Generic type definitions. Note that `Self` is listed in here
/// as having a single bound, the trait itself (e.g., in the trait
/// `Eq`, there is a single bound `Self : Eq`). This is so that
/// default methods get to assume that the `Self` parameters
/// implements the trait.
pub generics: Generics,
/// The "supertrait" bounds.
pub bounds: ParamBounds,
pub trait_ref: Rc<ty::TraitRef>,
}
@ -1345,6 +1378,7 @@ pub type type_cache = RefCell<DefIdMap<Polytype>>;
pub type node_type_table = RefCell<HashMap<uint,t>>;
/// Records information about each unboxed closure.
#[deriving(Clone)]
pub struct UnboxedClosure {
/// The type of the unboxed closure.
pub closure_type: ClosureTy,
@ -1352,7 +1386,7 @@ pub struct UnboxedClosure {
pub kind: UnboxedClosureKind,
}
#[deriving(PartialEq, Eq)]
#[deriving(Clone, PartialEq, Eq)]
pub enum UnboxedClosureKind {
FnUnboxedClosureKind,
FnMutUnboxedClosureKind,
@ -1523,7 +1557,7 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
&ty_enum(_, ref substs) | &ty_struct(_, ref substs) => {
flags |= sflags(substs);
}
&ty_trait(box ty::TyTrait { ref substs, ref bounds, .. }) => {
&ty_trait(box TyTrait { ref substs, ref bounds, .. }) => {
flags |= sflags(substs);
flags |= flags_for_bounds(bounds);
}
@ -2394,6 +2428,7 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
}
// Scalar and unique types are sendable, and durable
ty_infer(ty::SkolemizedIntTy(_)) |
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
ty_bare_fn(_) | ty::ty_char => {
TC::None
@ -2414,7 +2449,7 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
}
}
ty_trait(box ty::TyTrait { bounds, .. }) => {
ty_trait(box TyTrait { bounds, .. }) => {
object_contents(cx, bounds) | TC::ReachesFfiUnsafe | TC::Nonsized
}
@ -2926,6 +2961,14 @@ pub fn type_is_integral(ty: t) -> bool {
}
}
pub fn type_is_skolemized(ty: t) -> bool {
match get(ty).sty {
ty_infer(SkolemizedTy(_)) => true,
ty_infer(SkolemizedIntTy(_)) => true,
_ => false
}
}
pub fn type_is_uint(ty: t) -> bool {
match get(ty).sty {
ty_infer(IntVar(_)) | ty_uint(ast::TyU) => true,
@ -3760,6 +3803,8 @@ pub fn ty_sort_string(cx: &ctxt, t: t) -> String {
ty_infer(TyVar(_)) => "inferred type".to_string(),
ty_infer(IntVar(_)) => "integral variable".to_string(),
ty_infer(FloatVar(_)) => "floating-point variable".to_string(),
ty_infer(SkolemizedTy(_)) => "skolemized type".to_string(),
ty_infer(SkolemizedIntTy(_)) => "skolemized integral type".to_string(),
ty_param(ref p) => {
if p.space == subst::SelfSpace {
"Self".to_string()
@ -4683,7 +4728,7 @@ pub fn normalize_ty(cx: &ctxt, t: t) -> t {
struct TypeNormalizer<'a, 'tcx: 'a>(&'a ctxt<'tcx>);
impl<'a, 'tcx> TypeFolder<'tcx> for TypeNormalizer<'a, 'tcx> {
fn tcx<'a>(&'a self) -> &'a ctxt<'tcx> { let TypeNormalizer(c) = *self; c }
fn tcx(&self) -> &ctxt<'tcx> { let TypeNormalizer(c) = *self; c }
fn fold_ty(&mut self, t: ty::t) -> ty::t {
match self.tcx().normalized_cache.borrow().find_copy(&t) {
@ -4783,42 +4828,11 @@ pub fn eval_repeat_count(tcx: &ctxt, count_expr: &ast::Expr) -> uint {
pub fn each_bound_trait_and_supertraits(tcx: &ctxt,
bounds: &[Rc<TraitRef>],
f: |Rc<TraitRef>| -> bool)
-> bool {
for bound_trait_ref in bounds.iter() {
let mut supertrait_set = HashMap::new();
let mut trait_refs = Vec::new();
let mut i = 0;
// Seed the worklist with the trait from the bound
supertrait_set.insert(bound_trait_ref.def_id, ());
trait_refs.push(bound_trait_ref.clone());
// Add the given trait ty to the hash map
while i < trait_refs.len() {
debug!("each_bound_trait_and_supertraits(i={:?}, trait_ref={})",
i, trait_refs.get(i).repr(tcx));
if !f(trait_refs.get(i).clone()) {
return false;
}
// Add supertraits to supertrait_set
let trait_ref = trait_refs.get(i).clone();
let trait_def = lookup_trait_def(tcx, trait_ref.def_id);
for supertrait_ref in trait_def.bounds.trait_bounds.iter() {
let supertrait_ref = supertrait_ref.subst(tcx, &trait_ref.substs);
debug!("each_bound_trait_and_supertraits(supertrait_ref={})",
supertrait_ref.repr(tcx));
let d_id = supertrait_ref.def_id;
if !supertrait_set.contains_key(&d_id) {
// FIXME(#5527) Could have same trait multiple times
supertrait_set.insert(d_id, ());
trait_refs.push(supertrait_ref.clone());
}
}
i += 1;
-> bool
{
for bound_trait_ref in traits::transitive_bounds(tcx, bounds) {
if !f(bound_trait_ref) {
return false;
}
}
return true;
@ -5261,8 +5275,22 @@ impl Variance {
}
}
pub fn empty_parameter_environment() -> ParameterEnvironment {
/*!
* Construct a parameter environment suitable for static contexts
* or other contexts where there are no free type/lifetime
* parameters in scope.
*/
ty::ParameterEnvironment { free_substs: Substs::empty(),
bounds: VecPerParamSpace::empty(),
caller_obligations: VecPerParamSpace::empty(),
implicit_region_bound: ty::ReEmpty }
}
pub fn construct_parameter_environment(
tcx: &ctxt,
span: Span,
generics: &ty::Generics,
free_id: ast::NodeId)
-> ParameterEnvironment
@ -5321,10 +5349,14 @@ pub fn construct_parameter_environment(
free_substs.repr(tcx),
bounds.repr(tcx));
let obligations = traits::obligations_for_generics(tcx, traits::ObligationCause::misc(span),
generics, &free_substs);
return ty::ParameterEnvironment {
free_substs: free_substs,
bounds: bounds,
implicit_region_bound: ty::ReScope(free_id),
caller_obligations: obligations,
};
fn push_region_params(regions: &mut VecPerParamSpace<ty::Region>,

View File

@ -8,11 +8,38 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Generalized type folding mechanism.
/*!
* Generalized type folding mechanism. The setup is a bit convoluted
* but allows for convenient usage. Let T be an instance of some
* "foldable type" (one which implements `TypeFoldable`) and F be an
* instance of a "folder" (a type which implements `TypeFolder`). Then
* the setup is intended to be:
*
* T.fold_with(F) --calls--> F.fold_T(T) --calls--> super_fold_T(F, T)
*
* This way, when you define a new folder F, you can override
* `fold_T()` to customize the behavior, and invoke `super_fold_T()`
* to get the original behavior. Meanwhile, to actually fold
* something, you can just write `T.fold_with(F)`, which is
* convenient. (Note that `fold_with` will also transparently handle
* things like a `Vec<T>` where T is foldable and so on.)
*
* In this ideal setup, the only function that actually *does*
* anything is `super_fold_T`, which traverses the type `T`. Moreover,
* `super_fold_T` should only ever call `T.fold_with()`.
*
* In some cases, we follow a degenerate pattern where we do not have
* a `fold_T` nor `super_fold_T` method. Instead, `T.fold_with`
* traverses the structure directly. This is suboptimal because the
* behavior cannot be overriden, but it's much less work to implement.
* If you ever *do* need an override that doesn't exist, it's not hard
* to convert the degenerate pattern into the proper thing.
*/
use middle::subst;
use middle::subst::VecPerParamSpace;
use middle::ty;
use middle::traits;
use middle::typeck;
use std::rc::Rc;
use syntax::ast;
@ -97,6 +124,10 @@ pub trait TypeFolder<'tcx> {
fn fold_item_substs(&mut self, i: ty::ItemSubsts) -> ty::ItemSubsts {
super_fold_item_substs(self, i)
}
fn fold_obligation(&mut self, o: &traits::Obligation) -> traits::Obligation {
super_fold_obligation(self, o)
}
}
///////////////////////////////////////////////////////////////////////////
@ -110,6 +141,12 @@ pub trait TypeFolder<'tcx> {
// can easily refactor the folding into the TypeFolder trait as
// needed.
impl TypeFoldable for () {
fn fold_with<'tcx, F:TypeFolder<'tcx>>(&self, _: &mut F) -> () {
()
}
}
impl<T:TypeFoldable> TypeFoldable for Option<T> {
fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Option<T> {
self.as_ref().map(|t| t.fold_with(folder))
@ -296,13 +333,54 @@ impl TypeFoldable for ty::UnsizeKind {
match *self {
ty::UnsizeLength(len) => ty::UnsizeLength(len),
ty::UnsizeStruct(box ref k, n) => ty::UnsizeStruct(box k.fold_with(folder), n),
ty::UnsizeVtable(bounds, def_id, ref substs) => {
ty::UnsizeVtable(bounds.fold_with(folder), def_id, substs.fold_with(folder))
ty::UnsizeVtable(ty::TyTrait{bounds, def_id, substs: ref substs}, self_ty) => {
ty::UnsizeVtable(
ty::TyTrait {
bounds: bounds.fold_with(folder),
def_id: def_id,
substs: substs.fold_with(folder)
},
self_ty.fold_with(folder))
}
}
}
}
impl TypeFoldable for traits::Obligation {
fn fold_with<'tcx, F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::Obligation {
folder.fold_obligation(self)
}
}
impl<N:TypeFoldable> TypeFoldable for traits::VtableImpl<N> {
fn fold_with<'tcx, F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableImpl<N> {
traits::VtableImpl {
impl_def_id: self.impl_def_id,
substs: self.substs.fold_with(folder),
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,
}
}
}
impl TypeFoldable for traits::VtableParam {
fn fold_with<'tcx, F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableParam {
traits::VtableParam {
bound: self.bound.fold_with(folder),
}
}
}
///////////////////////////////////////////////////////////////////////////
// "super" routines: these are the default implementations for TypeFolder.
//
@ -482,6 +560,17 @@ pub fn super_fold_item_substs<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
}
}
pub fn super_fold_obligation<'tcx, T:TypeFolder<'tcx>>(this: &mut T,
obligation: &traits::Obligation)
-> traits::Obligation
{
traits::Obligation {
cause: obligation.cause,
recursion_depth: obligation.recursion_depth,
trait_ref: obligation.trait_ref.fold_with(this),
}
}
///////////////////////////////////////////////////////////////////////////
// Some sample folders

View File

@ -363,6 +363,7 @@ impl<'a, 'tcx> ErrorReporting for InferCtxt<'a, 'tcx> {
infer::ExprAssignable(_) => "mismatched types",
infer::RelateTraitRefs(_) => "mismatched traits",
infer::RelateSelfType(_) => "mismatched types",
infer::RelateOutputImplTypes(_) => "mismatched types",
infer::MatchExpressionArm(_, _) => "match arms have incompatible types",
infer::IfExpression(_) => "if and else have incompatible types",
};
@ -1465,7 +1466,11 @@ impl<'a, 'tcx> ErrorReportingHelpers for InferCtxt<'a, 'tcx> {
format!("traits are compatible")
}
infer::RelateSelfType(_) => {
format!("type matches impl")
format!("self type matches impl self type")
}
infer::RelateOutputImplTypes(_) => {
format!("trait type parameters matches those \
specified on the impl")
}
infer::MatchExpressionArm(_, _) => {
format!("match arms have compatible types")

View File

@ -26,6 +26,7 @@ use middle::subst::Substs;
use middle::ty::{TyVid, IntVid, FloatVid, RegionVid};
use middle::ty;
use middle::ty_fold;
use middle::ty_fold::TypeFoldable;
use middle::ty_fold::TypeFolder;
use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig;
use middle::typeck::infer::coercion::Coerce;
@ -57,6 +58,7 @@ pub mod lattice;
pub mod lub;
pub mod region_inference;
pub mod resolve;
mod skolemize;
pub mod sub;
pub mod test;
pub mod type_variable;
@ -114,9 +116,12 @@ pub enum TypeOrigin {
// Relating trait refs when resolving vtables
RelateTraitRefs(Span),
// Relating trait refs when resolving vtables
// Relating self types when resolving vtables
RelateSelfType(Span),
// Relating trait type parameters to those found in impl etc
RelateOutputImplTypes(Span),
// Computing common supertype in the arms of a match expression
MatchExpressionArm(Span, Span),
@ -262,6 +267,7 @@ pub enum RegionVariableOrigin {
BoundRegionInCoherence(ast::Name),
}
#[deriving(Show)]
pub enum fixup_err {
unresolved_int_ty(IntVid),
unresolved_float_ty(FloatVid),
@ -336,17 +342,12 @@ pub fn mk_subty(cx: &InferCtxt,
origin: TypeOrigin,
a: ty::t,
b: ty::t)
-> ures {
-> ures
{
debug!("mk_subty({} <: {})", a.repr(cx.tcx), b.repr(cx.tcx));
indent(|| {
cx.commit_if_ok(|| {
let trace = TypeTrace {
origin: origin,
values: Types(expected_found(a_is_expected, a, b))
};
cx.sub(a_is_expected, trace).tys(a, b)
})
}).to_ures()
cx.commit_if_ok(|| {
cx.sub_types(a_is_expected, origin, a, b)
})
}
pub fn can_mk_subty(cx: &InferCtxt, a: ty::t, b: ty::t) -> ures {
@ -356,8 +357,8 @@ pub fn can_mk_subty(cx: &InferCtxt, a: ty::t, b: ty::t) -> ures {
origin: Misc(codemap::DUMMY_SP),
values: Types(expected_found(true, a, b))
};
cx.sub(true, trace).tys(a, b)
}).to_ures()
cx.sub(true, trace).tys(a, b).to_ures()
})
}
pub fn can_mk_eqty(cx: &InferCtxt, a: ty::t, b: ty::t) -> ures {
@ -393,6 +394,14 @@ pub fn verify_param_bound(cx: &InferCtxt,
cx.region_vars.verify_param_bound(origin, param_ty, a, bs);
}
pub fn skolemize<T:TypeFoldable+Repr>(cx: &InferCtxt, a: T) -> T {
let mut skol = skolemize::TypeSkolemizer::new(cx);
let b = a.fold_with(&mut skol);
debug!("skol(a={}) -> {}", a.repr(cx.tcx), b.repr(cx.tcx));
b
}
pub fn mk_eqty(cx: &InferCtxt,
a_is_expected: bool,
origin: TypeOrigin,
@ -401,14 +410,8 @@ pub fn mk_eqty(cx: &InferCtxt,
-> ures
{
debug!("mk_eqty({} <: {})", a.repr(cx.tcx), b.repr(cx.tcx));
cx.commit_if_ok(|| {
let trace = TypeTrace {
origin: origin,
values: Types(expected_found(a_is_expected, a, b))
};
try!(cx.equate(a_is_expected, trace).tys(a, b));
Ok(())
})
cx.commit_if_ok(
|| cx.eq_types(a_is_expected, origin, a, b))
}
pub fn mk_sub_trait_refs(cx: &InferCtxt,
@ -416,25 +419,19 @@ pub fn mk_sub_trait_refs(cx: &InferCtxt,
origin: TypeOrigin,
a: Rc<ty::TraitRef>,
b: Rc<ty::TraitRef>)
-> ures
-> ures
{
debug!("mk_sub_trait_refs({} <: {})",
a.repr(cx.tcx), b.repr(cx.tcx));
indent(|| {
cx.commit_if_ok(|| {
let trace = TypeTrace {
origin: origin,
values: TraitRefs(expected_found(a_is_expected, a.clone(), b.clone()))
};
let suber = cx.sub(a_is_expected, trace);
suber.trait_refs(&*a, &*b)
})
}).to_ures()
cx.commit_if_ok(
|| cx.sub_trait_refs(a_is_expected, origin, a.clone(), b.clone()))
}
fn expected_found<T>(a_is_expected: bool,
a: T,
b: T) -> ty::expected_found<T> {
b: T)
-> ty::expected_found<T>
{
if a_is_expected {
ty::expected_found {expected: a, found: b}
} else {
@ -629,7 +626,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
/// Execute `f` then unroll any bindings it creates
pub fn probe<T,E>(&self, f: || -> Result<T,E>) -> Result<T,E> {
pub fn probe<R>(&self, f: || -> R) -> R {
debug!("probe()");
let snapshot = self.start_snapshot();
let r = f();
@ -643,6 +640,54 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
{
self.region_vars.add_given(sub, sup);
}
pub fn sub_types(&self,
a_is_expected: bool,
origin: TypeOrigin,
a: ty::t,
b: ty::t)
-> 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()
}
pub fn eq_types(&self,
a_is_expected: bool,
origin: TypeOrigin,
a: ty::t,
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()
}
pub fn sub_trait_refs(&self,
a_is_expected: bool,
origin: TypeOrigin,
a: Rc<ty::TraitRef>,
b: Rc<ty::TraitRef>)
-> ures
{
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()
}
}
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
@ -685,17 +730,40 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
.collect()
}
pub fn fresh_substs_for_type(&self,
span: Span,
generics: &ty::Generics)
-> subst::Substs
pub fn fresh_substs_for_generics(&self,
span: Span,
generics: &ty::Generics)
-> subst::Substs
{
/*!
* Given a set of generics defined on a type or impl, returns
* a substitution mapping each type/region parameter to a
* fresh inference variable.
*/
assert!(generics.types.len(subst::SelfSpace) == 0);
let type_params =
generics.types.map(
|_| self.next_ty_var());
let region_params =
generics.regions.map(
|d| self.next_region_var(EarlyBoundRegion(span, d.name)));
subst::Substs::new(type_params, region_params)
}
pub fn fresh_substs_for_trait(&self,
span: Span,
generics: &ty::Generics,
self_ty: ty::t)
-> subst::Substs
{
/*!
* Given a set of generics defined on a trait, returns a
* substitution mapping each output type/region parameter to a
* fresh inference variable, and mapping the self type to
* `self_ty`.
*/
assert!(generics.types.len(subst::SelfSpace) == 1);
assert!(generics.types.len(subst::FnSpace) == 0);
assert!(generics.regions.len(subst::SelfSpace) == 0);
assert!(generics.regions.len(subst::FnSpace) == 0);
@ -704,7 +772,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
let region_param_defs = generics.regions.get_slice(subst::TypeSpace);
let regions = self.region_vars_for_defs(span, region_param_defs);
let type_parameters = self.next_ty_vars(type_parameter_count);
subst::Substs::new_type(type_parameters, regions)
subst::Substs::new_trait(type_parameters, regions, self_ty)
}
pub fn fresh_bound_region(&self, binder_id: ast::NodeId) -> ty::Region {
@ -731,6 +799,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
trait_ref_to_string(self.tcx, &t)
}
pub fn contains_unbound_type_variables(&self, typ: ty::t) -> ty::t {
match resolve_type(self,
None,
typ, resolve_nested_tvar | resolve_ivar) {
Ok(new_type) => new_type,
Err(_) => typ
}
}
pub fn resolve_type_vars_if_possible(&self, typ: ty::t) -> ty::t {
match resolve_type(self,
None,
@ -907,6 +984,7 @@ impl TypeOrigin {
Misc(span) => span,
RelateTraitRefs(span) => span,
RelateSelfType(span) => span,
RelateOutputImplTypes(span) => span,
MatchExpressionArm(match_span, _) => match_span,
IfExpression(span) => span,
}
@ -929,6 +1007,9 @@ impl Repr for TypeOrigin {
RelateSelfType(a) => {
format!("RelateSelfType({})", a.repr(tcx))
}
RelateOutputImplTypes(a) => {
format!("RelateOutputImplTypes({})", a.repr(tcx))
}
MatchExpressionArm(a, b) => {
format!("MatchExpressionArm({}, {})", a.repr(tcx), b.repr(tcx))
}

View File

@ -46,7 +46,6 @@
// future). If you want to resolve everything but one type, you are
// probably better off writing `resolve_all - resolve_ivar`.
use middle::ty::{FloatVar, FloatVid, IntVar, IntVid, RegionVid, TyVar, TyVid};
use middle::ty::{IntType, UintType};
use middle::ty;
@ -54,7 +53,6 @@ use middle::ty_fold;
use middle::typeck::infer::{fixup_err, fres, InferCtxt};
use middle::typeck::infer::{unresolved_int_ty,unresolved_float_ty,unresolved_ty};
use syntax::codemap::Span;
use util::common::indent;
use util::ppaux::{Repr, ty_to_string};
pub static resolve_nested_tvar: uint = 0b0000000001;
@ -94,7 +92,7 @@ pub fn resolver<'a, 'tcx>(infcx: &'a InferCtxt<'a, 'tcx>,
}
impl<'a, 'tcx> ty_fold::TypeFolder<'tcx> for ResolveState<'a, 'tcx> {
fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> {
fn tcx(&self) -> &ty::ctxt<'tcx> {
self.infcx.tcx
}
@ -114,7 +112,8 @@ impl<'a, 'tcx> ResolveState<'a, 'tcx> {
pub fn resolve_type_chk(&mut self,
typ: ty::t)
-> fres<ty::t> {
-> fres<ty::t>
{
self.err = None;
debug!("Resolving {} (modes={:x})",
@ -126,14 +125,16 @@ impl<'a, 'tcx> ResolveState<'a, 'tcx> {
let rty = self.resolve_type(typ);
match self.err {
None => {
debug!("Resolved {} to {} (modes={:x})",
ty_to_string(self.infcx.tcx, typ),
ty_to_string(self.infcx.tcx, rty),
self.modes);
return Ok(rty);
}
Some(e) => return Err(e)
None => {
debug!("Resolved {} to {} (modes={:x})",
ty_to_string(self.infcx.tcx, typ),
ty_to_string(self.infcx.tcx, rty),
self.modes);
return Ok(rty);
}
Some(e) => {
return Err(e);
}
}
}
@ -141,7 +142,7 @@ impl<'a, 'tcx> ResolveState<'a, 'tcx> {
orig: ty::Region)
-> fres<ty::Region> {
self.err = None;
let resolved = indent(|| self.resolve_region(orig) );
let resolved = self.resolve_region(orig);
match self.err {
None => Ok(resolved),
Some(e) => Err(e)

View File

@ -0,0 +1,157 @@
// 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.
/*!
* 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.
*
* 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.
*
* 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
* form. Skolemization is only really useful as an internal detail.
*
* __An important detail concerning regions.__ The skolemizer also
* replaces *all* regions with 'static. The reason behind this is
* that, in general, we do not take region relationships into account
* when making type-overloaded decisions. This is important because of
* the design of the region inferencer, which is not based on
* unification but rather on accumulating and then solving a set of
* constraints. In contrast, the type inferencer assigns a value to
* each type variable only once, and it does so as soon as it can, so
* it is reasonable to ask what the type inferencer knows "so far".
*/
use middle::ty;
use middle::ty_fold;
use middle::ty_fold::TypeFoldable;
use middle::ty_fold::TypeFolder;
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
}
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)
}
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 => {
let index = self.skolemization_count;
self.skolemization_count += 1;
ty::mk_infer(self.tcx(), skolemizer(index))
}
}
}
}
impl<'a, 'tcx> TypeFolder<'tcx> for TypeSkolemizer<'a, 'tcx> {
fn tcx<'b>(&'b self) -> &'b ty::ctxt<'tcx> {
self.infcx.tcx
}
fn fold_region(&mut self, r: ty::Region) -> ty::Region {
match r {
ty::ReEarlyBound(..) |
ty::ReLateBound(..) => {
// leave bound regions alone
r
}
ty::ReStatic |
ty::ReFree(_) |
ty::ReScope(_) |
ty::ReInfer(_) |
ty::ReEmpty => {
// replace all free regions with 'static
ty::ReStatic
}
}
}
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)
}
ty::ty_infer(ty::IntVar(v)) => {
self.probe_unifiable(v)
}
ty::ty_infer(ty::FloatVar(v)) => {
self.probe_unifiable(v)
}
ty::ty_infer(ty::SkolemizedTy(_)) |
ty::ty_infer(ty::SkolemizedIntTy(_)) => {
self.tcx().sess.bug("Cannot skolemize a skolemized type");
}
ty::ty_open(..) => {
self.tcx().sess.bug("Cannot skolemize an open existential type");
}
ty::ty_nil |
ty::ty_bot |
ty::ty_bool |
ty::ty_char |
ty::ty_int(..) |
ty::ty_uint(..) |
ty::ty_float(..) |
ty::ty_enum(..) |
ty::ty_box(..) |
ty::ty_uniq(..) |
ty::ty_str |
ty::ty_err |
ty::ty_vec(..) |
ty::ty_ptr(..) |
ty::ty_rptr(..) |
ty::ty_bare_fn(..) |
ty::ty_closure(..) |
ty::ty_trait(..) |
ty::ty_struct(..) |
ty::ty_unboxed_closure(..) |
ty::ty_tup(..) |
ty::ty_param(..) => {
ty_fold::super_fold_ty(self, t)
}
}
}
}

View File

@ -258,6 +258,7 @@ impl<K,V> sv::SnapshotVecDelegate<VarValue<K,V>,()> for Delegate {
* relationship.
*/
pub trait SimplyUnifiable : Clone + PartialEq + Repr {
fn to_type(&self) -> ty::t;
fn to_type_err(expected_found<Self>) -> ty::type_err;
}
@ -286,6 +287,7 @@ pub trait InferCtxtMethodsForSimplyUnifiableTypes<V:SimplyUnifiable,
a_id: K,
b: V)
-> ures;
fn probe_var(&self, a_id: K) -> Option<ty::t>;
}
impl<'a,'tcx,V:SimplyUnifiable,K:UnifyKey<Option<V>>>
@ -370,6 +372,16 @@ impl<'a,'tcx,V:SimplyUnifiable,K:UnifyKey<Option<V>>>
}
}
}
fn probe_var(&self, a_id: K) -> Option<ty::t> {
let tcx = self.tcx;
let table = UnifyKey::unification_table(self);
let node_a = table.borrow_mut().get(tcx, a_id);
match node_a.value {
None => None,
Some(ref a_t) => Some(a_t.to_type())
}
}
}
///////////////////////////////////////////////////////////////////////////
@ -393,6 +405,13 @@ impl UnifyKey<Option<IntVarValue>> for ty::IntVid {
}
impl SimplyUnifiable for IntVarValue {
fn to_type(&self) -> ty::t {
match *self {
ty::IntType(i) => ty::mk_mach_int(i),
ty::UintType(i) => ty::mk_mach_uint(i),
}
}
fn to_type_err(err: expected_found<IntVarValue>) -> ty::type_err {
return ty::terr_int_mismatch(err);
}
@ -422,6 +441,10 @@ impl UnifyValue for Option<ast::FloatTy> {
}
impl SimplyUnifiable for ast::FloatTy {
fn to_type(&self) -> ty::t {
ty::mk_mach_float(*self)
}
fn to_type_err(err: expected_found<ast::FloatTy>) -> ty::type_err {
return ty::terr_float_mismatch(err);
}