Skip to content

Remove incorrect subtyping for &mut Trait and introduce coercion #23515

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 24, 2015
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() {}