about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2015-02-20 10:18:38 -0500
committerNiko Matsakis <niko@alum.mit.edu>2015-02-24 18:21:20 -0500
commitdb6ae66764ddb80926b2a18672162d1706ff82a6 (patch)
tree6cb2f24f17b1c10ababe13b620f25b9f51ecd904
parent0bd15657d93c932611f3aee351b6521cdfa77731 (diff)
downloadrust-db6ae66764ddb80926b2a18672162d1706ff82a6.tar.gz
rust-db6ae66764ddb80926b2a18672162d1706ff82a6.zip
Make traits with assoc types invariant in their inputs.
-rw-r--r--src/librustc/middle/infer/combine.rs8
-rw-r--r--src/librustc_typeck/variance.rs65
2 files changed, 64 insertions, 9 deletions
diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs
index 99cb2a0978e..40ccab510c4 100644
--- a/src/librustc/middle/infer/combine.rs
+++ b/src/librustc/middle/infer/combine.rs
@@ -265,13 +265,7 @@ pub trait Combine<'tcx> : Sized {
             Err(ty::terr_projection_name_mismatched(
                 expected_found(self, a.item_name, b.item_name)))
         } else {
-            // Note that the trait refs for the projection must be
-            // *equal*. This is because there is no inherent
-            // relationship between `<T as Foo>::Bar` and `<U as
-            // Foo>::Bar` that we can derive based on how `T` relates
-            // to `U`. Issue #21726 contains further discussion and
-            // in-depth examples.
-            let trait_ref = try!(self.equate().trait_refs(&*a.trait_ref, &*b.trait_ref));
+            let trait_ref = try!(self.trait_refs(&*a.trait_ref, &*b.trait_ref));
             Ok(ty::ProjectionTy { trait_ref: Rc::new(trait_ref), item_name: a.item_name })
         }
     }
diff --git a/src/librustc_typeck/variance.rs b/src/librustc_typeck/variance.rs
index 90ca6a79805..1fba4a21ccd 100644
--- a/src/librustc_typeck/variance.rs
+++ b/src/librustc_typeck/variance.rs
@@ -203,6 +203,56 @@
 //! 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.
+//!
+//! ### Associated types
+//!
+//! Any trait with an associated type is invariant with respect to all
+//! of its inputs. To see why this makes sense, consider what
+//! subtyping for a trait reference means:
+//!
+//!    <T as Trait> <: <U as Trait>
+//!
+//! means that if I know that `T as Trait`,
+//! I also know that `U as
+//! Trait`. Moreover, if you think of it as
+//! dictionary passing style, it means that
+//! a dictionary for `<T as Trait>` is safe
+//! to use where a dictionary for `<U as
+//! Trait>` is expected.
+//!
+//! The problem is that when you can
+//! project types out from `<T as Trait>`,
+//! the relationship to types projected out
+//! of `<U as Trait>` is completely unknown
+//! unless `T==U` (see #21726 for more
+//! details). Making `Trait` invariant
+//! ensures that this is true.
+//!
+//! *Historical note: we used to preserve this invariant another way,
+//! by tweaking the subtyping rules and requiring that when a type `T`
+//! appeared as part of a projection, that was considered an invariant
+//! location, but this version does away with the need for those
+//! somewhat "special-case-feeling" rules.*
+//!
+//! Another related reason is that if we didn't make traits with
+//! associated types invariant, then projection is no longer a
+//! function with a single result. Consider:
+//!
+//! ```
+//! trait Identity { type Out; fn foo(&self); }
+//! impl<T> Identity for T { type Out = T; ... }
+//! ```
+//!
+//! Now if I have `<&'static () as Identity>::Out`, this can be
+//! validly derived as `&'a ()` for any `'a`:
+//!
+//!    <&'a () as Identity> <: <&'static () as Identity>
+//!    if &'static () < : &'a ()   -- Identity is contravariant in Self
+//!    if 'static : 'a             -- Subtyping rules for relations
+//!
+//! This change otoh means that `<'static () as Identity>::Out` is
+//! always `&'static ()` (which might then be upcast to `'a ()`,
+//! separately). This was helpful in solving #21750.
 
 use self::VarianceTerm::*;
 use self::ParamKind::*;
@@ -613,7 +663,18 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ConstraintContext<'a, 'tcx> {
                                 &method.fty.sig,
                                 self.covariant);
                         }
-                        ty::TypeTraitItem(_) => {}
+                        ty::TypeTraitItem(ref data) => {
+                            // Any trait with an associated type is
+                            // invariant with respect to all of its
+                            // inputs. See length discussion in the comment
+                            // on this module.
+                            let projection_ty = ty::mk_projection(tcx,
+                                                                  trait_def.trait_ref.clone(),
+                                                                  data.name);
+                            self.add_constraints_from_ty(&trait_def.generics,
+                                                         projection_ty,
+                                                         self.invariant);
+                        }
                     }
                 }
             }
@@ -893,7 +954,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
                     trait_def.generics.types.as_slice(),
                     trait_def.generics.regions.as_slice(),
                     trait_ref.substs,
-                    self.invariant);
+                    variance);
             }
 
             ty::ty_trait(ref data) => {