]> git.lizzy.rs Git - rust.git/commitdiff
replace the type generalizer with one based on variance
authorNiko Matsakis <niko@alum.mit.edu>
Tue, 2 May 2017 19:03:34 +0000 (15:03 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Thu, 11 May 2017 18:52:25 +0000 (14:52 -0400)
src/librustc/infer/combine.rs
src/librustc/ty/mod.rs
src/librustc_typeck/variance/constraints.rs
src/librustc_typeck/variance/xform.rs

index 1bac512e20977b087d4fdd09b35e336bbf8e8e9b..ac78d53c02c50006c37f99d013b32acaeda88bc2 100644 (file)
@@ -42,8 +42,7 @@
 use ty::{IntType, UintType};
 use ty::{self, Ty, TyCtxt};
 use ty::error::TypeError;
-use ty::fold::TypeFoldable;
-use ty::relate::{RelateResult, TypeRelation};
+use ty::relate::{self, Relate, RelateResult, TypeRelation};
 use traits::PredicateObligations;
 
 use syntax::ast;
@@ -207,7 +206,7 @@ pub fn instantiate(&mut self,
         // `'?2` and `?3` are fresh region/type inference
         // variables. (Down below, we will relate `a_ty <: b_ty`,
         // adding constraints like `'x: '?2` and `?1 <: ?3`.)
-        let b_ty = self.generalize(a_ty, b_vid, dir == EqTo)?;
+        let b_ty = self.generalize(a_ty, b_vid, dir)?;
         debug!("instantiate(a_ty={:?}, dir={:?}, b_vid={:?}, generalized b_ty={:?})",
                a_ty, dir, b_vid, b_ty);
         self.infcx.type_variables.borrow_mut().instantiate(b_vid, b_ty);
@@ -241,22 +240,30 @@ pub fn instantiate(&mut self,
     fn generalize(&self,
                   ty: Ty<'tcx>,
                   for_vid: ty::TyVid,
-                  is_eq_relation: bool)
+                  dir: RelationDir)
                   -> RelateResult<'tcx, Ty<'tcx>>
     {
+        // Determine the ambient variance within which `ty` appears.
+        // The surrounding equation is:
+        //
+        //     ty [op] ty2
+        //
+        // where `op` is either `==`, `<:`, or `:>`. This maps quite
+        // naturally.
+        let ambient_variance = match dir {
+            RelationDir::EqTo => ty::Invariant,
+            RelationDir::SubtypeOf => ty::Covariant,
+            RelationDir::SupertypeOf => ty::Contravariant,
+        };
+
         let mut generalize = Generalizer {
             infcx: self.infcx,
             span: self.trace.cause.span,
             for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
-            is_eq_relation: is_eq_relation,
-            cycle_detected: false
+            ambient_variance: ambient_variance,
         };
-        let u = ty.fold_with(&mut generalize);
-        if generalize.cycle_detected {
-            Err(TypeError::CyclicTy)
-        } else {
-            Ok(u)
-        }
+
+        generalize.relate(&ty, &ty)
     }
 }
 
@@ -264,16 +271,46 @@ struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
     infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
     span: Span,
     for_vid_sub_root: ty::TyVid,
-    is_eq_relation: bool,
-    cycle_detected: bool,
+    ambient_variance: ty::Variance,
 }
 
-impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx, 'tcx> {
-    fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx> {
+impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, 'tcx> {
+    fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
         self.infcx.tcx
     }
 
