Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 1 addition & 12 deletions src/librustc/middle/infer/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}

Expand Down
45 changes: 35 additions & 10 deletions src/librustc_typeck/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
{
Expand Down Expand Up @@ -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,
}
Expand Down
35 changes: 35 additions & 0 deletions src/test/compile-fail/regions-trait-object-subtyping.rs
Original file line number Diff line number Diff line change
@@ -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() {}