diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 6963e25cd5b1b..39ab434690a8e 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -60,28 +60,44 @@ //! sort of a minor point so I've opted to leave it for later---after all //! we may want to adjust precisely when coercions occur. -use check::{autoderef, FnCtxt, LvaluePreference, UnresolvedTypeAction}; +use check::{autoderef, impl_self_ty, FnCtxt, LvaluePreference, UnresolvedTypeAction}; use middle::infer::{self, Coercion}; +use middle::subst::Substs; use middle::traits::{self, ObligationCause}; use middle::traits::{predicate_for_trait_def, report_selection_error}; use middle::ty::{AutoDerefRef, AdjustDerefRef}; use middle::ty::{self, TypeAndMut, Ty, TypeError}; use middle::ty_relate::RelateResult; -use util::common::indent; -use std::cell::RefCell; use std::collections::VecDeque; use syntax::ast; struct Coerce<'a, 'tcx: 'a> { fcx: &'a FnCtxt<'a, 'tcx>, - origin: infer::TypeOrigin, - unsizing_obligations: RefCell>>, + origin: infer::TypeOrigin } type CoerceResult<'tcx> = RelateResult<'tcx, Option>>; +/// The result of an attempt at coercing a Source type to a +/// Target type via unsizing (`Source: CoerceUnsized`). +/// If successful, all `CoerceUnsized` and `Unsized` obligations were selected. +/// Other obligations, such as `T: Trait` for `&T -> &Trait`, are provided +/// alongside the adjustment, to be enforced later. +type CoerceUnsizedResult<'tcx> = Result<(AutoDerefRef<'tcx>, + Vec>), + CoerceUnsizedError>; + +#[derive(PartialEq, Eq)] +enum CoerceUnsizedError { + /// Source definitely does not implement `CoerceUnsized`. + Unapplicable, + + /// Source might implement `CoerceUnsized`. + Ambiguous, +} + impl<'f, 'tcx> Coerce<'f, 'tcx> { fn tcx(&self) -> &ty::ctxt<'tcx> { self.fcx.tcx() @@ -92,27 +108,21 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Ok(None) // No coercion required. } - fn unpack_actual_value(&self, a: Ty<'tcx>, f: F) -> T where - F: FnOnce(Ty<'tcx>) -> T, - { - f(self.fcx.infcx().shallow_resolve(a)) - } - fn coerce(&self, expr_a: &ast::Expr, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { - debug!("Coerce.tys({:?} => {:?})", - a, - b); + let a = self.fcx.infcx().shallow_resolve(a); + debug!("coerce({:?} => {:?})", a, b); // Consider coercing the subtype to a DST - let unsize = self.unpack_actual_value(a, |a| { - self.coerce_unsized(a, b) - }); - if unsize.is_ok() { - return unsize; + let unsize = self.fcx.infcx().commit_if_ok(|_| self.coerce_unsized(a, b)); + if let Ok((adjustment, leftover_predicates)) = unsize { + for obligation in leftover_predicates { + self.fcx.register_predicate(obligation); + } + return Ok(Some(AdjustDerefRef(adjustment))); } // Examine the supertype and consider auto-borrowing. @@ -121,39 +131,107 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // See above for details. match b.sty { ty::TyRawPtr(mt_b) => { - return self.unpack_actual_value(a, |a| { - self.coerce_unsafe_ptr(a, b, mt_b.mutbl) - }); + return self.coerce_unsafe_ptr(a, b, mt_b.mutbl); } ty::TyRef(_, mt_b) => { - return self.unpack_actual_value(a, |a| { - self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl) - }); + return self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl); } _ => {} } - self.unpack_actual_value(a, |a| { - match a.sty { - ty::TyBareFn(Some(_), a_f) => { - // Function items are coercible to any closure - // type; function pointers are not (that would - // require double indirection). - self.coerce_from_fn_item(a, a_f, b) - } - ty::TyBareFn(None, a_f) => { - // We permit coercion of fn pointers to drop the - // unsafe qualifier. - self.coerce_from_fn_pointer(a, a_f, b) - } - _ => { - // Otherwise, just use subtyping rules. - self.subtype(a, b) + let b = self.fcx.infcx().shallow_resolve(b); + match a.sty { + ty::TyBareFn(Some(_), a_f) => { + // Function items are coercible to any closure + // type; function pointers are not (that would + // require double indirection). + return self.coerce_from_fn_item(a, a_f, b); + } + ty::TyBareFn(None, a_f) => { + // We permit coercion of fn pointers to drop the + // unsafe qualifier. + return self.coerce_from_fn_pointer(a, a_f, b); + } + _ => {} + } + + // Attempt to generalize the expected type in hopes of an unsizing + // coercion, where an intermediary stop-gap is usually necessary. + // This is the case with trait method calls where the returned type + // was not inferred, e.g. `Make::make(x: T): Box`, if `Make` + // has many implementations. Unsizing coercion will be ambiguous + // and subtyping would result in a selection failure, if `Box` + // does not implement `Make`, but `Box` does. The stop-gap fix + // is `Make::make(x: T): Box: Box`. + // In that same case, the following generalization will attempt to + // apply `Box<_>` to the otherwise unconstrained `Make::make` return + // type and trigger selection, hoping to get the unambiguous source + // type `Box` for the coercion to `Box`. + // Subtyping rules are used if the generalization and second attempt + // at coercions through unsizing do not apply. + + // The first unsizing coercion must have failed due to ambiguity. + if unsize.err() != Some(CoerceUnsizedError::Ambiguous) { + return self.subtype(a, b); + } + + // The target type needs to be a structure or Box. + // The only other types that implement CoerceUnsized are + // references and pointers and those have multiple forms, + // such as `*mut T -> *const Trait`. + match b.sty { + ty::TyBox(_) | ty::TyStruct(..) => {} + _ => return self.subtype(a, b) + } + + // Construct a `Target: CoerceUnsized` predicate. + let trait_predicate = ty::Binder(ty::TraitRef { + def_id: self.tcx().lang_items.coerce_unsized_trait().unwrap(), + substs: self.tcx().mk_substs(Substs::new_trait(vec![b], vec![], b)) + }).to_poly_trait_predicate(); + + // Select `Target: CoerceUnsized`. + let mut selcx = traits::SelectionContext::new(self.fcx.infcx()); + let cause = ObligationCause::misc(self.origin.span(), self.fcx.body_id); + let obligation = traits::Obligation::new(cause, trait_predicate); + if let Ok(Some(traits::VtableImpl(i))) = selcx.select(&obligation) { + // There is a single applicable impl. If `Target = P`, then + // the `Self` of this impl is some kind of supertype of `P`, + // most likely `P forall T: Unsize`. + // This `Self` type, when all impl type parameters have been + // substituted with fresh inference variables (e.g. `P<_>`), + // will unify with the target type and all possible source types + // for a coercion. + // It can thus be used as a supertype of the source type, + // the generalized form that can allow fulfilling pending + // obligations and ultimately an unsizing coercion. + let success = self.fcx.infcx().commit_if_ok(|_| { + self.subtype(a, impl_self_ty(self.fcx, + self.origin.span(), + i.impl_def_id).ty) + }); + + if success.is_ok() { + // Select pending obligations to constrain the + // source type further, and resolve it again. + self.fcx.select_obligations_where_possible(); + let a = self.fcx.infcx().shallow_resolve(a); + + // Finally, attempt a coercion by unsizing again, + // now that the types are (hopefully) better known. + let unsize = self.fcx.infcx().commit_if_ok(|_| self.coerce_unsized(a, b)); + if let Ok((adjustment, leftover_predicates)) = unsize { + for obligation in leftover_predicates { + self.fcx.register_predicate(obligation); + } + return Ok(Some(AdjustDerefRef(adjustment))); } } - }) + } + + self.subtype(a, b) } /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. @@ -236,7 +314,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) - -> CoerceResult<'tcx> { + -> CoerceUnsizedResult<'tcx> { debug!("coerce_unsized(source={:?}, target={:?})", source, target); @@ -247,7 +325,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { (u, cu) } else { debug!("Missing Unsize or CoerceUnsized traits"); - return Err(TypeError::Mismatch); + return Err(CoerceUnsizedError::Unapplicable); }; // Note, we want to avoid unnecessary unsizing. We don't want to coerce to @@ -258,7 +336,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Handle reborrows before selecting `Source: CoerceUnsized`. let (source, reborrow) = match (&source.sty, &target.sty) { (&ty::TyRef(_, mt_a), &ty::TyRef(_, mt_b)) => { - try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl)); + if coerce_mutbls(mt_a.mutbl, mt_b.mutbl).is_err() { + return Err(CoerceUnsizedError::Unapplicable); + } let coercion = Coercion(self.origin.span()); let r_borrow = self.fcx.infcx().next_region_var(coercion); @@ -266,7 +346,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { (mt_a.ty, Some(ty::AutoPtr(region, mt_b.mutbl))) } (&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) => { - try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl)); + if coerce_mutbls(mt_a.mutbl, mt_b.mutbl).is_err() { + return Err(CoerceUnsizedError::Unapplicable); + } (mt_a.ty, Some(ty::AutoUnsafe(mt_b.mutbl))) } _ => (source, None) @@ -305,9 +387,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { }; match selcx.select(&obligation.with(trait_ref)) { // Uncertain or unimplemented. - Ok(None) | Err(traits::Unimplemented) => { - debug!("coerce_unsized: early return - can't prove obligation"); - return Err(TypeError::Mismatch); + Ok(None) => { + debug!("coerce_unsized: early return (Ambiguous)"); + return Err(CoerceUnsizedError::Ambiguous); + } + Err(traits::Unimplemented) => { + debug!("coerce_unsized: early return (Unapplicable)"); + return Err(CoerceUnsizedError::Unapplicable); } // Object safety violations or miscellaneous. @@ -326,17 +412,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } - let mut obligations = self.unsizing_obligations.borrow_mut(); - assert!(obligations.is_empty()); - *obligations = leftover_predicates; - let adjustment = AutoDerefRef { autoderefs: if reborrow.is_some() { 1 } else { 0 }, autoref: reborrow, unsize: Some(target) }; debug!("Success, coerced with {:?}", adjustment); - Ok(Some(AdjustDerefRef(adjustment))) + Ok((adjustment, leftover_predicates)) } fn coerce_from_fn_pointer(&self, @@ -350,22 +432,20 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { * into a closure or a `proc`. */ - self.unpack_actual_value(b, |b| { - debug!("coerce_from_fn_pointer(a={:?}, b={:?})", - a, b); + debug!("coerce_from_fn_pointer(a={:?}, b={:?})", + a, b); - if let ty::TyBareFn(None, fn_ty_b) = b.sty { - match (fn_ty_a.unsafety, fn_ty_b.unsafety) { - (ast::Unsafety::Normal, ast::Unsafety::Unsafe) => { - let unsafe_a = self.tcx().safe_to_unsafe_fn_ty(fn_ty_a); - try!(self.subtype(unsafe_a, b)); - return Ok(Some(ty::AdjustUnsafeFnPointer)); - } - _ => {} + if let ty::TyBareFn(None, fn_ty_b) = b.sty { + match (fn_ty_a.unsafety, fn_ty_b.unsafety) { + (ast::Unsafety::Normal, ast::Unsafety::Unsafe) => { + let unsafe_a = self.tcx().safe_to_unsafe_fn_ty(fn_ty_a); + try!(self.subtype(unsafe_a, b)); + return Ok(Some(ty::AdjustUnsafeFnPointer)); } + _ => {} } - self.subtype(a, b) - }) + } + self.subtype(a, b) } fn coerce_from_fn_item(&self, @@ -378,19 +458,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { * into a closure or a `proc`. */ - self.unpack_actual_value(b, |b| { - debug!("coerce_from_fn_item(a={:?}, b={:?})", - a, b); + debug!("coerce_from_fn_item(a={:?}, b={:?})", + a, b); - match b.sty { - ty::TyBareFn(None, _) => { - let a_fn_pointer = self.tcx().mk_fn(None, fn_ty_a); - try!(self.subtype(a_fn_pointer, b)); - Ok(Some(ty::AdjustReifyFnPointer)) - } - _ => self.subtype(a, b) + match b.sty { + ty::TyBareFn(None, _) => { + let a_fn_pointer = self.tcx().mk_fn(None, fn_ty_a); + try!(self.subtype(a_fn_pointer, b)); + Ok(Some(ty::AdjustReifyFnPointer)) } - }) + _ => self.subtype(a, b) + } } fn coerce_unsafe_ptr(&self, @@ -412,8 +490,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Check that the types which they point at are compatible. let a_unsafe = self.tcx().mk_ptr(ty::TypeAndMut{ mutbl: mutbl_b, ty: mt_a.ty }); - try!(self.subtype(a_unsafe, b)); - try!(coerce_mutbls(mt_a.mutbl, mutbl_b)); + try!(self.fcx.infcx().commit_if_ok(|_| { + try!(self.subtype(a_unsafe, b)); + coerce_mutbls(mt_a.mutbl, mutbl_b) + })); // Although references and unsafe ptrs have the same // representation, we still register an AutoDerefRef so that @@ -436,29 +516,12 @@ pub fn mk_assignty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { debug!("mk_assignty({:?} -> {:?})", a, b); - let mut unsizing_obligations = vec![]; - let adjustment = try!(indent(|| { - fcx.infcx().commit_if_ok(|_| { - let coerce = Coerce { - fcx: fcx, - origin: infer::ExprAssignable(expr.span), - unsizing_obligations: RefCell::new(vec![]) - }; - let adjustment = try!(coerce.coerce(expr, a, b)); - unsizing_obligations = coerce.unsizing_obligations.into_inner(); - Ok(adjustment) - }) - })); - - if let Some(AdjustDerefRef(auto)) = adjustment { - if auto.unsize.is_some() { - for obligation in unsizing_obligations { - fcx.register_predicate(obligation); - } - } - } + let coerce = Coerce { + fcx: fcx, + origin: infer::ExprAssignable(expr.span) + }; - if let Some(adjustment) = adjustment { + if let Some(adjustment) = try!(coerce.coerce(expr, a, b)) { debug!("Success, coerced with {:?}", adjustment); fcx.write_adjustment(expr.id, adjustment); } diff --git a/src/test/compile-fail/destructure-trait-ref.rs b/src/test/compile-fail/destructure-trait-ref.rs index 08db643df00e4..2284a744ffb23 100644 --- a/src/test/compile-fail/destructure-trait-ref.rs +++ b/src/test/compile-fail/destructure-trait-ref.rs @@ -35,7 +35,7 @@ fn main() { // n == m let &x = &1isize as &T; //~ ERROR type `&T` cannot be dereferenced let &&x = &(&1isize as &T); //~ ERROR type `&T` cannot be dereferenced - let box x = box 1isize as Box; //~ ERROR the trait `core::marker::Sized` is not implemented + let box x = box 1isize as Box; //~ ERROR type `Box` cannot be dereferenced // n > m let &&x = &1isize as &T; diff --git a/src/test/run-pass/coerce-box-protocol.rs b/src/test/run-pass/coerce-box-protocol.rs new file mode 100644 index 0000000000000..ede071081fcd4 --- /dev/null +++ b/src/test/run-pass/coerce-box-protocol.rs @@ -0,0 +1,134 @@ +// Copyright 2015 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. + +use std::rc::Rc; + +mod protocol { + use std::ptr; + + pub trait BoxPlace: Place { + fn make() -> Self; + } + + pub trait Place { + // NOTE(eddyb) The use of `&mut T` here is to force + // the LLVM `noalias` and `dereferenceable(sizeof(T))` + // attributes, which are required for eliding the copy + // and producing actual in-place initialization via RVO. + // Neither of those attributes are present on `*mut T`, + // but `&mut T` is not a great choice either, the proper + // way might be to add those attributes to `Unique`. + unsafe fn pointer(&mut self) -> &mut T; + } + + pub trait Boxed: Sized { + type P: Place; + fn write_and_fin(mut place: Self::P, + value: T) + -> Self { + unsafe { + ptr::write(place.pointer(), value); + Self::fin(place) + } + } + unsafe fn fin(filled: Self::P) -> Self; + } +} + +macro_rules! box_ { + ($x:expr) => { + ::protocol::Boxed::write_and_fin(::protocol::BoxPlace::make(), $x) + } +} + +// Hacky implementations of the box protocol for Box and Rc. +// They pass mem::uninitialized() to Box::new, and Rc::new, respectively, +// to allocate memory and will leak the allocation in case of unwinding. +mod boxed { + use std::mem; + use protocol; + + pub struct Place { + ptr: *mut T + } + + impl protocol::BoxPlace for Place { + fn make() -> Place { + unsafe { + Place { + ptr: mem::transmute(Box::new(mem::uninitialized::())) + } + } + } + } + + impl protocol::Place for Place { + unsafe fn pointer(&mut self) -> &mut T { &mut *self.ptr } + } + + impl protocol::Boxed for Box { + type P = Place; + unsafe fn fin(place: Place) -> Box { + mem::transmute(place.ptr) + } + } +} + +mod rc { + use std::mem; + use std::rc::Rc; + use protocol; + + pub struct Place { + rc_ptr: *mut (), + data_ptr: *mut T + } + + impl protocol::BoxPlace for Place { + fn make() -> Place { + unsafe { + let rc = Rc::new(mem::uninitialized::()); + Place { + data_ptr: &*rc as *const _ as *mut _, + rc_ptr: mem::transmute(rc) + } + } + } + } + + impl protocol::Place for Place { + unsafe fn pointer(&mut self) -> &mut T { + &mut *self.data_ptr + } + } + + impl protocol::Boxed for Rc { + type P = Place; + unsafe fn fin(place: Place) -> Rc { + mem::transmute(place.rc_ptr) + } + } +} + +fn main() { + let v = vec![1, 2, 3]; + + let bx: Box<_> = box_!(|| &v); + let rc: Rc<_> = box_!(|| &v); + + assert_eq!(bx(), &v); + assert_eq!(rc(), &v); + + let bx_trait: Box _> = box_!(|| &v); + let rc_trait: Rc _> = box_!(|| &v); + + assert_eq!(bx(), &v); + assert_eq!(rc(), &v); +}