-    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+    fn tag(&self) -> &'static str {
+        "Generalizer"
+    }
+
+    fn a_is_expected(&self) -> bool {
+        true
+    }
+
+    fn binders<T>(&mut self, a: &ty::Binder<T>, b: &ty::Binder<T>)
+                  -> RelateResult<'tcx, ty::Binder<T>>
+        where T: Relate<'tcx>
+    {
+        Ok(ty::Binder(self.relate(a.skip_binder(), b.skip_binder())?))
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(&mut self,
+                                             variance: ty::Variance,
+                                             a: &T,
+                                             b: &T)
+                                             -> RelateResult<'tcx, T>
+    {
+        let old_ambient_variance = self.ambient_variance;
+        self.ambient_variance = self.ambient_variance.xform(variance);
+
+        let result = self.relate(a, b);
+        self.ambient_variance = old_ambient_variance;
+        result
+    }
+
+    fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        assert_eq!(t, t2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==
+
         // Check to see whether the type we are genealizing references
         // any other type variable related to `vid` via
         // subtyping. This is basically our "occurs check", preventing
@@ -286,41 +323,54 @@ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
                 if sub_vid == self.for_vid_sub_root {
                     // If sub-roots are equal, then `for_vid` and
                     // `vid` are related via subtyping.
-                    self.cycle_detected = true;
-                    self.tcx().types.err
+                    return Err(TypeError::CyclicTy);
                 } else {
                     match variables.probe_root(vid) {
                         Some(u) => {
                             drop(variables);
-                            self.fold_ty(u)
+                            self.relate(&u, &u)
                         }
                         None => {
-                            if !self.is_eq_relation {
-                                let origin = variables.origin(vid);
-                                let new_var_id = variables.new_var(false, origin, None);
-                                let u = self.tcx().mk_var(new_var_id);
-                                debug!("generalize: replacing original vid={:?} with new={:?}",
-                                       vid, u);
-                                u
-                            } else {
-                                t
+                            match self.ambient_variance {
+                                ty::Invariant => Ok(t),
+
+                                ty::Bivariant | ty::Covariant | ty::Contravariant => {
+                                    let origin = variables.origin(vid);
+                                    let new_var_id = variables.new_var(false, origin, None);
+                                    let u = self.tcx().mk_var(new_var_id);
+                                    debug!("generalize: replacing original vid={:?} with new={:?}",
+                                           vid, u);
+                                    Ok(u)
+                                }
                             }
                         }
                     }
                 }
             }
+            ty::TyInfer(ty::IntVar(_)) |
+            ty::TyInfer(ty::FloatVar(_)) => {
+                // No matter what mode we are in,
+                // integer/floating-point types must be equal to be
+                // relatable.
+                Ok(t)
+            }
             _ => {
-                t.super_fold_with(self)
+                relate::super_relate_tys(self, t, t)
             }
         }
     }
 
