From 50ea6f6886f3315ad93b035e918b8a4165325132 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 19 Mar 2015 07:16:40 -0400 Subject: [PATCH] Remove incorrect subtyping for `&mut Trait` and introduce coercion for `&mut (Trait+'a)` to `&mut (Trait+'b)` if `'a:'b`. Fixes #14985. --- src/librustc/middle/infer/combine.rs | 13 +----- src/librustc_typeck/check/coercion.rs | 45 ++++++++++++++----- .../regions-trait-object-subtyping.rs | 35 +++++++++++++++ 3 files changed, 71 insertions(+), 22 deletions(-) create mode 100644 src/test/compile-fail/regions-trait-object-subtyping.rs diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs index 94c9699e30cc4..930e95d1f939a 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 a82ebcd9175fa..f731507ba906d 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(&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 0000000000000..8d05cb67e77b1 --- /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 or the MIT license +// , 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); +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() {}