Introduce the new phantomdata/phantomfn markers and integrate them

into variance inference; fix various bugs in variance inference
so that it considers the correct set of constraints; modify infer to
consider the results of variance inference for type arguments.
This commit is contained in:
Niko Matsakis 2015-02-12 05:16:02 -05:00
parent dfc5c0f1e8
commit 2594d56e32
38 changed files with 1521 additions and 502 deletions

View File

@ -16,7 +16,7 @@ use any;
use cell::{Cell, RefCell, Ref, RefMut, BorrowState};
use char::CharExt;
use iter::{Iterator, IteratorExt};
use marker::{Copy, Sized};
use marker::{Copy, PhantomData, Sized};
use mem;
use option::Option;
use option::Option::{Some, None};
@ -914,6 +914,11 @@ impl Debug for () {
f.pad("()")
}
}
impl<T> Debug for PhantomData<T> {
fn fmt(&self, f: &mut Formatter) -> Result {
f.pad("PhantomData")
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Copy + Debug> Debug for Cell<T> {

View File

@ -26,6 +26,10 @@
#![stable(feature = "rust1", since = "1.0.0")]
use clone::Clone;
use cmp;
use option::Option;
use hash::Hash;
use hash::Hasher;
/// Types able to be transferred across thread boundaries.
#[unstable(feature = "core",
@ -42,7 +46,7 @@ pub unsafe trait Send: 'static {
#[lang="send"]
#[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"]
#[cfg(not(stage0))]
pub unsafe trait Send {
pub unsafe trait Send : MarkerTrait {
// empty.
}
@ -50,7 +54,7 @@ pub unsafe trait Send {
#[stable(feature = "rust1", since = "1.0.0")]
#[lang="sized"]
#[rustc_on_unimplemented = "`{Self}` does not have a constant size known at compile-time"]
pub trait Sized {
pub trait Sized : MarkerTrait {
// Empty.
}
@ -155,7 +159,7 @@ pub trait Sized {
/// change: that second example would fail to compile if we made `Foo` non-`Copy`.
#[stable(feature = "rust1", since = "1.0.0")]
#[lang="copy"]
pub trait Copy {
pub trait Copy : MarkerTrait {
// Empty.
}
@ -208,216 +212,10 @@ pub trait Copy {
reason = "will be overhauled with new lifetime rules; see RFC 458")]
#[lang="sync"]
#[rustc_on_unimplemented = "`{Self}` cannot be shared between threads safely"]
pub unsafe trait Sync {
pub unsafe trait Sync : MarkerTrait {
// Empty
}
/// A marker type that indicates to the compiler that the instances
/// of the type itself owns instances of the type parameter `T`.
///
/// This is used to indicate that one or more instances of the type
/// `T` could be dropped when instances of the type itself is dropped,
/// though that may not be apparent from the other structure of the
/// type itself. For example, the type may hold a `*mut T`, which the
/// compiler does not automatically treat as owned.
#[unstable(feature = "core",
reason = "Newly added to deal with scoping and destructor changes")]
#[lang="phantom_data"]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct PhantomData<T: ?Sized>;
impl<T: ?Sized> Copy for PhantomData<T> {}
impl<T: ?Sized> Clone for PhantomData<T> {
fn clone(&self) -> PhantomData<T> { *self }
}
/// A marker type whose type parameter `T` is considered to be
/// covariant with respect to the type itself. This is (typically)
/// used to indicate that an instance of the type `T` is being stored
/// into memory and read from, even though that may not be apparent.
///
/// For more information about variance, refer to this Wikipedia
/// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
///
/// *Note:* It is very unusual to have to add a covariant constraint.
/// If you are not sure, you probably want to use `InvariantType`.
///
/// # Example
///
/// Given a struct `S` that includes a type parameter `T`
/// but does not actually *reference* that type parameter:
///
/// ```ignore
/// use std::mem;
///
/// struct S<T> { x: *() }
/// fn get<T>(s: &S<T>) -> T {
/// unsafe {
/// let x: *T = mem::transmute(s.x);
/// *x
/// }
/// }
/// ```
///
/// The type system would currently infer that the value of
/// the type parameter `T` is irrelevant, and hence a `S<int>` is
/// a subtype of `S<Box<int>>` (or, for that matter, `S<U>` for
/// any `U`). But this is incorrect because `get()` converts the
/// `*()` into a `*T` and reads from it. Therefore, we should include the
/// a marker field `CovariantType<T>` to inform the type checker that
/// `S<T>` is a subtype of `S<U>` if `T` is a subtype of `U`
/// (for example, `S<&'static int>` is a subtype of `S<&'a int>`
/// for some lifetime `'a`, but not the other way around).
#[unstable(feature = "core",
reason = "likely to change with new variance strategy")]
#[lang="covariant_type"]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct CovariantType<T: ?Sized>;
impl<T: ?Sized> Copy for CovariantType<T> {}
impl<T: ?Sized> Clone for CovariantType<T> {
fn clone(&self) -> CovariantType<T> { *self }
}
/// A marker type whose type parameter `T` is considered to be
/// contravariant with respect to the type itself. This is (typically)
/// used to indicate that an instance of the type `T` will be consumed
/// (but not read from), even though that may not be apparent.
///
/// For more information about variance, refer to this Wikipedia
/// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
///
/// *Note:* It is very unusual to have to add a contravariant constraint.
/// If you are not sure, you probably want to use `InvariantType`.
///
/// # Example
///
/// Given a struct `S` that includes a type parameter `T`
/// but does not actually *reference* that type parameter:
///
/// ```
/// use std::mem;
///
/// struct S<T> { x: *const () }
/// fn get<T>(s: &S<T>, v: T) {
/// unsafe {
/// let x: fn(T) = mem::transmute(s.x);
/// x(v)
/// }
/// }
/// ```
///
/// The type system would currently infer that the value of
/// the type parameter `T` is irrelevant, and hence a `S<int>` is
/// a subtype of `S<Box<int>>` (or, for that matter, `S<U>` for
/// any `U`). But this is incorrect because `get()` converts the
/// `*()` into a `fn(T)` and then passes a value of type `T` to it.
///
/// Supplying a `ContravariantType` marker would correct the
/// problem, because it would mark `S` so that `S<T>` is only a
/// subtype of `S<U>` if `U` is a subtype of `T`; given that the
/// function requires arguments of type `T`, it must also accept
/// arguments of type `U`, hence such a conversion is safe.
#[unstable(feature = "core",
reason = "likely to change with new variance strategy")]
#[lang="contravariant_type"]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct ContravariantType<T: ?Sized>;
impl<T: ?Sized> Copy for ContravariantType<T> {}
impl<T: ?Sized> Clone for ContravariantType<T> {
fn clone(&self) -> ContravariantType<T> { *self }
}
/// A marker type whose type parameter `T` is considered to be
/// invariant with respect to the type itself. This is (typically)
/// used to indicate that instances of the type `T` may be read or
/// written, even though that may not be apparent.
///
/// For more information about variance, refer to this Wikipedia
/// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
///
/// # Example
///
/// The Cell type is an example of an `InvariantType` which uses unsafe
/// code to achieve "interior" mutability:
///
/// ```
/// struct Cell<T> { value: T }
/// ```
///
/// The type system would infer that `value` is only read here
/// and never written, but in fact `Cell` uses unsafe code to achieve
/// interior mutability. In order to get correct behavior, the
/// `InvariantType` marker must be applied.
#[unstable(feature = "core",
reason = "likely to change with new variance strategy")]
#[lang="invariant_type"]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct InvariantType<T: ?Sized>;
#[unstable(feature = "core",
reason = "likely to change with new variance strategy")]
impl<T: ?Sized> Copy for InvariantType<T> {}
#[unstable(feature = "core",
reason = "likely to change with new variance strategy")]
impl<T: ?Sized> Clone for InvariantType<T> {
fn clone(&self) -> InvariantType<T> { *self }
}
/// As `CovariantType`, but for lifetime parameters. Using
/// `CovariantLifetime<'a>` indicates that it is ok to substitute
/// a *longer* lifetime for `'a` than the one you originally
/// started with (e.g., you could convert any lifetime `'foo` to
/// `'static`). You almost certainly want `ContravariantLifetime`
/// instead, or possibly `InvariantLifetime`. The only case where
/// it would be appropriate is that you have a (type-casted, and
/// hence hidden from the type system) function pointer with a
/// signature like `fn(&'a T)` (and no other uses of `'a`). In
/// this case, it is ok to substitute a larger lifetime for `'a`
/// (e.g., `fn(&'static T)`), because the function is only
/// becoming more selective in terms of what it accepts as
/// argument.
///
/// For more information about variance, refer to this Wikipedia
/// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
#[unstable(feature = "core",
reason = "likely to change with new variance strategy")]
#[lang="covariant_lifetime"]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct CovariantLifetime<'a>;
/// As `ContravariantType`, but for lifetime parameters. Using
/// `ContravariantLifetime<'a>` indicates that it is ok to
/// substitute a *shorter* lifetime for `'a` than the one you
/// originally started with (e.g., you could convert `'static` to
/// any lifetime `'foo`). This is appropriate for cases where you
/// have an unsafe pointer that is actually a pointer into some
/// memory with lifetime `'a`, and thus you want to limit the
/// lifetime of your data structure to `'a`. An example of where
/// this is used is the iterator for vectors.
///
/// For more information about variance, refer to this Wikipedia
/// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
#[unstable(feature = "core",
reason = "likely to change with new variance strategy")]
#[lang="contravariant_lifetime"]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ContravariantLifetime<'a>;
/// As `InvariantType`, but for lifetime parameters. Using
/// `InvariantLifetime<'a>` indicates that it is not ok to
/// substitute any other lifetime for `'a` besides its original
/// value. This is appropriate for cases where you have an unsafe
/// pointer that is actually a pointer into memory with lifetime `'a`,
/// and this pointer is itself stored in an inherently mutable
/// location (such as a `Cell`).
#[unstable(feature = "core",
reason = "likely to change with new variance strategy")]
#[lang="invariant_lifetime"]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct InvariantLifetime<'a>;
/// A type which is considered "not POD", meaning that it is not
/// implicitly copyable. This is typically embedded in other types to
/// ensure that they are never copied, even if they lack a destructor.
@ -435,6 +233,83 @@ pub struct NoCopy;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Managed;
macro_rules! impls{
($t: ident) => (
impl<T:?Sized, S: Hasher> Hash<S> for $t<T> {
#[inline]
fn hash(&self, _: &mut S) {
}
}
impl<T:?Sized> cmp::PartialEq for $t<T> {
fn eq(&self, _other: &$t<T>) -> bool {
true
}
}
impl<T:?Sized> cmp::Eq for $t<T> {
}
impl<T:?Sized> cmp::PartialOrd for $t<T> {
fn partial_cmp(&self, _other: &$t<T>) -> Option<cmp::Ordering> {
Option::Some(cmp::Ordering::Equal)
}
}
impl<T:?Sized> cmp::Ord for $t<T> {
fn cmp(&self, _other: &$t<T>) -> cmp::Ordering {
cmp::Ordering::Equal
}
}
impl<T:?Sized> Copy for $t<T> { }
impl<T:?Sized> Clone for $t<T> {
fn clone(&self) -> $t<T> {
$t
}
}
)
}
/// `MarkerTrait` is intended to be used as the supertrait for traits
/// that don't have any methods but instead serve just to designate
/// categories of types. An example would be the `Send` trait, which
/// indicates types that are sendable: `Send` does not itself offer
/// any methods, but instead is used to gate access to data.
///
/// FIXME. Better documentation needed here!
pub trait MarkerTrait : PhantomFn<Self> { }
impl<T:?Sized> MarkerTrait for T { }
/// `PhantomFn` is a marker trait for use with traits that do not
/// include any methods.
///
/// FIXME. Better documentation needed here!
#[lang="phantom_fn"]
pub trait PhantomFn<A:?Sized,R:?Sized=()> { }
#[cfg(stage0)] // built into the trait matching system after stage0
impl<A:?Sized, R:?Sized, U:?Sized> PhantomFn<A,R> for U { }
/// Specific to stage0. You should not be seeing these docs!
#[cfg(stage0)]
#[lang="covariant_type"] // only relevant to stage0
pub struct PhantomData<T:?Sized>;
/// `PhantomData` is a way to tell the compiler about fake fields.
/// The idea is that if the compiler encounters a `PhantomData<T>`
/// instance, it will behave *as if* an instance of the type `T` were
/// present for the purpose of various automatic analyses.
///
/// FIXME. Better documentation needed here!
#[cfg(not(stage0))]
#[lang="phantom_data"]
pub struct PhantomData<T:?Sized>;
impls! { PhantomData }
#[cfg(not(stage0))]
mod impls {
use super::{Send, Sync, Sized};

View File

@ -0,0 +1,128 @@
// 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::{BuiltinBounds};
use middle::ty::{self, Ty};
use middle::ty::TyVar;
use middle::infer::combine::*;
use middle::infer::{cres};
use middle::infer::type_variable::{BiTo};
use util::ppaux::{Repr};
use syntax::ast::{Unsafety};
pub struct Bivariate<'f, 'tcx: 'f> {
fields: CombineFields<'f, 'tcx>
}
#[allow(non_snake_case)]
pub fn Bivariate<'f, 'tcx>(cf: CombineFields<'f, 'tcx>) -> Bivariate<'f, 'tcx> {
Bivariate { fields: cf }
}
impl<'f, 'tcx> Combine<'tcx> for Bivariate<'f, 'tcx> {
fn tag(&self) -> String { "Bivariate".to_string() }
fn fields<'a>(&'a self) -> &'a CombineFields<'a, 'tcx> { &self.fields }
fn tys_with_variance(&self, v: ty::Variance, a: Ty<'tcx>, b: Ty<'tcx>)
-> cres<'tcx, Ty<'tcx>>
{
match v {
ty::Invariant => self.equate().tys(a, b),
ty::Covariant => self.tys(a, b),
ty::Contravariant => self.tys(a, b),
ty::Bivariant => self.tys(a, b),
}
}
fn regions_with_variance(&self, v: ty::Variance, a: ty::Region, b: ty::Region)
-> cres<'tcx, ty::Region>
{
match v {
ty::Invariant => self.equate().regions(a, b),
ty::Covariant => self.regions(a, b),
ty::Contravariant => self.regions(a, b),
ty::Bivariant => self.regions(a, b),
}
}
fn regions(&self, a: ty::Region, _: ty::Region) -> cres<'tcx, ty::Region> {
Ok(a)
}
fn mts(&self, a: &ty::mt<'tcx>, b: &ty::mt<'tcx>) -> cres<'tcx, ty::mt<'tcx>> {
debug!("mts({} <: {})",
a.repr(self.fields.infcx.tcx),
b.repr(self.fields.infcx.tcx));
if a.mutbl != b.mutbl { return Err(ty::terr_mutability); }
let t = try!(self.tys(a.ty, b.ty));
Ok(ty::mt { mutbl: a.mutbl, ty: t })
}
fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> {
if a != b {
Err(ty::terr_unsafety_mismatch(expected_found(self, a, b)))
} else {
Ok(a)
}
}
fn builtin_bounds(&self,
a: BuiltinBounds,
b: BuiltinBounds)
-> cres<'tcx, BuiltinBounds>
{
if a != b {
Err(ty::terr_builtin_bounds(expected_found(self, a, b)))
} else {
Ok(a)
}
}
fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
debug!("{}.tys({}, {})", self.tag(),
a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx));
if a == b { return Ok(a); }
let infcx = self.fields.infcx;
let a = infcx.type_variables.borrow().replace_if_possible(a);
let b = infcx.type_variables.borrow().replace_if_possible(b);
match (&a.sty, &b.sty) {
(&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => {
infcx.type_variables.borrow_mut().relate_vars(a_id, BiTo, b_id);
Ok(a)
}
(&ty::ty_infer(TyVar(a_id)), _) => {
try!(self.fields.instantiate(b, BiTo, a_id));
Ok(a)
}
(_, &ty::ty_infer(TyVar(b_id))) => {
try!(self.fields.instantiate(a, BiTo, b_id));
Ok(a)
}
_ => {
super_tys(self, a, b)
}
}
}
fn binders<T>(&self, a: &ty::Binder<T>, b: &ty::Binder<T>) -> cres<'tcx, ty::Binder<T>>
where T : Combineable<'tcx>
{
let a1 = ty::erase_late_bound_regions(self.tcx(), a);
let b1 = ty::erase_late_bound_regions(self.tcx(), b);
let c = try!(Combineable::combine(self, &a1, &b1));
Ok(ty::Binder(c))
}
}

