Navigation Menu

Skip to content

Commit

Permalink
Remove incorrect subtyping for &mut Trait and introduce coercion
Browse files Browse the repository at this point in the history
for `&mut (Trait+'a)` to `&mut (Trait+'b)` if `'a:'b`.

Fixes #14985.
  • Loading branch information
nikomatsakis committed Mar 23, 2015
1 parent b0aad7d commit 50ea6f6
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 22 deletions.
13 changes: 1 addition & 12 deletions src/librustc/middle/infer/combine.rs
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
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
@@ -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() {}

0 comments on commit 50ea6f6

Please sign in to comment.