rollup merge of #23515: nikomatsakis/issue-14985-trait-subtyping

Remove incorrect subtyping for `&mut Trait` and introduce coercion for `&mut (Trait+'a)` to `&mut (Trait+'b)` if `'a:'b`.

Fixes #14985.

r? @nrc
This commit is contained in:
Alex Crichton 2015-03-23 15:08:13 -07:00
commit 0678f0b10c
3 changed files with 71 additions and 22 deletions

View File

@ -557,18 +557,7 @@ pub fn super_tys<'tcx, C>(this: &C,
(&ty::ty_rptr(a_r, ref a_mt), &ty::ty_rptr(b_r, ref b_mt)) => {
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
// path which would require invariance.
let mt = match (&a_mt.ty.sty, &b_mt.ty.sty) {
(&ty::ty_trait(..), &ty::ty_trait(..)) if a_mt.mutbl == b_mt.mutbl => {
let ty = try!(this.tys(a_mt.ty, b_mt.ty));
ty::mt { ty: ty, mutbl: a_mt.mutbl }
}
_ => try!(this.mts(a_mt, b_mt))
};
let mt = try!(this.mts(a_mt, b_mt));
Ok(ty::mk_rptr(tcx, tcx.mk_region(r), mt))
}

View File

@ -92,6 +92,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
Ok(None) // No coercion required.
}
fn outlives(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ()> {
let sub = Sub(self.fcx.infcx().combine_fields(false, self.trace.clone()));
try!(sub.regions(b, a));
Ok(())
}
fn unpack_actual_value<T, F>(&self, a: Ty<'tcx>, f: F) -> T where
F: FnOnce(Ty<'tcx>) -> T,
{
@ -340,21 +346,40 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
Some((ty, ty::UnsizeLength(len)))
}
(&ty::ty_trait(ref data_a), &ty::ty_trait(ref data_b)) => {
// For now, we only support upcasts from
// `Foo+Send` to `Foo` (really, any time there are
// fewer builtin bounds then before). These are
// convenient because they don't require any sort
// of change to the vtable at runtime.
if data_a.bounds.builtin_bounds != data_b.bounds.builtin_bounds &&
data_a.bounds.builtin_bounds.is_superset(&data_b.bounds.builtin_bounds)
{
// Upcasts permit two things:
//
// 1. Dropping builtin bounds, e.g. `Foo+Send` to `Foo`
// 2. Tightening the region bound, e.g. `Foo+'a` to `Foo+'b` if `'a : 'b`
//
// Note that neither of these changes requires any
// change at runtime. Eventually this will be
// generalized.
//
// We always upcast when we can because of reason
// #2 (region bounds).
if data_a.bounds.builtin_bounds.is_superset(&data_b.bounds.builtin_bounds) {
// construct a type `a1` which is a version of
// `a` using the upcast bounds from `b`
let bounds_a1 = ty::ExistentialBounds {
region_bound: data_a.bounds.region_bound,
// From type b
region_bound: data_b.bounds.region_bound,
builtin_bounds: data_b.bounds.builtin_bounds,
// From type a
projection_bounds: data_a.bounds.projection_bounds.clone(),
};
let ty_a1 = ty::mk_trait(tcx, data_a.principal.clone(), bounds_a1);
match self.fcx.infcx().try(|_| self.subtype(ty_a1, ty_b)) {
// relate `a1` to `b`
let result = self.fcx.infcx().try(|_| {
// it's ok to upcast from Foo+'a to Foo+'b so long as 'a : 'b
try!(self.outlives(data_a.bounds.region_bound,
data_b.bounds.region_bound));
self.subtype(ty_a1, ty_b)
});
// if that was successful, we have a coercion
match result {
Ok(_) => Some((ty_b, ty::UnsizeUpcast(ty_b))),
Err(_) => None,
}

View File

@ -0,0 +1,35 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
trait Dummy { fn dummy(&self); }
fn foo1<'a:'b,'b>(x: &'a mut (Dummy+'a)) -> &'b mut (Dummy+'b) {
// Here, we are able to coerce
x
}
fn foo2<'a:'b,'b>(x: &'b mut (Dummy+'a)) -> &'b mut (Dummy+'b) {
// Here, we are able to coerce
x
}
fn foo3<'a,'b>(x: &'a mut Dummy) -> &'b mut Dummy {
// Without knowing 'a:'b, we can't coerce
x //~ ERROR mismatched types
//~^ ERROR cannot infer
}
struct Wrapper<T>(T);
fn foo4<'a:'b,'b>(x: Wrapper<&'a mut Dummy>) -> Wrapper<&'b mut Dummy> {
// We can't coerce because it is packed in `Wrapper`
x //~ ERROR mismatched types
}
fn main() {}