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:
commit
0678f0b10c
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
35
src/test/compile-fail/regions-trait-object-subtyping.rs
Normal file
35
src/test/compile-fail/regions-trait-object-subtyping.rs
Normal 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() {}
|
Loading…
x
Reference in New Issue
Block a user