about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2015-03-23 15:08:13 -0700
committerAlex Crichton <alex@alexcrichton.com>2015-03-23 15:08:13 -0700
commit0678f0b10c31e033a071db69d2e5175f1aa6b621 (patch)
tree1cf4fae8a0dfeb2633504a3a678ba92dc8a52507
parent4f7437647f6fd55171c61f35466b9266c35c4cfa (diff)
parent50ea6f6886f3315ad93b035e918b8a4165325132 (diff)
downloadrust-0678f0b10c31e033a071db69d2e5175f1aa6b621.tar.gz
rust-0678f0b10c31e033a071db69d2e5175f1aa6b621.zip
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
-rw-r--r--src/librustc/middle/infer/combine.rs13
-rw-r--r--src/librustc_typeck/check/coercion.rs45
-rw-r--r--src/test/compile-fail/regions-trait-object-subtyping.rs35
3 files changed, 71 insertions, 22 deletions
diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs
index 94c9699e30c..930e95d1f93 100644
--- a/src/librustc/middle/infer/combine.rs
+++ b/src/librustc/middle/infer/combine.rs
@@ -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))
         }
 
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index a82ebcd9175..f731507ba90 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -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,
                         }
diff --git a/src/test/compile-fail/regions-trait-object-subtyping.rs b/src/test/compile-fail/regions-trait-object-subtyping.rs
new file mode 100644
index 00000000000..8d05cb67e77
--- /dev/null
+++ b/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() {}