Skip to content

Commit 50ea6f6

Browse files
committed
Remove incorrect subtyping for &mut Trait and introduce coercion
for `&mut (Trait+'a)` to `&mut (Trait+'b)` if `'a:'b`. Fixes #14985.
1 parent b0aad7d commit 50ea6f6

File tree

3 files changed

+71
-22
lines changed

3 files changed

+71
-22
lines changed

src/librustc/middle/infer/combine.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -557,18 +557,7 @@ pub fn super_tys<'tcx, C>(this: &C,
557557

558558
(&ty::ty_rptr(a_r, ref a_mt), &ty::ty_rptr(b_r, ref b_mt)) => {
559559
let r = try!(this.regions_with_variance(ty::Contravariant, *a_r, *b_r));
560-
561-
// FIXME(14985) If we have mutable references to trait objects, we
562-
// used to use covariant subtyping. I have preserved this behaviour,
563-
// even though it is probably incorrect. So don't go down the usual
564-
// path which would require invariance.
565-
let mt = match (&a_mt.ty.sty, &b_mt.ty.sty) {
566-
(&ty::ty_trait(..), &ty::ty_trait(..)) if a_mt.mutbl == b_mt.mutbl => {
567-
let ty = try!(this.tys(a_mt.ty, b_mt.ty));
568-
ty::mt { ty: ty, mutbl: a_mt.mutbl }
569-
}
570-
_ => try!(this.mts(a_mt, b_mt))
571-
};
560+
let mt = try!(this.mts(a_mt, b_mt));
572561
Ok(ty::mk_rptr(tcx, tcx.mk_region(r), mt))
573562
}
574563

src/librustc_typeck/check/coercion.rs

+35-10
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
9292
Ok(None) // No coercion required.
9393
}
9494

95+
fn outlives(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ()> {
96+
let sub = Sub(self.fcx.infcx().combine_fields(false, self.trace.clone()));
97+
try!(sub.regions(b, a));
98+
Ok(())
99+
}
100+
95101
fn unpack_actual_value<T, F>(&self, a: Ty<'tcx>, f: F) -> T where
96102
F: FnOnce(Ty<'tcx>) -> T,
97103
{
@@ -340,21 +346,40 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
340346
Some((ty, ty::UnsizeLength(len)))
341347
}
342348
(&ty::ty_trait(ref data_a), &ty::ty_trait(ref data_b)) => {
343-
// For now, we only support upcasts from
344-
// `Foo+Send` to `Foo` (really, any time there are
345-
// fewer builtin bounds then before). These are
346-
// convenient because they don't require any sort
347-
// of change to the vtable at runtime.
348-
if data_a.bounds.builtin_bounds != data_b.bounds.builtin_bounds &&
349-
data_a.bounds.builtin_bounds.is_superset(&data_b.bounds.builtin_bounds)
350-
{
349+
// Upcasts permit two things:
350+
//
351+
// 1. Dropping builtin bounds, e.g. `Foo+Send` to `Foo`
352+
// 2. Tightening the region bound, e.g. `Foo+'a` to `Foo+'b` if `'a : 'b`
353+
//
354+
// Note that neither of these changes requires any
355+
// change at runtime. Eventually this will be
356+
// generalized.
357+
//
358+
// We always upcast when we can because of reason
359+
// #2 (region bounds).
360+
if data_a.bounds.builtin_bounds.is_superset(&data_b.bounds.builtin_bounds) {
361+
// construct a type `a1` which is a version of
362+
// `a` using the upcast bounds from `b`
351363
let bounds_a1 = ty::ExistentialBounds {
352-
region_bound: data_a.bounds.region_bound,
364+
// From type b
365+
region_bound: data_b.bounds.region_bound,
353366
builtin_bounds: data_b.bounds.builtin_bounds,
367+
368+
// From type a
354369
projection_bounds: data_a.bounds.projection_bounds.clone(),
355370
};
356371
let ty_a1 = ty::mk_trait(tcx, data_a.principal.clone(), bounds_a1);
357-
match self.fcx.infcx().try(|_| self.subtype(ty_a1, ty_b)) {
372+
373+
// relate `a1` to `b`
374+
let result = self.fcx.infcx().try(|_| {
375+
// it's ok to upcast from Foo+'a to Foo+'b so long as 'a : 'b
376+
try!(self.outlives(data_a.bounds.region_bound,
377+
data_b.bounds.region_bound));
378+
self.subtype(ty_a1, ty_b)
379+
});
380+
381+
// if that was successful, we have a coercion
382+
match result {
358383
Ok(_) => Some((ty_b, ty::UnsizeUpcast(ty_b))),
359384
Err(_) => None,
360385
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
trait Dummy { fn dummy(&self); }
12+
13+
fn foo1<'a:'b,'b>(x: &'a mut (Dummy+'a)) -> &'b mut (Dummy+'b) {
14+
// Here, we are able to coerce
15+
x
16+
}
17+
18+
fn foo2<'a:'b,'b>(x: &'b mut (Dummy+'a)) -> &'b mut (Dummy+'b) {
19+
// Here, we are able to coerce
20+
x
21+
}
22+
23+
fn foo3<'a,'b>(x: &'a mut Dummy) -> &'b mut Dummy {
24+
// Without knowing 'a:'b, we can't coerce
25+
x //~ ERROR mismatched types
26+
//~^ ERROR cannot infer
27+
}
28+
29+
struct Wrapper<T>(T);
30+
fn foo4<'a:'b,'b>(x: Wrapper<&'a mut Dummy>) -> Wrapper<&'b mut Dummy> {
31+
// We can't coerce because it is packed in `Wrapper`
32+
x //~ ERROR mismatched types
33+
}
34+
35+
fn main() {}

0 commit comments

Comments
 (0)