diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs index 99cb2a0978e7..40ccab510c4f 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 `::Bar` and `::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 90ca6a798056..1fba4a21ccd3 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` 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: +//! +//! <: +//! +//! 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 `` is safe +//! to use where a dictionary for `` is expected. +//! +//! The problem is that when you can +//! project types out from ``, +//! the relationship to types projected out +//! of `` 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 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) => {