View File

@ -32,6 +32,7 @@
// is also useful to track which value is the "expected" value in
// terms of error reporting.
use super::bivariate::Bivariate;
use super::equate::Equate;
use super::glb::Glb;
use super::lub::Lub;
@ -39,7 +40,7 @@ use super::sub::Sub;
use super::unify::InferCtxtMethodsForSimplyUnifiableTypes;
use super::{InferCtxt, cres};
use super::{MiscVariable, TypeTrace};
use super::type_variable::{RelationDir, EqTo, SubtypeOf, SupertypeOf};
use super::type_variable::{RelationDir, BiTo, EqTo, SubtypeOf, SupertypeOf};
use middle::subst;
use middle::subst::{ErasedRegions, NonerasedRegions, Substs};
@ -48,7 +49,7 @@ use middle::ty::{IntType, UintType};
use middle::ty::{BuiltinBounds};
use middle::ty::{self, Ty};
use middle::ty_fold;
use middle::ty_fold::{TypeFoldable};
use middle::ty_fold::{TypeFolder, TypeFoldable};
use util::ppaux::Repr;
use std::rc::Rc;
@ -58,41 +59,32 @@ use syntax::abi;
use syntax::codemap::Span;
pub trait Combine<'tcx> : Sized {
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a, 'tcx>;
fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.infcx().tcx }
fn tag(&self) -> String;
fn a_is_expected(&self) -> bool;
fn trace(&self) -> TypeTrace<'tcx>;
fn equate<'a>(&'a self) -> Equate<'a, 'tcx>;
fn sub<'a>(&'a self) -> Sub<'a, 'tcx>;
fn lub<'a>(&'a self) -> Lub<'a, 'tcx>;
fn glb<'a>(&'a self) -> Glb<'a, 'tcx>;
fn fields<'a>(&'a self) -> &'a CombineFields<'a, 'tcx>;
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a, 'tcx> { self.fields().infcx }
fn a_is_expected(&self) -> bool { self.fields().a_is_expected }
fn trace(&self) -> TypeTrace<'tcx> { self.fields().trace.clone() }
fn equate<'a>(&'a self) -> Equate<'a, 'tcx> { self.fields().equate() }
fn bivariate<'a>(&'a self) -> Bivariate<'a, 'tcx> { self.fields().bivariate() }
fn sub<'a>(&'a self) -> Sub<'a, 'tcx> { self.fields().sub() }
fn lub<'a>(&'a self) -> Lub<'a, 'tcx> { Lub(self.fields().clone()) }
fn glb<'a>(&'a self) -> Glb<'a, 'tcx> { Glb(self.fields().clone()) }
fn mts(&self, a: &ty::mt<'tcx>, b: &ty::mt<'tcx>) -> cres<'tcx, ty::mt<'tcx>>;
fn contratys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>>;
fn tys_with_variance(&self, variance: ty::Variance, a: Ty<'tcx>, b: Ty<'tcx>)
-> cres<'tcx, Ty<'tcx>>;
fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>>;
fn tps(&self,
_: subst::ParamSpace,
as_: &[Ty<'tcx>],
bs: &[Ty<'tcx>])
-> cres<'tcx, Vec<Ty<'tcx>>> {
// FIXME -- In general, we treat variance a bit wrong
// here. For historical reasons, we treat tps and Self
// as invariant. This is overly conservative.
fn regions_with_variance(&self, variance: ty::Variance, a: ty::Region, b: ty::Region)
-> cres<'tcx, ty::Region>;
if as_.len() != bs.len() {
return Err(ty::terr_ty_param_size(expected_found(self,
as_.len(),
bs.len())));
}
try!(as_.iter().zip(bs.iter())
.map(|(a, b)| self.equate().tys(*a, *b))
.collect::<cres<Vec<Ty>>>());
Ok(as_.to_vec())
}
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ty::Region>;
fn substs(&self,
item_def_id: ast::DefId,
@ -100,6 +92,11 @@ pub trait Combine<'tcx> : Sized {
b_subst: &subst::Substs<'tcx>)
-> cres<'tcx, subst::Substs<'tcx>>
{
debug!("substs: item_def_id={} a_subst={} b_subst={}",
item_def_id.repr(self.infcx().tcx),
a_subst.repr(self.infcx().tcx),
b_subst.repr(self.infcx().tcx));
let variances = if self.infcx().tcx.variance_computed.get() {
Some(ty::item_variances(self.infcx().tcx, item_def_id))
} else {
@ -119,7 +116,8 @@ pub trait Combine<'tcx> : Sized {
for &space in &subst::ParamSpace::all() {
let a_tps = a_subst.types.get_slice(space);
let b_tps = b_subst.types.get_slice(space);
let tps = try!(self.tps(space, a_tps, b_tps));
let t_variances = variances.map(|v| v.types.get_slice(space));
let tps = try!(relate_type_params(self, t_variances, a_tps, b_tps));
substs.types.replace(space, tps);
}
@ -132,20 +130,7 @@ pub trait Combine<'tcx> : Sized {
for &space in &subst::ParamSpace::all() {
let a_regions = a.get_slice(space);
let b_regions = b.get_slice(space);
let mut invariance = Vec::new();
let r_variances = match variances {
Some(variances) => {
variances.regions.get_slice(space)
}
None => {
for _ in a_regions {
invariance.push(ty::Invariant);
}
&invariance[]
}
};
let r_variances = variances.map(|v| v.regions.get_slice(space));
let regions = try!(relate_region_params(self,
r_variances,
a_regions,
@ -157,13 +142,34 @@ pub trait Combine<'tcx> : Sized {
return Ok(substs);
fn relate_type_params<'tcx, C: Combine<'tcx>>(this: &C,
variances: Option<&[ty::Variance]>,
a_tys: &[Ty<'tcx>],
b_tys: &[Ty<'tcx>])
-> cres<'tcx, Vec<Ty<'tcx>>>
{
if a_tys.len() != b_tys.len() {
return Err(ty::terr_ty_param_size(expected_found(this,
a_tys.len(),
b_tys.len())));
}
range(0, a_tys.len()).map(|i| {
let a_ty = a_tys[i];
let b_ty = b_tys[i];
let v = variances.map_or(ty::Invariant, |v| v[i]);
this.tys_with_variance(v, a_ty, b_ty)
}).collect()
}
fn relate_region_params<'tcx, C: Combine<'tcx>>(this: &C,
variances: &[ty::Variance],
variances: Option<&[ty::Variance]>,
a_rs: &[ty::Region],
b_rs: &[ty::Region])
-> cres<'tcx, Vec<ty::Region>> {
-> cres<'tcx, Vec<ty::Region>>
{
let tcx = this.infcx().tcx;
let num_region_params = variances.len();
let num_region_params = a_rs.len();
debug!("relate_region_params(\
a_rs={}, \
@ -173,22 +179,18 @@ pub trait Combine<'tcx> : Sized {
b_rs.repr(tcx),
variances.repr(tcx));
assert_eq!(num_region_params, a_rs.len());
assert_eq!(num_region_params,
variances.map_or(num_region_params,
|v| v.len()));
assert_eq!(num_region_params, b_rs.len());
let mut rs = vec!();
for i in 0..num_region_params {
(0..a_rs.len()).map(|i| {
let a_r = a_rs[i];
let b_r = b_rs[i];
let variance = variances[i];
let r = match variance {
ty::Invariant => this.equate().regions(a_r, b_r),
ty::Covariant => this.regions(a_r, b_r),
ty::Contravariant => this.contraregions(a_r, b_r),
ty::Bivariant => Ok(a_r),
};
rs.push(try!(r));
}
Ok(rs)
let variance = variances.map_or(ty::Invariant, |v| v[i]);
this.regions_with_variance(variance, a_r, b_r)
}).collect()
}
}
@ -241,7 +243,7 @@ pub trait Combine<'tcx> : Sized {
}
fn args(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
self.contratys(a, b).and_then(|t| Ok(t))
self.tys_with_variance(ty::Contravariant, a, b).and_then(|t| Ok(t))
}
fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety>;
@ -309,7 +311,7 @@ pub trait Combine<'tcx> : Sized {
b: &ty::ExistentialBounds<'tcx>)
-> cres<'tcx, ty::ExistentialBounds<'tcx>>
{
let r = try!(self.contraregions(a.region_bound, b.region_bound));
let r = try!(self.regions_with_variance(ty::Contravariant, a.region_bound, b.region_bound));
let nb = try!(self.builtin_bounds(a.builtin_bounds, b.builtin_bounds));
let pb = try!(self.projection_bounds(&a.projection_bounds, &b.projection_bounds));
Ok(ty::ExistentialBounds { region_bound: r,
@ -322,11 +324,6 @@ pub trait Combine<'tcx> : Sized {
b: ty::BuiltinBounds)
-> cres<'tcx, ty::BuiltinBounds>;
fn contraregions(&self, a: ty::Region, b: ty::Region)
-> cres<'tcx, ty::Region>;
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ty::Region>;
fn trait_refs(&self,
a: &ty::TraitRef<'tcx>,
b: &ty::TraitRef<'tcx>)
@ -540,7 +537,8 @@ pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C,
}
(&ty::ty_rptr(a_r, ref a_mt), &ty::ty_rptr(b_r, ref b_mt)) => {
let r = try!(this.contraregions(*a_r, *b_r));
let r = try!(this.regions_with_variance(ty::Contravariant, *a_r, *b_r));
// FIXME(14985) If we have mutable references to trait objects, we
// used to use covariant subtyping. I have preserved this behaviour,
// even though it is probably incorrect. So don't go down the usual
@ -644,6 +642,10 @@ impl<'f, 'tcx> CombineFields<'f, 'tcx> {
Equate((*self).clone())
}
fn bivariate(&self) -> Bivariate<'f, 'tcx> {
Bivariate((*self).clone())
}
fn sub(&self) -> Sub<'f, 'tcx> {
Sub((*self).clone())
}
@ -697,7 +699,7 @@ impl<'f, 'tcx> CombineFields<'f, 'tcx> {
EqTo => {
self.generalize(a_ty, b_vid, false)
}
SupertypeOf | SubtypeOf => {
BiTo | SupertypeOf | SubtypeOf => {
self.generalize(a_ty, b_vid, true)
}
});
@ -721,6 +723,10 @@ impl<'f, 'tcx> CombineFields<'f, 'tcx> {
// to associate causes/spans with each of the relations in
// the stack to get this right.
match dir {
BiTo => {
try!(self.bivariate().tys(a_ty, b_ty));
}
EqTo => {
try!(self.equate().tys(a_ty, b_ty));
}
@ -730,7 +736,7 @@ impl<'f, 'tcx> CombineFields<'f, 'tcx> {
}
SupertypeOf => {
try!(self.sub().contratys(a_ty, b_ty));
try!(self.sub().tys_with_variance(ty::Contravariant, a_ty, b_ty));
}
}
}

View File

@ -13,11 +13,7 @@ use middle::ty::{self, Ty};
use middle::ty::TyVar;
use middle::infer::combine::*;
use middle::infer::{cres};
use middle::infer::glb::Glb;
use middle::infer::InferCtxt;
use middle::infer::lub::Lub;
use middle::infer::sub::Sub;
use middle::infer::{TypeTrace, Subtype};
use middle::infer::{Subtype};
use middle::infer::type_variable::{EqTo};
use util::ppaux::{Repr};
@ -33,21 +29,20 @@ pub fn Equate<'f, 'tcx>(cf: CombineFields<'f, 'tcx>) -> Equate<'f, 'tcx> {
}
impl<'f, 'tcx> Combine<'tcx> for Equate<'f, 'tcx> {
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a, 'tcx> { self.fields.infcx }
fn tag(&self) -> String { "eq".to_string() }
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
fn trace(&self) -> TypeTrace<'tcx> { self.fields.trace.clone() }
fn tag(&self) -> String { "Equate".to_string() }
fn fields<'a>(&'a self) -> &'a CombineFields<'a, 'tcx> { &self.fields }
fn equate<'a>(&'a self) -> Equate<'a, 'tcx> { Equate(self.fields.clone()) }
fn sub<'a>(&'a self) -> Sub<'a, 'tcx> { Sub(self.fields.clone()) }
fn lub<'a>(&'a self) -> Lub<'a, 'tcx> { Lub(self.fields.clone()) }
fn glb<'a>(&'a self) -> Glb<'a, 'tcx> { Glb(self.fields.clone()) }
fn contratys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
fn tys_with_variance(&self, _: ty::Variance, a: Ty<'tcx>, b: Ty<'tcx>)
-> cres<'tcx, Ty<'tcx>>
{
// Once we're equating, it doesn't matter what the variance is.
self.tys(a, b)
}
fn contraregions(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ty::Region> {
fn regions_with_variance(&self, _: ty::Variance, a: ty::Region, b: ty::Region)
-> cres<'tcx, ty::Region>
{
// Once we're equating, it doesn't matter what the variance is.
self.regions(a, b)
}

View File

@ -10,12 +10,9 @@
use super::combine::*;
use super::lattice::*;
use super::equate::Equate;
use super::higher_ranked::HigherRankedRelations;
use super::lub::Lub;
use super::sub::Sub;
use super::{cres, InferCtxt};
use super::{TypeTrace, Subtype};
use super::{cres};
use super::Subtype;
use middle::ty::{BuiltinBounds};
use middle::ty::{self, Ty};
@ -34,15 +31,30 @@ pub fn Glb<'f, 'tcx>(cf: CombineFields<'f, 'tcx>) -> Glb<'f, 'tcx> {
}
impl<'f, 'tcx> Combine<'tcx> for Glb<'f, 'tcx> {
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a, 'tcx> { self.fields.infcx }
fn tag(&self) -> String { "glb".to_string() }
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
fn trace(&self) -> TypeTrace<'tcx> { self.fields.trace.clone() }
fn tag(&self) -> String { "Glb".to_string() }
fn fields<'a>(&'a self) -> &'a CombineFields<'a, 'tcx> { &self.fields }
fn equate<'a>(&'a self) -> Equate<'a, 'tcx> { Equate(self.fields.clone()) }
fn sub<'a>(&'a self) -> Sub<'a, 'tcx> { Sub(self.fields.clone()) }
fn lub<'a>(&'a self) -> Lub<'a, 'tcx> { Lub(self.fields.clone()) }
fn glb<'a>(&'a self) -> Glb<'a, 'tcx> { Glb(self.fields.clone()) }
fn tys_with_variance(&self, v: ty::Variance, a: Ty<'tcx>, b: Ty<'tcx>)
-> cres<'tcx, Ty<'tcx>>
{
match v {
ty::Invariant => self.equate().tys(a, b),
ty::Covariant => self.tys(a, b),
ty::Bivariant => self.bivariate().tys(a, b),
ty::Contravariant => self.lub().tys(a, b),
}
}
fn regions_with_variance(&self, v: ty::Variance, a: ty::Region, b: ty::Region)
-> cres<'tcx, ty::Region>
{
match v {
ty::Invariant => self.equate().regions(a, b),
ty::Covariant => self.regions(a, b),
ty::Bivariant => self.bivariate().regions(a, b),
ty::Contravariant => self.lub().regions(a, b),
}
}
fn mts(&self, a: &ty::mt<'tcx>, b: &ty::mt<'tcx>) -> cres<'tcx, ty::mt<'tcx>> {
let tcx = self.fields.infcx.tcx;
@ -75,10 +87,6 @@ impl<'f, 'tcx> Combine<'tcx> for Glb<'f, 'tcx> {
}
}
fn contratys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
self.lub().tys(a, b)
}
fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> {
match (a, b) {
(Unsafety::Normal, _) | (_, Unsafety::Normal) => Ok(Unsafety::Normal),
@ -104,11 +112,6 @@ impl<'f, 'tcx> Combine<'tcx> for Glb<'f, 'tcx> {
Ok(self.fields.infcx.region_vars.glb_regions(Subtype(self.trace()), a, b))
}
fn contraregions(&self, a: ty::Region, b: ty::Region)
-> cres<'tcx, ty::Region> {
self.lub().regions(a, b)
}
fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
super_lattice_tys(self, a, b)
}

View File

@ -9,13 +9,10 @@
// except according to those terms.
use super::combine::*;
use super::equate::Equate;
use super::glb::Glb;
use super::higher_ranked::HigherRankedRelations;
use super::lattice::*;
use super::sub::Sub;
use super::{cres, InferCtxt};
use super::{TypeTrace, Subtype};
use super::{cres};
use super::{Subtype};
use middle::ty::{BuiltinBounds};
use middle::ty::{self, Ty};
@ -34,15 +31,30 @@ pub fn Lub<'f, 'tcx>(cf: CombineFields<'f, 'tcx>) -> Lub<'f, 'tcx> {
}
impl<'f, 'tcx> Combine<'tcx> for Lub<'f, 'tcx> {
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a, 'tcx> { self.fields.infcx }
fn tag(&self) -> String { "lub".to_string() }
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
fn trace(&self) -> TypeTrace<'tcx> { self.fields.trace.clone() }
fn tag(&self) -> String { "Lub".to_string() }
fn fields<'a>(&'a self) -> &'a CombineFields<'a, 'tcx> { &self.fields }
fn equate<'a>(&'a self) -> Equate<'a, 'tcx> { Equate(self.fields.clone()) }
fn sub<'a>(&'a self) -> Sub<'a, 'tcx> { Sub(self.fields.clone()) }
fn lub<'a>(&'a self) -> Lub<'a, 'tcx> { Lub(self.fields.clone()) }
fn glb<'a>(&'a self) -> Glb<'a, 'tcx> { Glb(self.fields.clone()) }
fn tys_with_variance(&self, v: ty::Variance, a: Ty<'tcx>, b: Ty<'tcx>)
-> cres<'tcx, Ty<'tcx>>
{
match v {
ty::Invariant => self.equate().tys(a, b),
ty::Covariant => self.tys(a, b),
ty::Bivariant => self.bivariate().tys(a, b),
ty::Contravariant => self.glb().tys(a, b),
}
}
fn regions_with_variance(&self, v: ty::Variance, a: ty::Region, b: ty::Region)
-> cres<'tcx, ty::Region>
{
match v {
ty::Invariant => self.equate().regions(a, b),
ty::Covariant => self.regions(a, b),
ty::Bivariant => self.bivariate().regions(a, b),
ty::Contravariant => self.glb().regions(a, b),
}
}
fn mts(&self, a: &ty::mt<'tcx>, b: &ty::mt<'tcx>) -> cres<'tcx, ty::mt<'tcx>> {
let tcx = self.tcx();
@ -70,10 +82,6 @@ impl<'f, 'tcx> Combine<'tcx> for Lub<'f, 'tcx> {
}
}
fn contratys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
self.glb().tys(a, b)
}
fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> {
match (a, b) {
(Unsafety::Unsafe, _) | (_, Unsafety::Unsafe) => Ok(Unsafety::Unsafe),
@ -90,11 +98,6 @@ impl<'f, 'tcx> Combine<'tcx> for Lub<'f, 'tcx> {
Ok(a.intersection(b))
}
fn contraregions(&self, a: ty::Region, b: ty::Region)
-> cres<'tcx, ty::Region> {
self.glb().regions(a, b)
}
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ty::Region> {
debug!("{}.regions({}, {})",
self.tag(),

View File

@ -45,6 +45,7 @@ use self::lub::Lub;
use self::unify::{UnificationTable, InferCtxtMethodsForSimplyUnifiableTypes};
use self::error_reporting::ErrorReporting;
pub mod bivariate;
pub mod combine;
pub mod equate;
pub mod error_reporting;

View File

@ -10,12 +10,8 @@
use super::combine::*;
use super::{cres, CresCompare};
use super::equate::Equate;
use super::glb::Glb;
use super::higher_ranked::HigherRankedRelations;
use super::InferCtxt;
use super::lub::Lub;
use super::{TypeTrace, Subtype};
use super::{Subtype};
use super::type_variable::{SubtypeOf, SupertypeOf};
use middle::ty::{BuiltinBounds};
@ -37,28 +33,31 @@ pub fn Sub<'f, 'tcx>(cf: CombineFields<'f, 'tcx>) -> Sub<'f, 'tcx> {
}
impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> {
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a, 'tcx> { self.fields.infcx }
fn tag(&self) -> String { "sub".to_string() }
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
fn trace(&self) -> TypeTrace<'tcx> { self.fields.trace.clone() }
fn tag(&self) -> String { "Sub".to_string() }
fn fields<'a>(&'a self) -> &'a CombineFields<'a, 'tcx> { &self.fields }
fn equate<'a>(&'a self) -> Equate<'a, 'tcx> { Equate(self.fields.clone()) }
fn sub<'a>(&'a self) -> Sub<'a, 'tcx> { Sub(self.fields.clone()) }
fn lub<'a>(&'a self) -> Lub<'a, 'tcx> { Lub(self.fields.clone()) }
fn glb<'a>(&'a self) -> Glb<'a, 'tcx> { Glb(self.fields.clone()) }
fn contratys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
Sub(self.fields.switch_expected()).tys(b, a)
fn tys_with_variance(&self, v: ty::Variance, a: Ty<'tcx>, b: Ty<'tcx>)
-> cres<'tcx, Ty<'tcx>>
{
// Once we're equating, it doesn't matter what the variance is.
match v {
ty::Invariant => self.equate().tys(a, b),
ty::Covariant => self.tys(a, b),
ty::Bivariant => self.bivariate().tys(a, b),
ty::Contravariant => Sub(self.fields.switch_expected()).tys(b, a),
}
}
fn contraregions(&self, a: ty::Region, b: ty::Region)
-> cres<'tcx, ty::Region> {
let opp = CombineFields {
a_is_expected: !self.fields.a_is_expected,
..self.fields.clone()
};
Sub(opp).regions(b, a)
}
fn regions_with_variance(&self, v: ty::Variance, a: ty::Region, b: ty::Region)
-> cres<'tcx, ty::Region>
{
match v {
ty::Invariant => self.equate().regions(a, b),
ty::Covariant => self.regions(a, b),
ty::Bivariant => self.bivariate().regions(a, b),
ty::Contravariant => Sub(self.fields.switch_expected()).regions(b, a),
}
}
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ty::Region> {
debug!("{}.regions({}, {})",

View File

@ -48,7 +48,7 @@ type Relation = (RelationDir, ty::TyVid);
#[derive(Copy, PartialEq, Debug)]
pub enum RelationDir {
SubtypeOf, SupertypeOf, EqTo
SubtypeOf, SupertypeOf, EqTo, BiTo
}
impl RelationDir {
@ -56,7 +56,8 @@ impl RelationDir {
match self {
SubtypeOf => SupertypeOf,
SupertypeOf => SubtypeOf,
EqTo => EqTo
EqTo => EqTo,
BiTo => BiTo,
}
}
}

View File

@ -312,16 +312,9 @@ lets_do_this! {
ExchangeHeapLangItem, "exchange_heap", exchange_heap;
OwnedBoxLangItem, "owned_box", owned_box;
PhantomFnItem, "phantom_fn", phantom_fn;
PhantomDataItem, "phantom_data", phantom_data;
CovariantTypeItem, "covariant_type", covariant_type;
ContravariantTypeItem, "contravariant_type", contravariant_type;
InvariantTypeItem, "invariant_type", invariant_type;
CovariantLifetimeItem, "covariant_lifetime", covariant_lifetime;
ContravariantLifetimeItem, "contravariant_lifetime", contravariant_lifetime;
InvariantLifetimeItem, "invariant_lifetime", invariant_lifetime;
NoCopyItem, "no_copy_bound", no_copy_bound;
ManagedItem, "managed_bound", managed_bound;

View File

@ -133,6 +133,7 @@ pub enum MethodMatchedData {
/// parameters) that would have to be inferred from the impl.
#[derive(PartialEq,Eq,Debug,Clone)]
enum SelectionCandidate<'tcx> {
PhantomFnCandidate,
BuiltinCandidate(ty::BuiltinBound),
ParamCandidate(ty::PolyTraitRef<'tcx>),
ImplCandidate(ast::DefId),
@ -795,8 +796,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
stack: &TraitObligationStack<'o, 'tcx>)
-> Result<SelectionCandidateSet<'tcx>, SelectionError<'tcx>>
{
// Check for overflow.
let TraitObligationStack { obligation, .. } = *stack;
let mut candidates = SelectionCandidateSet {
@ -804,6 +803,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ambiguous: false
};
// Check for the `PhantomFn` trait. This is really just a special annotation that
// *always* be considered to match, no matter what the type parameters etc.
if self.tcx().lang_items.phantom_fn() == Some(obligation.predicate.def_id()) {
candidates.vec.push(PhantomFnCandidate);
return Ok(candidates);
}
// Other bounds. Consider both in-scope bounds from fn decl
// and applicable impls. There is a certain set of precedence rules here.
@ -1673,6 +1679,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
try!(self.confirm_builtin_candidate(obligation, builtin_bound))))
}
PhantomFnCandidate |
ErrorCandidate => {
Ok(VtableBuiltin(VtableBuiltinData { nested: VecPerParamSpace::empty() }))
}
@ -2339,6 +2346,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
impl<'tcx> Repr<'tcx> for SelectionCandidate<'tcx> {
fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
match *self {
PhantomFnCandidate => format!("PhantomFnCandidate"),
ErrorCandidate => format!("ErrorCandidate"),
BuiltinCandidate(b) => format!("BuiltinCandidate({:?})", b),
ParamCandidate(ref a) => format!("ParamCandidate({})", a.repr(tcx)),

View File

@ -0,0 +1,61 @@
// Copyright 2015 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::{self};
use std::collections::HashSet;
use std::rc::Rc;
pub fn identify_constrained_type_params<'tcx>(_tcx: &ty::ctxt<'tcx>,
predicates: &[ty::Predicate<'tcx>],
impl_trait_ref: Option<Rc<ty::TraitRef<'tcx>>>,
input_parameters: &mut HashSet<ty::ParamTy>)
{
loop {
let num_inputs = input_parameters.len();
let projection_predicates =
predicates.iter()
.filter_map(|predicate| {
match *predicate {
// Ignore higher-ranked binders. For the purposes
// of this check, they don't matter because they
// only affect named regions, and we're just
// concerned about type parameters here.
ty::Predicate::Projection(ref data) => Some(data.0.clone()),
_ => None,
}
});
for projection in projection_predicates {
// Special case: watch out for some kind of sneaky attempt
// to project out an associated type defined by this very trait.
if Some(projection.projection_ty.trait_ref.clone()) == impl_trait_ref {
continue;
}
let relies_only_on_inputs =
projection.projection_ty.trait_ref.input_types()
.iter()
.flat_map(|t| t.walk())
.filter_map(|t| t.as_opt_param_ty())
.all(|t| input_parameters.contains(&t));
if relies_only_on_inputs {
input_parameters.extend(
projection.ty.walk().filter_map(|t| t.as_opt_param_ty()));
}
}
if input_parameters.len() == num_inputs {
break;
}
}
}

View File

@ -187,6 +187,22 @@
//! and the definition-site variance of the [corresponding] type parameter
//! of a class `C` is `V1`, then the variance of `X` in the type expression
//! `C<E>` is `V3 = V1.xform(V2)`.
//!
//! ### Constraints
//!
//! If I have a struct or enum with where clauses:
//!
//! struct Foo<T:Bar> { ... }
//!
//! you might wonder whether the variance of `T` with respect to `Bar`
//! affects the variance `T` with respect to `Foo`. I claim no. The
//! reason: assume that `T` is invariant w/r/t `Bar` but covariant w/r/t
//! `Foo`. And then we have a `Foo<X>` that is upcast to `Foo<Y>`, where
//! `X <: Y`. However, while `X : Bar`, `Y : Bar` does not hold. In that
//! case, the upcast will be illegal, but not because of a variance
//! failure, but rather because the target type `Foo<Y>` is itself just
//! not well-formed. Basically we get to assume well-formedness of all
//! types involved before considering variance.
use self::VarianceTerm::*;
use self::ParamKind::*;
@ -199,7 +215,6 @@ use middle::subst::{ParamSpace, FnSpace, TypeSpace, SelfSpace, VecPerParamSpace}
use middle::ty::{self, Ty};
use std::fmt;
use std::rc::Rc;
use std::iter::repeat;
use syntax::ast;
use syntax::ast_map;
use syntax::ast_util;
@ -258,6 +273,11 @@ struct TermsContext<'a, 'tcx: 'a> {
empty_variances: Rc<ty::ItemVariances>,
// For marker types, UnsafeCell, and other lang items where
// variance is hardcoded, records the item-id and the hardcoded
// variance.
lang_items: Vec<(ast::NodeId, Vec<ty::Variance>)>,
// Maps from the node id of a type/generic parameter to the
// corresponding inferred index.
inferred_map: NodeMap<InferredIndex>,
@ -269,7 +289,7 @@ struct TermsContext<'a, 'tcx: 'a> {
#[derive(Copy, Debug, PartialEq)]
enum ParamKind {
TypeParam,
RegionParam
RegionParam,
}
struct InferredInfo<'a> {
@ -279,6 +299,11 @@ struct InferredInfo<'a> {
index: uint,
param_id: ast::NodeId,
term: VarianceTermPtr<'a>,
// Initial value to use for this parameter when inferring
// variance. For most parameters, this is Bivariant. But for lang
// items and input type parameters on traits, it is different.
initial_variance: ty::Variance,
}
fn determine_parameters_to_be_inferred<'a, 'tcx>(tcx: &'a ty::ctxt<'tcx>,
@ -291,6 +316,8 @@ fn determine_parameters_to_be_inferred<'a, 'tcx>(tcx: &'a ty::ctxt<'tcx>,
inferred_map: NodeMap(),
inferred_infos: Vec::new(),
lang_items: lang_items(tcx),
// cache and share the variance struct used for items with
// no type/region parameters
empty_variances: Rc::new(ty::ItemVariances {
@ -304,7 +331,68 @@ fn determine_parameters_to_be_inferred<'a, 'tcx>(tcx: &'a ty::ctxt<'tcx>,
terms_cx
}
fn lang_items(tcx: &ty::ctxt) -> Vec<(ast::NodeId,Vec<ty::Variance>)> {
let all = vec![
(tcx.lang_items.phantom_fn(), vec![ty::Contravariant, ty::Covariant]),
(tcx.lang_items.phantom_data(), vec![ty::Covariant]),
(tcx.lang_items.unsafe_cell_type(), vec![ty::Invariant])];
all.into_iter()
.filter(|&(ref d,_)| d.is_some())
.filter(|&(ref d,_)| d.as_ref().unwrap().krate == ast::LOCAL_CRATE)
.map(|(d, v)| (d.unwrap().node, v))
.collect()
}
impl<'a, 'tcx> TermsContext<'a, 'tcx> {
fn add_inferreds_for_item(&mut self,
item_id: ast::NodeId,
has_self: bool,
generics: &ast::Generics)
{
/*!
* Add "inferreds" for the generic parameters declared on this
* item. This has a lot of annoying parameters because we are
* trying to drive this from the AST, rather than the
* ty::Generics, so that we can get span info -- but this
* means we must accommodate syntactic distinctions.
*/
// NB: In the code below for writing the results back into the
// tcx, we rely on the fact that all inferreds for a particular
// item are assigned continuous indices.
let inferreds_on_entry = self.num_inferred();
if has_self {
self.add_inferred(item_id, TypeParam, SelfSpace, 0, item_id);
}
for (i, p) in generics.lifetimes.iter().enumerate() {
let id = p.lifetime.id;
self.add_inferred(item_id, RegionParam, TypeSpace, i, id);
}
for (i, p) in generics.ty_params.iter().enumerate() {
self.add_inferred(item_id, TypeParam, TypeSpace, i, p.id);
}
// If this item has no type or lifetime parameters,
// then there are no variances to infer, so just
// insert an empty entry into the variance map.
// Arguably we could just leave the map empty in this
// case but it seems cleaner to be able to distinguish
// "invalid item id" from "item id with no
// parameters".
if self.num_inferred() == inferreds_on_entry {
let newly_added =
self.tcx.item_variance_map.borrow_mut().insert(
ast_util::local_def(item_id),
self.empty_variances.clone()).is_none();
assert!(newly_added);
}
}
fn add_inferred(&mut self,
item_id: ast::NodeId,
kind: ParamKind,
@ -313,21 +401,48 @@ impl<'a, 'tcx> TermsContext<'a, 'tcx> {
param_id: ast::NodeId) {
let inf_index = InferredIndex(self.inferred_infos.len());
let term = self.arena.alloc(InferredTerm(inf_index));
let initial_variance = self.pick_initial_variance(item_id, space, index);
self.inferred_infos.push(InferredInfo { item_id: item_id,
kind: kind,
space: space,
index: index,
param_id: param_id,
term: term });
term: term,
initial_variance: initial_variance });
let newly_added = self.inferred_map.insert(param_id, inf_index).is_none();
assert!(newly_added);
debug!("add_inferred(item_id={}, \
debug!("add_inferred(item_path={}, \
item_id={}, \
kind={:?}, \
space={:?}, \
index={}, \
param_id={},
inf_index={:?})",
item_id, kind, index, param_id, inf_index);
param_id={}, \
inf_index={:?}, \
initial_variance={:?})",
ty::item_path_str(self.tcx, ast_util::local_def(item_id)),
item_id, kind, space, index, param_id, inf_index,
initial_variance);
}
fn pick_initial_variance(&self,
item_id: ast::NodeId,
space: ParamSpace,
index: uint)
-> ty::Variance
{
match space {
SelfSpace | FnSpace => {
ty::Bivariant
}
TypeSpace => {
match self.lang_items.iter().find(|&&(n, _)| n == item_id) {
Some(&(_, ref variances)) => variances[index],
None => ty::Bivariant
}
}
}
}
fn num_inferred(&self) -> uint {
@ -339,44 +454,13 @@ impl<'a, 'tcx, 'v> Visitor<'v> for TermsContext<'a, 'tcx> {
fn visit_item(&mut self, item: &ast::Item) {
debug!("add_inferreds for item {}", item.repr(self.tcx));
let inferreds_on_entry = self.num_inferred();
// NB: In the code below for writing the results back into the
// tcx, we rely on the fact that all inferreds for a particular
// item are assigned continuous indices.
match item.node {
ast::ItemTrait(..) => {
self.add_inferred(item.id, TypeParam, SelfSpace, 0, item.id);
}
_ => { }
}
match item.node {
ast::ItemEnum(_, ref generics) |
ast::ItemStruct(_, ref generics) |
ast::ItemStruct(_, ref generics) => {
self.add_inferreds_for_item(item.id, false, generics);
}
ast::ItemTrait(_, ref generics, _, _) => {
for (i, p) in generics.lifetimes.iter().enumerate() {
let id = p.lifetime.id;
self.add_inferred(item.id, RegionParam, TypeSpace, i, id);
}
for (i, p) in generics.ty_params.iter().enumerate() {
self.add_inferred(item.id, TypeParam, TypeSpace, i, p.id);
}
// If this item has no type or lifetime parameters,
// then there are no variances to infer, so just
// insert an empty entry into the variance map.
// Arguably we could just leave the map empty in this
// case but it seems cleaner to be able to distinguish
// "invalid item id" from "item id with no
// parameters".
if self.num_inferred() == inferreds_on_entry {
let newly_added = self.tcx.item_variance_map.borrow_mut().insert(
ast_util::local_def(item.id),
self.empty_variances.clone()).is_none();
assert!(newly_added);
}
self.add_inferreds_for_item(item.id, true, generics);
visit::walk_item(self, item);
}
@ -404,16 +488,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for TermsContext<'a, 'tcx> {
struct ConstraintContext<'a, 'tcx: 'a> {
terms_cx: TermsContext<'a, 'tcx>,
// These are the def-id of the std::marker::InvariantType,
// std::marker::InvariantLifetime, and so on. The arrays
// are indexed by the `ParamKind` (type, lifetime, self). Note
// that there are no marker types for self, so the entries for
// self are always None.
invariant_lang_items: [Option<ast::DefId>; 2],
covariant_lang_items: [Option<ast::DefId>; 2],
contravariant_lang_items: [Option<ast::DefId>; 2],
unsafe_cell_lang_item: Option<ast::DefId>,
// These are pointers to common `ConstantTerm` instances
covariant: VarianceTermPtr<'a>,
contravariant: VarianceTermPtr<'a>,
@ -433,40 +507,14 @@ struct Constraint<'a> {
fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>,
krate: &ast::Crate)
-> ConstraintContext<'a, 'tcx> {
let mut invariant_lang_items = [None; 2];
let mut covariant_lang_items = [None; 2];
let mut contravariant_lang_items = [None; 2];
covariant_lang_items[TypeParam as uint] =
terms_cx.tcx.lang_items.covariant_type();
covariant_lang_items[RegionParam as uint] =
terms_cx.tcx.lang_items.covariant_lifetime();
contravariant_lang_items[TypeParam as uint] =
terms_cx.tcx.lang_items.contravariant_type();
contravariant_lang_items[RegionParam as uint] =
terms_cx.tcx.lang_items.contravariant_lifetime();
invariant_lang_items[TypeParam as uint] =
terms_cx.tcx.lang_items.invariant_type();
invariant_lang_items[RegionParam as uint] =
terms_cx.tcx.lang_items.invariant_lifetime();
let unsafe_cell_lang_item = terms_cx.tcx.lang_items.unsafe_cell_type();
-> ConstraintContext<'a, 'tcx>
{
let covariant = terms_cx.arena.alloc(ConstantTerm(ty::Covariant));
let contravariant = terms_cx.arena.alloc(ConstantTerm(ty::Contravariant));
let invariant = terms_cx.arena.alloc(ConstantTerm(ty::Invariant));
let bivariant = terms_cx.arena.alloc(ConstantTerm(ty::Bivariant));
let mut constraint_cx = ConstraintContext {
terms_cx: terms_cx,
invariant_lang_items: invariant_lang_items,
covariant_lang_items: covariant_lang_items,
contravariant_lang_items: contravariant_lang_items,
unsafe_cell_lang_item: unsafe_cell_lang_item,
covariant: covariant,
contravariant: contravariant,
invariant: invariant,
@ -487,7 +535,13 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ConstraintContext<'a, 'tcx> {
match item.node {
ast::ItemEnum(ref enum_definition, _) => {
let generics = &ty::lookup_item_type(tcx, did).generics;
let scheme = ty::lookup_item_type(tcx, did);
// Not entirely obvious: constraints on structs/enums do not
// affect the variance of their type parameters. See discussion
// in comment at top of module.
//
// self.add_constraints_from_generics(&scheme.generics);
// Hack: If we directly call `ty::enum_variants`, it
// annoyingly takes it upon itself to run off and
@ -505,29 +559,48 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ConstraintContext<'a, 'tcx> {
&**ast_variant,
/*discriminant*/ 0);
for arg_ty in &variant.args {
self.add_constraints_from_ty(generics, *arg_ty, self.covariant);
self.add_constraints_from_ty(&scheme.generics, *arg_ty, self.covariant);
}
}
}
ast::ItemStruct(..) => {
let generics = &ty::lookup_item_type(tcx, did).generics;
let scheme = ty::lookup_item_type(tcx, did);
// Not entirely obvious: constraints on structs/enums do not
// affect the variance of their type parameters. See discussion
// in comment at top of module.
//
// self.add_constraints_from_generics(&scheme.generics);
let struct_fields = ty::lookup_struct_fields(tcx, did);
for field_info in &struct_fields {
assert_eq!(field_info.id.krate, ast::LOCAL_CRATE);
let field_ty = ty::node_id_to_type(tcx, field_info.id.node);
self.add_constraints_from_ty(generics, field_ty, self.covariant);
self.add_constraints_from_ty(&scheme.generics, field_ty, self.covariant);
}
}
ast::ItemTrait(..) => {
let trait_def = ty::lookup_trait_def(tcx, did);
let predicates = ty::predicates(tcx, ty::mk_self_type(tcx), &trait_def.bounds);
self.add_constraints_from_predicates(&trait_def.generics,
&predicates[],
self.covariant);
let trait_items = ty::trait_items(tcx, did);
for trait_item in &*trait_items {
match *trait_item {
ty::MethodTraitItem(ref method) => {
self.add_constraints_from_sig(&method.generics,
&method.fty.sig,
self.covariant);
self.add_constraints_from_predicates(
&method.generics,
method.predicates.predicates.get_slice(FnSpace),
self.contravariant);
self.add_constraints_from_sig(
&method.generics,
&method.fty.sig,
self.covariant);
}
ty::TypeTraitItem(_) => {}
}
@ -544,9 +617,10 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ConstraintContext<'a, 'tcx> {
ast::ItemTy(..) |
ast::ItemImpl(..) |
ast::ItemMac(..) => {
visit::walk_item(self, item);
}
}
visit::walk_item(self, item);
}
}
@ -648,15 +722,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
-> VarianceTermPtr<'a> {
assert_eq!(param_def_id.krate, item_def_id.krate);
if self.invariant_lang_items[kind as uint] == Some(item_def_id) {
self.invariant
} else if self.covariant_lang_items[kind as uint] == Some(item_def_id) {
self.covariant
} else if self.contravariant_lang_items[kind as uint] == Some(item_def_id) {
self.contravariant
} else if kind == TypeParam && Some(item_def_id) == self.unsafe_cell_lang_item {
self.invariant
} else if param_def_id.krate == ast::LOCAL_CRATE {
if param_def_id.krate == ast::LOCAL_CRATE {
// Parameter on an item defined within current crate:
// variance not yet inferred, so return a symbolic
// variance.
@ -724,6 +790,25 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
}
}
fn add_constraints_from_trait_ref(&mut self,
generics: &ty::Generics<'tcx>,
trait_ref: &ty::TraitRef<'tcx>,
variance: VarianceTermPtr<'a>) {
debug!("add_constraints_from_trait_ref: trait_ref={} variance={:?}",
trait_ref.repr(self.tcx()),
variance);
let trait_def = ty::lookup_trait_def(self.tcx(), trait_ref.def_id);
self.add_constraints_from_substs(
generics,
trait_ref.def_id,
trait_def.generics.types.as_slice(),
trait_def.generics.regions.as_slice(),
trait_ref.substs,
variance);
}
/// Adds constraints appropriate for an instance of `ty` appearing
/// in a context with the generics defined in `generics` and
/// ambient variance `variance`
@ -731,7 +816,9 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
generics: &ty::Generics<'tcx>,
ty: Ty<'tcx>,
variance: VarianceTermPtr<'a>) {
debug!("add_constraints_from_ty(ty={})", ty.repr(self.tcx()));
debug!("add_constraints_from_ty(ty={}, variance={:?})",
ty.repr(self.tcx()),
variance);
match ty.sty {
ty::ty_bool |
@ -754,6 +841,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
self.add_constraints_from_ty(generics, typ, variance);
}
ty::ty_ptr(ref mt) => {
self.add_constraints_from_mt(generics, mt, variance);
}
@ -797,27 +885,16 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
}
ty::ty_trait(ref data) => {
let trait_ref = data.principal_trait_ref_with_self_ty(self.tcx(),
self.tcx().types.err);
let trait_def = ty::lookup_trait_def(self.tcx(), trait_ref.def_id());
// Traits never declare region parameters in the self
// space nor anything in the fn space.
assert!(trait_def.generics.regions.is_empty_in(subst::SelfSpace));
assert!(trait_def.generics.types.is_empty_in(subst::FnSpace));
assert!(trait_def.generics.regions.is_empty_in(subst::FnSpace));
let poly_trait_ref =
data.principal_trait_ref_with_self_ty(self.tcx(),
self.tcx().types.err);
// The type `Foo<T+'a>` is contravariant w/r/t `'a`:
let contra = self.contravariant(variance);
self.add_constraints_from_region(generics, data.bounds.region_bound, contra);
self.add_constraints_from_substs(
generics,
trait_ref.def_id(),
trait_def.generics.types.get_slice(subst::TypeSpace),
trait_def.generics.regions.get_slice(subst::TypeSpace),
trait_ref.substs(),
variance);
// Ignore the SelfSpace, it is erased.
self.add_constraints_from_trait_ref(generics, &*poly_trait_ref.0, variance);
let projections = data.projection_bounds_with_self_ty(self.tcx(),
self.tcx().types.err);
@ -845,7 +922,12 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
self.add_constraints_from_sig(generics, sig, variance);
}
ty::ty_infer(..) | ty::ty_err => {
ty::ty_err => {
// we encounter this when walking the trait references for object
// types, where we use ty_err as the Self type
}
ty::ty_infer(..) => {
self.tcx().sess.bug(
&format!("unexpected type encountered in \
variance inference: {}",
@ -864,7 +946,10 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
region_param_defs: &[ty::RegionParameterDef],
substs: &subst::Substs<'tcx>,
variance: VarianceTermPtr<'a>) {
debug!("add_constraints_from_substs(def_id={:?})", def_id);
debug!("add_constraints_from_substs(def_id={}, substs={}, variance={:?})",
def_id.repr(self.tcx()),
substs.repr(self.tcx()),
variance);
for p in type_param_defs {
let variance_decl =
@ -872,6 +957,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
p.space, p.index as uint);
let variance_i = self.xform(variance, variance_decl);
let substs_ty = *substs.types.get(p.space, p.index as uint);
debug!("add_constraints_from_substs: variance_decl={:?} variance_i={:?}",
variance_decl, variance_i);
self.add_constraints_from_ty(generics, substs_ty, variance_i);
}
@ -885,6 +972,51 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
}
}
fn add_constraints_from_predicates(&mut self,
generics: &ty::Generics<'tcx>,
predicates: &[ty::Predicate<'tcx>],
variance: VarianceTermPtr<'a>) {
debug!("add_constraints_from_generics({})",
generics.repr(self.tcx()));
for predicate in predicates.iter() {
match *predicate {
ty::Predicate::Trait(ty::Binder(ref data)) => {
self.add_constraints_from_trait_ref(generics, &*data.trait_ref, variance);
}
ty::Predicate::Equate(ty::Binder(ref data)) => {
self.add_constraints_from_ty(generics, data.0, variance);
self.add_constraints_from_ty(generics, data.1, variance);
}
ty::Predicate::TypeOutlives(ty::Binder(ref data)) => {
self.add_constraints_from_ty(generics, data.0, variance);
let variance_r = self.xform(variance, self.contravariant);
self.add_constraints_from_region(generics, data.1, variance_r);
}
ty::Predicate::RegionOutlives(ty::Binder(ref data)) => {
// `'a : 'b` is still true if 'a gets bigger
self.add_constraints_from_region(generics, data.0, variance);
// `'a : 'b` is still true if 'b gets smaller
let variance_r = self.xform(variance, self.contravariant);
self.add_constraints_from_region(generics, data.1, variance_r);
}
ty::Predicate::Projection(ty::Binder(ref data)) => {
self.add_constraints_from_trait_ref(generics,
&*data.projection_ty.trait_ref,
variance);
self.add_constraints_from_ty(generics, data.ty, self.invariant);
}
}
}
}
/// Adds constraints appropriate for a function with signature
/// `sig` appearing in a context with ambient variance `variance`
fn add_constraints_from_sig(&mut self,
@ -969,7 +1101,12 @@ struct SolveContext<'a, 'tcx: 'a> {
fn solve_constraints(constraints_cx: ConstraintContext) {
let ConstraintContext { terms_cx, constraints, .. } = constraints_cx;
let solutions: Vec<_> = repeat(ty::Bivariant).take(terms_cx.num_inferred()).collect();
let solutions =
terms_cx.inferred_infos.iter()
.map(|ii| ii.initial_variance)
.collect();
let mut solutions_cx = SolveContext {
terms_cx: terms_cx,
constraints: constraints,
@ -1034,20 +1171,16 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> {
let mut types = VecPerParamSpace::empty();
let mut regions = VecPerParamSpace::empty();
while index < num_inferred &&
inferred_infos[index].item_id == item_id {
while index < num_inferred && inferred_infos[index].item_id == item_id {
let info = &inferred_infos[index];
let variance = solutions[index];
debug!("Index {} Info {} / {:?} / {:?} Variance {:?}",
index, info.index, info.kind, info.space, variance);
match info.kind {
TypeParam => {
types.push(info.space, variance);
}
RegionParam => {
regions.push(info.space, variance);
}
TypeParam => { types.push(info.space, variance); }
RegionParam => { regions.push(info.space, variance); }
}
index += 1;
}
@ -1144,3 +1277,4 @@ fn glb(v1: ty::Variance, v2: ty::Variance) -> ty::Variance {
(x, ty::Bivariant) | (ty::Bivariant, x) => x,
}
}

View File

@ -0,0 +1,31 @@
// Copyright 2015 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.
#![allow(dead_code)]
trait Get<T> : 'static {
fn get(&self, t: T);
}
fn get_min_from_max<'min, 'max>(v: Box<Get<&'max i32>>)
-> Box<Get<&'min i32>>
where 'max : 'min
{
v //~ ERROR mismatched types
}
fn get_max_from_min<'min, 'max, G>(v: Box<Get<&'min i32>>)
-> Box<Get<&'max i32>>
where 'max : 'min
{
v
}
fn main() { }

View File

@ -0,0 +1,31 @@
// Copyright 2015 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.
#![allow(dead_code)]
trait Get<T> {
fn get(&self, t: T);
}
fn get_min_from_max<'min, 'max, G>()
where 'max : 'min, G : Get<&'max i32>
{
impls_get::<G,&'min i32>() //~ ERROR mismatched types
}
fn get_max_from_min<'min, 'max, G>()
where 'max : 'min, G : Get<&'min i32>
{
impls_get::<G,&'max i32>()
}
fn impls_get<G,T>() where G : Get<T> { }
fn main() { }

View File

@ -0,0 +1,31 @@
// Copyright 2015 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.
#![allow(dead_code)]
trait Get {
fn get(&self);
}
fn get_min_from_max<'min, 'max, G>()
where 'max : 'min, G : 'max, &'max G : Get
{
impls_get::<&'min G>(); //~ ERROR mismatched types
}
fn get_max_from_min<'min, 'max, G>()
where 'max : 'min, G : 'max, &'min G : Get
{
impls_get::<&'max G>();
}
fn impls_get<G>() where G : Get { }
fn main() { }

View File

@ -0,0 +1,31 @@
// Copyright 2015 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.
#![allow(dead_code)]
trait Get<T> : 'static {
fn get(&self) -> T;
}
fn get_min_from_max<'min, 'max>(v: Box<Get<&'max i32>>)
-> Box<Get<&'min i32>>
where 'max : 'min
{
v
}
fn get_max_from_min<'min, 'max, G>(v: Box<Get<&'min i32>>)
-> Box<Get<&'max i32>>
where 'max : 'min
{
v //~ ERROR mismatched types
}
fn main() { }

View File

@ -0,0 +1,31 @@
// Copyright 2015 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.
#![allow(dead_code)]
trait Get<T> {
fn get(&self) -> T;
}
fn get_min_from_max<'min, 'max, G>()
where 'max : 'min, G : Get<&'max i32>
{
impls_get::<G,&'min i32>()
}
fn get_max_from_min<'min, 'max, G>()
where 'max : 'min, G : Get<&'min i32>
{
impls_get::<G,&'max i32>() //~ ERROR mismatched types
}
fn impls_get<G,T>() where G : Get<T> { }
fn main() { }

View File

@ -0,0 +1,31 @@
// Copyright 2015 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.
#![allow(dead_code)]
trait Get {
fn get() -> Self;
}
fn get_min_from_max<'min, 'max, G>()
where 'max : 'min, G : 'max, &'max G : Get
{
impls_get::<&'min G>();
}
fn get_max_from_min<'min, 'max, G>()
where 'max : 'min, G : 'max, &'min G : Get
{
impls_get::<&'max G>(); //~ ERROR mismatched types
}
fn impls_get<G>() where G : Get { }
fn main() { }

View File

@ -0,0 +1,31 @@
// Copyright 2015 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.
#![allow(dead_code)]
trait Get<T> : 'static {
fn get(&self, t: T) -> T;
}
fn get_min_from_max<'min, 'max>(v: Box<Get<&'max i32>>)
-> Box<Get<&'min i32>>
where 'max : 'min
{
v //~ ERROR mismatched types
}
fn get_max_from_min<'min, 'max, G>(v: Box<Get<&'min i32>>)
-> Box<Get<&'max i32>>
where 'max : 'min
{
v //~ ERROR mismatched types
}
fn main() { }

View File

@ -0,0 +1,31 @@
// Copyright 2015 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.
#![allow(dead_code)]
trait Get<T> {
fn get(&self, t: T) -> T;
}
fn get_min_from_max<'min, 'max, G>()
where 'max : 'min, G : Get<&'max i32>
{
impls_get::<G,&'min i32>() //~ ERROR mismatched types
}
fn get_max_from_min<'min, 'max, G>()
where 'max : 'min, G : Get<&'min i32>
{
impls_get::<G,&'max i32>() //~ ERROR mismatched types
}
fn impls_get<G,T>() where G : Get<T> { }
fn main() { }

View File

@ -0,0 +1,31 @@
// Copyright 2015 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.
#![allow(dead_code)]
trait Get {
fn get(&self) -> Self;
}
fn get_min_from_max<'min, 'max, G>()
where 'max : 'min, &'max G : Get
{
impls_get::<&'min G>(); //~ ERROR mismatched types
}
fn get_max_from_min<'min, 'max, G>()
where 'max : 'min, &'min G : Get
{
impls_get::<&'max G>(); //~ ERROR mismatched types
}
fn impls_get<G>() where G : Get { }
fn main() { }

View File

@ -0,0 +1,25 @@
// Copyright 2012 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 disallow lifetime parameters that are unused.
use std::marker;
struct Bivariant<'a>; //~ ERROR parameter `'a` is never used
struct Struct<'a, 'd> { //~ ERROR parameter `'d` is never used
field: &'a [i32]
}
trait Trait<'a, 'd> { //~ ERROR parameter `'d` is never used
fn method(&'a self);
}
fn main() {}

View File

@ -0,0 +1,21 @@
// Copyright 2012 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 disallow lifetime parameters that are unused.
enum Foo<'a> { //~ ERROR parameter `'a` is never used
Foo1(Bar<'a>)
}
enum Bar<'a> { //~ ERROR parameter `'a` is never used
Bar1(Foo<'a>)
}
fn main() {}

View File

@ -0,0 +1,64 @@
// 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.
#![deny(bivariance)]
#![allow(dead_code)]
// Check that bounds on type parameters (other than `Self`) do not
// influence variance.
#[rustc_variance]
trait Getter<T> { //~ ERROR types=[[+];[-];[]]
fn get(&self) -> T;
}
#[rustc_variance]
trait Setter<T> { //~ ERROR types=[[-];[-];[]]
fn get(&self, T);
}
#[rustc_variance]
struct TestStruct<U,T:Setter<U>> { //~ ERROR types=[[+, +];[];[]]
t: T, u: U
}
#[rustc_variance]
enum TestEnum<U,T:Setter<U>> {//~ ERROR types=[[*, +];[];[]]
//~^ ERROR parameter `U` is never used
Foo(T)
}
#[rustc_variance]
trait TestTrait<U,T:Setter<U>> { //~ ERROR types=[[-, +];[-];[]]
fn getter(&self, u: U) -> T;
}
#[rustc_variance]
trait TestTrait2<U> : Getter<U> { //~ ERROR types=[[+];[-];[]]
}
#[rustc_variance]
trait TestTrait3<U> { //~ ERROR types=[[-];[-];[]]
fn getter<T:Getter<U>>(&self);
}
#[rustc_variance]
struct TestContraStruct<U,T:Setter<U>> { //~ ERROR types=[[*, +];[];[]]
//~^ ERROR parameter `U` is never used
t: T
}
#[rustc_variance]
struct TestBox<U,T:Getter<U>+Setter<U>> { //~ ERROR types=[[*, +];[];[]]
//~^ ERROR parameter `U` is never used
t: T
}
pub fn main() { }

View File

@ -0,0 +1,74 @@
// Copyright 2012 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 correctly infer variance for type parameters in
// various types and traits.
#[rustc_variance]
struct TestImm<A, B> { //~ ERROR types=[[+, +];[];[]]
x: A,
y: B,
}
#[rustc_variance]
struct TestMut<A, B:'static> { //~ ERROR types=[[+, o];[];[]]
x: A,
y: &'static mut B,
}
#[rustc_variance]
struct TestIndirect<A:'static, B:'static> { //~ ERROR types=[[+, o];[];[]]
m: TestMut<A, B>
}
#[rustc_variance]
struct TestIndirect2<A:'static, B:'static> { //~ ERROR types=[[o, o];[];[]]
n: TestMut<A, B>,
m: TestMut<B, A>
}
#[rustc_variance]
trait Getter<A> { //~ ERROR types=[[+];[-];[]]
fn get(&self) -> A;
}
#[rustc_variance]
trait Setter<A> { //~ ERROR types=[[-];[o];[]]
fn set(&mut self, a: A);
}
#[rustc_variance]
trait GetterSetter<A> { //~ ERROR types=[[o];[o];[]]
fn get(&self) -> A;
fn set(&mut self, a: A);
}
#[rustc_variance]
trait GetterInTypeBound<A> { //~ ERROR types=[[-];[-];[]]
// Here, the use of `A` in the method bound *does* affect
// variance. Think of it as if the method requested a dictionary
// for `T:Getter<A>`. Since this dictionary is an input, it is
// contravariant, and the Getter is covariant w/r/t A, yielding an
// overall contravariant result.
fn do_it<T:Getter<A>>(&self);
}
#[rustc_variance]
trait SetterInTypeBound<A> { //~ ERROR types=[[+];[-];[]]
fn do_it<T:Setter<A>>(&self);
}
#[rustc_variance]
struct TestObject<A, R> { //~ ERROR types=[[-, +];[];[]]
n: Box<Setter<A>+Send>,
m: Box<Getter<R>+Send>,
}
fn main() {}

View File

@ -0,0 +1,51 @@
// 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.
#![deny(bivariance)]
#![allow(dead_code)]
use std::cell::Cell;
// Check that a type parameter which is only used in a trait bound is
// not considered bivariant.
#[rustc_variance]
struct InvariantMut<'a,A:'a,B:'a> { //~ ERROR types=[[o, o];[];[]], regions=[[-];[];[]]
t: &'a mut (A,B)
}
#[rustc_variance]
struct InvariantCell<A> { //~ ERROR types=[[o];[];[]]
t: Cell<A>
}
#[rustc_variance]
struct InvariantIndirect<A> { //~ ERROR types=[[o];[];[]]
t: InvariantCell<A>
}
#[rustc_variance]
struct Covariant<A> { //~ ERROR types=[[+];[];[]]
t: A, u: fn() -> A
}
#[rustc_variance]
struct Contravariant<A> { //~ ERROR types=[[-];[];[]]
t: fn(A)
}
#[rustc_variance]
enum Enum<A,B,C> { //~ ERROR types=[[+, -, o];[];[]]
Foo(Covariant<A>),
Bar(Contravariant<B>),
Zed(Covariant<C>,Contravariant<C>)
}
pub fn main() { }

View File

@ -0,0 +1,17 @@
// Copyright 2012 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 report an error for unused type parameters in types.
struct SomeStruct<'a> { x: u32 } //~ ERROR parameter `'a` is never used
enum SomeEnum<'a> { Nothing } //~ ERROR parameter `'a` is never used
trait SomeTrait<'a> { fn foo(&self); } //~ ERROR parameter `'a` is never used
fn main() {}

View File

@ -0,0 +1,36 @@
// Copyright 2012 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.
#![allow(dead_code)]
// Test that we report an error for unused type parameters in types and traits,
// and that we offer a helpful suggestion.
struct SomeStruct<A> { x: u32 }
//~^ ERROR parameter `A` is never used
//~| HELP PhantomData
enum SomeEnum<A> { Nothing }
//~^ ERROR parameter `A` is never used
//~| HELP PhantomData
trait SomeTrait<A> { fn foo(&self); }
//~^ ERROR parameter `A` is never used
//~| HELP PhantomFn
// Here T might *appear* used, but in fact it isn't.
enum ListCell<T> {
//~^ ERROR parameter `T` is never used
//~| HELP PhantomData
Cons(Box<ListCell<T>>),
Nil
}
fn main() {}

View File

@ -0,0 +1,24 @@
// Copyright 2015 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 various uses of structs with distint variances to make sure
// they permit lifetimes to be approximated as expected.
struct SomeStruct<T>(fn(T));
fn foo<'min,'max>(v: SomeStruct<&'max ()>)
-> SomeStruct<&'min ()>
where 'max : 'min
{
v //~ ERROR mismatched types
}
#[rustc_error]
fn main() { }

View File

@ -0,0 +1,26 @@
// Copyright 2015 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 various uses of structs with distint variances to make sure
// they permit lifetimes to be approximated as expected.
#![allow(dead_code)]
struct SomeStruct<T>(fn(T));
fn bar<'min,'max>(v: SomeStruct<&'min ()>)
-> SomeStruct<&'max ()>
where 'max : 'min
{
v
}
#[rustc_error]
fn main() { } //~ ERROR compilation successful

View File

@ -0,0 +1,23 @@
// Copyright 2015 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 covariant struct does not permit the lifetime of a
// reference to be enlarged.
struct SomeStruct<T>(T);
fn foo<'min,'max>(v: SomeStruct<&'min ()>)
-> SomeStruct<&'max ()>
where 'max : 'min
{
v //~ ERROR mismatched types
}
fn main() { }

View File

@ -0,0 +1,25 @@
// Copyright 2015 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 covariant struct permits the lifetime of a reference to
// be shortened.
#![allow(dead_code)]
struct SomeStruct<T>(T);
fn foo<'min,'max>(v: SomeStruct<&'max ()>)
-> SomeStruct<&'min ()>
where 'max : 'min
{
v
}
#[rustc_error] fn main() { } //~ ERROR compilation successful

View File

@ -0,0 +1,31 @@
// Copyright 2015 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 various uses of structs with distint variances to make sure
// they permit lifetimes to be approximated as expected.
struct SomeStruct<T>(*mut T);
fn foo<'min,'max>(v: SomeStruct<&'max ()>)
-> SomeStruct<&'min ()>
where 'max : 'min
{
v //~ ERROR mismatched types
}
fn bar<'min,'max>(v: SomeStruct<&'min ()>)
-> SomeStruct<&'max ()>
where 'max : 'min
{
v //~ ERROR mismatched types
}
#[rustc_error]
fn main() { }

View File

@ -0,0 +1,34 @@
// Copyright 2015 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.
// Elaborated version of the opening example from RFC 738. This failed
// to compile before variance because invariance of `Option` prevented
// us from approximating the lifetimes of `field1` and `field2` to a
// common intersection.
#![allow(dead_code)]
struct List<'l> {
field1: &'l i32,
field2: Option<&'l i32>,
}
fn foo(field1: &i32, field2: Option<&i32>) -> i32 {
let list = List { field1: field1, field2: field2 };
*list.field1 + list.field2.cloned().unwrap_or(0)
}
fn main() {
let x = 22;
let y = Some(3);
let z = None;
assert_eq!(foo(&x, y.as_ref()), 25);
assert_eq!(foo(&x, z.as_ref()), 22);
}

View File

@ -0,0 +1,49 @@
// Copyright 2015 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.
#![allow(dead_code)]
// Get<T> is covariant in T
trait Get<T> {
fn get(&self) -> T;
}
struct Cloner<T:Clone> {
t: T
}
impl<T:Clone> Get<T> for Cloner<T> {
fn get(&self) -> T {
self.t.clone()
}
}
fn get<'a, G>(get: &G) -> i32
where G : Get<&'a i32>
{
// This call only type checks if we can use `G : Get<&'a i32>` as
// evidence that `G : Get<&'b i32>` where `'a : 'b`.
pick(get, &22)
}
fn pick<'b, G>(get: &'b G, if_odd: &'b i32) -> i32
where G : Get<&'b i32>
{
let v = *get.get();
if v % 2 != 0 { v } else { *if_odd }
}
fn main() {
let x = Cloner { t: &23 };
let y = get(&x);
assert_eq!(y, 23);
}

View File

@ -0,0 +1,28 @@
// Copyright 2015 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 vec is now covariant in its argument type.
#![allow(dead_code)]
fn foo<'a,'b>(v1: Vec<&'a i32>, v2: Vec<&'b i32>) -> i32 {
bar(v1, v2).cloned().unwrap_or(0) // only type checks if we can intersect 'a and 'b
}
fn bar<'c>(v1: Vec<&'c i32>, v2: Vec<&'c i32>) -> Option<&'c i32> {
v1.get(0).cloned().or_else(|| v2.get(0).cloned())
}
fn main() {
let x = 22;
let y = 44;
assert_eq!(foo(vec![&x], vec![&y]), 22);
assert_eq!(foo(vec![&y], vec![&x]), 44);
}