-    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+    fn regions(&mut self, r: ty::Region<'tcx>, r2: ty::Region<'tcx>)
+               -> RelateResult<'tcx, ty::Region<'tcx>> {
+        assert_eq!(r, r2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==
+
         match *r {
             // Never make variables for regions bound within the type itself,
             // nor for erased regions.
             ty::ReLateBound(..) |
-            ty::ReErased => { return r; }
+            ty::ReErased => {
+                return Ok(r);
+            }
 
             // Early-bound regions should really have been substituted away before
             // we get to this point.
@@ -342,15 +392,16 @@ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
             ty::ReScope(..) |
             ty::ReVar(..) |
             ty::ReFree(..) => {
-                if self.is_eq_relation {
-                    return r;
+                match self.ambient_variance {
+                    ty::Invariant => return Ok(r),
+                    ty::Bivariant | ty::Covariant | ty::Contravariant => (),
                 }
             }
         }
 
         // FIXME: This is non-ideal because we don't give a
         // very descriptive origin for this region variable.
-        self.infcx.next_region_var(MiscVariable(self.span))
+        Ok(self.infcx.next_region_var(MiscVariable(self.span)))
     }
 }
 
index 51cdba56cf10a98c1ab7eeaa65f1beba9ef080bc..f5d510c11ae9dd64d1f48b4c3d021b7f29c6e334 100644 (file)
@@ -330,6 +330,66 @@ pub struct CrateVariancesMap {
     pub empty_variance: Rc<Vec<ty::Variance>>,
 }
 
+impl Variance {
+    /// `a.xform(b)` combines the variance of a context with the
+    /// variance of a type with the following meaning.  If we are in a
+    /// context with variance `a`, and we encounter a type argument in
+    /// a position with variance `b`, then `a.xform(b)` is the new
+    /// variance with which the argument appears.
+    ///
+    /// Example 1:
+    ///
+    ///     *mut Vec<i32>
+    ///
+    /// Here, the "ambient" variance starts as covariant. `*mut T` is
+    /// invariant with respect to `T`, so the variance in which the
+    /// `Vec<i32>` appears is `Covariant.xform(Invariant)`, which
+    /// yields `Invariant`. Now, the type `Vec<T>` is covariant with
+    /// respect to its type argument `T`, and hence the variance of
+    /// the `i32` here is `Invariant.xform(Covariant)`, which results
+    /// (again) in `Invariant`.
+    ///
+    /// Example 2:
+    ///
+    ///     fn(*const Vec<i32>, *mut Vec<i32)
+    ///
+    /// The ambient variance is covariant. A `fn` type is
+    /// contravariant with respect to its parameters, so the variance
+    /// within which both pointer types appear is
+    /// `Covariant.xform(Contravariant)`, or `Contravariant`.  `*const
+    /// T` is covariant with respect to `T`, so the variance within
+    /// which the first `Vec<i32>` appears is
+    /// `Contravariant.xform(Covariant)` or `Contravariant`.  The same
+    /// is true for its `i32` argument. In the `*mut T` case, the
+    /// variance of `Vec<i32>` is `Contravariant.xform(Invariant)`,
+    /// and hence the outermost type is `Invariant` with respect to
+    /// `Vec<i32>` (and its `i32` argument).
+    ///
+    /// Source: Figure 1 of "Taming the Wildcards:
+    /// Combining Definition- and Use-Site Variance" published in PLDI'11.
+    pub fn xform(self, v: ty::Variance) -> ty::Variance {
+        match (self, v) {
+            // Figure 1, column 1.
+            (ty::Covariant, ty::Covariant) => ty::Covariant,
+            (ty::Covariant, ty::Contravariant) => ty::Contravariant,
+            (ty::Covariant, ty::Invariant) => ty::Invariant,
+            (ty::Covariant, ty::Bivariant) => ty::Bivariant,
+
+            // Figure 1, column 2.
+            (ty::Contravariant, ty::Covariant) => ty::Contravariant,
+            (ty::Contravariant, ty::Contravariant) => ty::Covariant,
+            (ty::Contravariant, ty::Invariant) => ty::Invariant,
+            (ty::Contravariant, ty::Bivariant) => ty::Bivariant,
+
+            // Figure 1, column 3.
+            (ty::Invariant, _) => ty::Invariant,
+
+            // Figure 1, column 4.
+            (ty::Bivariant, _) => ty::Bivariant,
+        }
+    }
+}
+
 #[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)]
 pub struct MethodCallee<'tcx> {
     /// Impl method ID, for inherent methods, or trait method ID, otherwise.
index e986a381cd963df034968e27a66fa463c2a5c2e6..cb2ee7dd1bcda5c7b89bcc4fda7a95e6d543f81c 100644 (file)
@@ -27,7 +27,6 @@
 
 use super::terms::*;
 use super::terms::VarianceTerm::*;
-use super::xform::*;
 
 pub struct ConstraintContext<'a, 'tcx: 'a> {
     pub terms_cx: TermsContext<'a, 'tcx>,
index 507734ce35e44fdcf1695911ac48b7c7190b6293..7106ca4d420a86100bebc22a21d4e64a8f98dd81 100644 (file)
 
 use rustc::ty;
 
-pub trait Xform {
-    fn xform(self, v: Self) -> Self;
-}
-
-impl Xform for ty::Variance {
-    fn xform(self, v: ty::Variance) -> ty::Variance {
-        // "Variance transformation", Figure 1 of The Paper
-        match (self, v) {
-            // Figure 1, column 1.
-            (ty::Covariant, ty::Covariant) => ty::Covariant,
-            (ty::Covariant, ty::Contravariant) => ty::Contravariant,
-            (ty::Covariant, ty::Invariant) => ty::Invariant,
-            (ty::Covariant, ty::Bivariant) => ty::Bivariant,
-
-            // Figure 1, column 2.
-            (ty::Contravariant, ty::Covariant) => ty::Contravariant,
-            (ty::Contravariant, ty::Contravariant) => ty::Covariant,
-            (ty::Contravariant, ty::Invariant) => ty::Invariant,
-            (ty::Contravariant, ty::Bivariant) => ty::Bivariant,
-
-            // Figure 1, column 3.
-            (ty::Invariant, _) => ty::Invariant,
-
-            // Figure 1, column 4.
-            (ty::Bivariant, _) => ty::Bivariant,
-        }
-    }
-}
-
 pub fn glb(v1: ty::Variance, v2: ty::Variance) -> ty::Variance {
     // Greatest lower bound of the variance lattice as
     // defined in The Paper: