584 lines
23 KiB
Rust
584 lines
23 KiB
Rust
// Copyright 2012 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.
|
|
|
|
//! # Type Coercion
|
|
//!
|
|
//! Under certain circumstances we will coerce from one type to another,
|
|
//! for example by auto-borrowing. This occurs in situations where the
|
|
//! compiler has a firm 'expected type' that was supplied from the user,
|
|
//! and where the actual type is similar to that expected type in purpose
|
|
//! but not in representation (so actual subtyping is inappropriate).
|
|
//!
|
|
//! ## Reborrowing
|
|
//!
|
|
//! Note that if we are expecting a reference, we will *reborrow*
|
|
//! even if the argument provided was already a reference. This is
|
|
//! useful for freezing mut/const things (that is, when the expected is &T
|
|
//! but you have &const T or &mut T) and also for avoiding the linearity
|
|
//! of mut things (when the expected is &mut T and you have &mut T). See
|
|
//! the various `src/test/run-pass/coerce-reborrow-*.rs` tests for
|
|
//! examples of where this is useful.
|
|
//!
|
|
//! ## Subtle note
|
|
//!
|
|
//! When deciding what type coercions to consider, we do not attempt to
|
|
//! resolve any type variables we may encounter. This is because `b`
|
|
//! represents the expected type "as the user wrote it", meaning that if
|
|
//! the user defined a generic function like
|
|
//!
|
|
//! fn foo<A>(a: A, b: A) { ... }
|
|
//!
|
|
//! and then we wrote `foo(&1, @2)`, we will not auto-borrow
|
|
//! either argument. In older code we went to some lengths to
|
|
//! resolve the `b` variable, which could mean that we'd
|
|
//! auto-borrow later arguments but not earlier ones, which
|
|
//! seems very confusing.
|
|
//!
|
|
//! ## Subtler note
|
|
//!
|
|
//! However, right now, if the user manually specifies the
|
|
//! values for the type variables, as so:
|
|
//!
|
|
//! foo::<&int>(@1, @2)
|
|
//!
|
|
//! then we *will* auto-borrow, because we can't distinguish this from a
|
|
//! function that declared `&int`. This is inconsistent but it's easiest
|
|
//! at the moment. The right thing to do, I think, is to consider the
|
|
//! *unsubstituted* type when deciding whether to auto-borrow, but the
|
|
//! *substituted* type when considering the bounds and so forth. But most
|
|
//! of our methods don't give access to the unsubstituted type, and
|
|
//! rightly so because they'd be error-prone. So maybe the thing to do is
|
|
//! to actually determine the kind of coercions that should occur
|
|
//! separately and pass them in. Or maybe it's ok as is. Anyway, it's
|
|
//! 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 super::{CoerceResult, Coercion};
|
|
use super::combine::{CombineFields, Combine};
|
|
use super::sub::Sub;
|
|
|
|
use middle::subst;
|
|
use middle::ty::{AutoPtr, AutoDerefRef, AdjustDerefRef, AutoUnsize, AutoUnsafe};
|
|
use middle::ty::{mt};
|
|
use middle::ty::{mod, Ty};
|
|
use util::ppaux;
|
|
use util::ppaux::Repr;
|
|
|
|
use syntax::abi;
|
|
use syntax::ast;
|
|
|
|
// Note: Coerce is not actually a combiner, in that it does not
|
|
// conform to the same interface, though it performs a similar
|
|
// function.
|
|
pub struct Coerce<'f, 'tcx: 'f>(pub CombineFields<'f, 'tcx>);
|
|
|
|
impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|
pub fn get_ref<'a>(&'a self) -> &'a CombineFields<'f, 'tcx> {
|
|
let Coerce(ref v) = *self; v
|
|
}
|
|
|
|
fn tcx(&self) -> &ty::ctxt<'tcx> {
|
|
self.get_ref().infcx.tcx
|
|
}
|
|
|
|
pub fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
|
debug!("Coerce.tys({} => {})",
|
|
a.repr(self.tcx()),
|
|
b.repr(self.tcx()));
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Examine the supertype and consider auto-borrowing.
|
|
//
|
|
// Note: does not attempt to resolve type variables we encounter.
|
|
// See above for details.
|
|
match b.sty {
|
|
ty::ty_ptr(mt_b) => {
|
|
match mt_b.ty.sty {
|
|
ty::ty_str => {
|
|
return self.unpack_actual_value(a, |a| {
|
|
self.coerce_unsafe_ptr(a, b, ast::MutImmutable)
|
|
});
|
|
}
|
|
|
|
ty::ty_trait(..) => {
|
|
let result = self.unpack_actual_value(a, |a| {
|
|
self.coerce_unsafe_object(a, b, mt_b.mutbl)
|
|
});
|
|
|
|
match result {
|
|
Ok(t) => return Ok(t),
|
|
Err(..) => {}
|
|
}
|
|
}
|
|
|
|
_ => {
|
|
return self.unpack_actual_value(a, |a| {
|
|
self.coerce_unsafe_ptr(a, b, mt_b.mutbl)
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
ty::ty_rptr(_, mt_b) => {
|
|
match mt_b.ty.sty {
|
|
ty::ty_str => {
|
|
return self.unpack_actual_value(a, |a| {
|
|
self.coerce_borrowed_pointer(a, b, ast::MutImmutable)
|
|
});
|
|
}
|
|
|
|
ty::ty_trait(..) => {
|
|
let result = self.unpack_actual_value(a, |a| {
|
|
self.coerce_borrowed_object(a, b, mt_b.mutbl)
|
|
});
|
|
|
|
match result {
|
|
Ok(t) => return Ok(t),
|
|
Err(..) => {}
|
|
}
|
|
}
|
|
|
|
_ => {
|
|
return self.unpack_actual_value(a, |a| {
|
|
self.coerce_borrowed_pointer(a, b, mt_b.mutbl)
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
ty::ty_closure(box ty::ClosureTy {
|
|
store: ty::RegionTraitStore(..),
|
|
..
|
|
}) => {
|
|
return self.unpack_actual_value(a, |a| {
|
|
self.coerce_borrowed_fn(a, b)
|
|
});
|
|
}
|
|
|
|
_ => {}
|
|
}
|
|
|
|
self.unpack_actual_value(a, |a| {
|
|
match a.sty {
|
|
ty::ty_bare_fn(Some(a_def_id), 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_def_id, a_f, b)
|
|
}
|
|
_ => {
|
|
// Otherwise, just use subtyping rules.
|
|
self.subtype(a, b)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
|
match Sub(self.get_ref().clone()).tys(a, b) {
|
|
Ok(_) => Ok(None), // No coercion required.
|
|
Err(ref e) => Err(*e)
|
|
}
|
|
}
|
|
|
|
pub fn unpack_actual_value<T, F>(&self, a: Ty<'tcx>, f: F) -> T where
|
|
F: FnOnce(Ty<'tcx>) -> T,
|
|
{
|
|
f(self.get_ref().infcx.shallow_resolve(a))
|
|
}
|
|
|
|
// ~T -> &T or &mut T -> &T (including where T = [U] or str)
|
|
pub fn coerce_borrowed_pointer(&self,
|
|
a: Ty<'tcx>,
|
|
b: Ty<'tcx>,
|
|
mutbl_b: ast::Mutability)
|
|
-> CoerceResult<'tcx> {
|
|
debug!("coerce_borrowed_pointer(a={}, b={})",
|
|
a.repr(self.tcx()),
|
|
b.repr(self.tcx()));
|
|
|
|
// If we have a parameter of type `&M T_a` and the value
|
|
// provided is `expr`, we will be adding an implicit borrow,
|
|
// meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore,
|
|
// to type check, we will construct the type that `&M*expr` would
|
|
// yield.
|
|
|
|
let sub = Sub(self.get_ref().clone());
|
|
let coercion = Coercion(self.get_ref().trace.clone());
|
|
let r_borrow = self.get_ref().infcx.next_region_var(coercion);
|
|
|
|
let inner_ty = match a.sty {
|
|
ty::ty_uniq(_) => return Err(ty::terr_mismatch),
|
|
ty::ty_rptr(_, mt_a) => mt_a.ty,
|
|
_ => {
|
|
return self.subtype(a, b);
|
|
}
|
|
};
|
|
|
|
let a_borrowed = ty::mk_rptr(self.tcx(),
|
|
self.tcx().mk_region(r_borrow),
|
|
mt {ty: inner_ty, mutbl: mutbl_b});
|
|
try!(sub.tys(a_borrowed, b));
|
|
|
|
Ok(Some(AdjustDerefRef(AutoDerefRef {
|
|
autoderefs: 1,
|
|
autoref: Some(AutoPtr(r_borrow, mutbl_b, None))
|
|
})))
|
|
}
|
|
|
|
|
|
// &[T, ..n] or &mut [T, ..n] -> &[T]
|
|
// or &mut [T, ..n] -> &mut [T]
|
|
// or &Concrete -> &Trait, etc.
|
|
fn coerce_unsized(&self,
|
|
a: Ty<'tcx>,
|
|
b: Ty<'tcx>)
|
|
-> CoerceResult<'tcx> {
|
|
debug!("coerce_unsized(a={}, b={})",
|
|
a.repr(self.tcx()),
|
|
b.repr(self.tcx()));
|
|
|
|
// Note, we want to avoid unnecessary unsizing. We don't want to coerce to
|
|
// a DST unless we have to. This currently comes out in the wash since
|
|
// we can't unify [T] with U. But to properly support DST, we need to allow
|
|
// that, at which point we will need extra checks on b here.
|
|
|
|
let sub = Sub(self.get_ref().clone());
|
|
|
|
match (&a.sty, &b.sty) {
|
|
(&ty::ty_rptr(_, ty::mt{ty: t_a, mutbl: mutbl_a}), &ty::ty_rptr(_, mt_b)) => {
|
|
self.unpack_actual_value(t_a, |a| {
|
|
match self.unsize_ty(t_a, a, mt_b.ty) {
|
|
Some((ty, kind)) => {
|
|
if !can_coerce_mutbls(mutbl_a, mt_b.mutbl) {
|
|
return Err(ty::terr_mutability);
|
|
}
|
|
|
|
let coercion = Coercion(self.get_ref().trace.clone());
|
|
let r_borrow = self.get_ref().infcx.next_region_var(coercion);
|
|
let ty = ty::mk_rptr(self.tcx(),
|
|
self.tcx().mk_region(r_borrow),
|
|
ty::mt{ty: ty, mutbl: mt_b.mutbl});
|
|
try!(self.get_ref().infcx.try(|_| sub.tys(ty, b)));
|
|
debug!("Success, coerced with AutoDerefRef(1, \
|
|
AutoPtr(AutoUnsize({})))", kind);
|
|
Ok(Some(AdjustDerefRef(AutoDerefRef {
|
|
autoderefs: 1,
|
|
autoref: Some(ty::AutoPtr(r_borrow, mt_b.mutbl,
|
|
Some(box AutoUnsize(kind))))
|
|
})))
|
|
}
|
|
_ => Err(ty::terr_mismatch)
|
|
}
|
|
})
|
|
}
|
|
(&ty::ty_rptr(_, ty::mt{ty: t_a, mutbl: mutbl_a}), &ty::ty_ptr(mt_b)) => {
|
|
self.unpack_actual_value(t_a, |a| {
|
|
match self.unsize_ty(t_a, a, mt_b.ty) {
|
|
Some((ty, kind)) => {
|
|
if !can_coerce_mutbls(mutbl_a, mt_b.mutbl) {
|
|
return Err(ty::terr_mutability);
|
|
}
|
|
|
|
let ty = ty::mk_ptr(self.tcx(),
|
|
ty::mt{ty: ty, mutbl: mt_b.mutbl});
|
|
try!(self.get_ref().infcx.try(|_| sub.tys(ty, b)));
|
|
debug!("Success, coerced with AutoDerefRef(1, \
|
|
AutoPtr(AutoUnsize({})))", kind);
|
|
Ok(Some(AdjustDerefRef(AutoDerefRef {
|
|
autoderefs: 1,
|
|
autoref: Some(ty::AutoUnsafe(mt_b.mutbl,
|
|
Some(box AutoUnsize(kind))))
|
|
})))
|
|
}
|
|
_ => Err(ty::terr_mismatch)
|
|
}
|
|
})
|
|
}
|
|
(&ty::ty_uniq(t_a), &ty::ty_uniq(t_b)) => {
|
|
self.unpack_actual_value(t_a, |a| {
|
|
match self.unsize_ty(t_a, a, t_b) {
|
|
Some((ty, kind)) => {
|
|
let ty = ty::mk_uniq(self.tcx(), ty);
|
|
try!(self.get_ref().infcx.try(|_| sub.tys(ty, b)));
|
|
debug!("Success, coerced with AutoDerefRef(1, \
|
|
AutoUnsizeUniq({}))", kind);
|
|
Ok(Some(AdjustDerefRef(AutoDerefRef {
|
|
autoderefs: 1,
|
|
autoref: Some(ty::AutoUnsizeUniq(kind))
|
|
})))
|
|
}
|
|
_ => Err(ty::terr_mismatch)
|
|
}
|
|
})
|
|
}
|
|
_ => Err(ty::terr_mismatch)
|
|
}
|
|
}
|
|
|
|
// Takes a type and returns an unsized version along with the adjustment
|
|
// performed to unsize it.
|
|
// E.g., `[T, ..n]` -> `([T], UnsizeLength(n))`
|
|
fn unsize_ty(&self,
|
|
ty_a: Ty<'tcx>,
|
|
a: Ty<'tcx>,
|
|
ty_b: Ty<'tcx>)
|
|
-> Option<(Ty<'tcx>, ty::UnsizeKind<'tcx>)> {
|
|
debug!("unsize_ty(a={}, ty_b={})", a, ty_b.repr(self.tcx()));
|
|
|
|
let tcx = self.tcx();
|
|
|
|
self.unpack_actual_value(ty_b, |b|
|
|
match (&a.sty, &b.sty) {
|
|
(&ty::ty_vec(t_a, Some(len)), &ty::ty_vec(_, None)) => {
|
|
let ty = ty::mk_vec(tcx, t_a, None);
|
|
Some((ty, ty::UnsizeLength(len)))
|
|
}
|
|
(&ty::ty_trait(..), &ty::ty_trait(..)) => {
|
|
None
|
|
}
|
|
(_, &ty::ty_trait(box ty::TyTrait { ref principal, ref bounds })) => {
|
|
// FIXME what is the purpose of `ty`?
|
|
let ty = ty::mk_trait(tcx, principal.clone(), bounds.clone());
|
|
Some((ty, ty::UnsizeVtable(ty::TyTrait { principal: principal.clone(),
|
|
bounds: bounds.clone() },
|
|
ty_a)))
|
|
}
|
|
(&ty::ty_struct(did_a, substs_a), &ty::ty_struct(did_b, substs_b))
|
|
if did_a == did_b => {
|
|
debug!("unsizing a struct");
|
|
// Try unsizing each type param in turn to see if we end up with ty_b.
|
|
let ty_substs_a = substs_a.types.get_slice(subst::TypeSpace);
|
|
let ty_substs_b = substs_b.types.get_slice(subst::TypeSpace);
|
|
assert!(ty_substs_a.len() == ty_substs_b.len());
|
|
|
|
let sub = Sub(self.get_ref().clone());
|
|
|
|
let mut result = None;
|
|
let mut tps = ty_substs_a.iter().zip(ty_substs_b.iter()).enumerate();
|
|
for (i, (tp_a, tp_b)) in tps {
|
|
if self.get_ref().infcx.try(|_| sub.tys(*tp_a, *tp_b)).is_ok() {
|
|
continue;
|
|
}
|
|
match
|
|
self.unpack_actual_value(
|
|
*tp_a,
|
|
|tp| self.unsize_ty(*tp_a, tp, *tp_b))
|
|
{
|
|
Some((new_tp, k)) => {
|
|
// Check that the whole types match.
|
|
let mut new_substs = substs_a.clone();
|
|
new_substs.types.get_mut_slice(subst::TypeSpace)[i] = new_tp;
|
|
let ty = ty::mk_struct(tcx, did_a, tcx.mk_substs(new_substs));
|
|
if self.get_ref().infcx.try(|_| sub.tys(ty, ty_b)).is_err() {
|
|
debug!("Unsized type parameter '{}', but still \
|
|
could not match types {} and {}",
|
|
ppaux::ty_to_string(tcx, *tp_a),
|
|
ppaux::ty_to_string(tcx, ty),
|
|
ppaux::ty_to_string(tcx, ty_b));
|
|
// We can only unsize a single type parameter, so
|
|
// if we unsize one and it doesn't give us the
|
|
// type we want, then we won't succeed later.
|
|
break;
|
|
}
|
|
|
|
result = Some((ty, ty::UnsizeStruct(box k, i)));
|
|
break;
|
|
}
|
|
None => {}
|
|
}
|
|
}
|
|
result
|
|
}
|
|
_ => None
|
|
}
|
|
)
|
|
}
|
|
|
|
fn coerce_borrowed_object(&self,
|
|
a: Ty<'tcx>,
|
|
b: Ty<'tcx>,
|
|
b_mutbl: ast::Mutability) -> CoerceResult<'tcx>
|
|
{
|
|
let tcx = self.tcx();
|
|
|
|
debug!("coerce_borrowed_object(a={}, b={}, b_mutbl={})",
|
|
a.repr(tcx),
|
|
b.repr(tcx), b_mutbl);
|
|
|
|
let coercion = Coercion(self.get_ref().trace.clone());
|
|
let r_a = self.get_ref().infcx.next_region_var(coercion);
|
|
|
|
self.coerce_object(a, b, b_mutbl,
|
|
|tr| ty::mk_rptr(tcx, tcx.mk_region(r_a),
|
|
ty::mt{ mutbl: b_mutbl, ty: tr }),
|
|
|| AutoPtr(r_a, b_mutbl, None))
|
|
}
|
|
|
|
fn coerce_unsafe_object(&self,
|
|
a: Ty<'tcx>,
|
|
b: Ty<'tcx>,
|
|
b_mutbl: ast::Mutability) -> CoerceResult<'tcx>
|
|
{
|
|
let tcx = self.tcx();
|
|
|
|
debug!("coerce_unsafe_object(a={}, b={}, b_mutbl={})",
|
|
a.repr(tcx),
|
|
b.repr(tcx), b_mutbl);
|
|
|
|
self.coerce_object(a, b, b_mutbl,
|
|
|tr| ty::mk_ptr(tcx, ty::mt{ mutbl: b_mutbl, ty: tr }),
|
|
|| AutoUnsafe(b_mutbl, None))
|
|
}
|
|
|
|
fn coerce_object<F, G>(&self,
|
|
a: Ty<'tcx>,
|
|
b: Ty<'tcx>,
|
|
b_mutbl: ast::Mutability,
|
|
mk_ty: F,
|
|
mk_adjust: G) -> CoerceResult<'tcx> where
|
|
F: FnOnce(Ty<'tcx>) -> Ty<'tcx>,
|
|
G: FnOnce() -> ty::AutoRef<'tcx>,
|
|
{
|
|
let tcx = self.tcx();
|
|
|
|
match a.sty {
|
|
ty::ty_rptr(_, ty::mt{ty, mutbl}) => match ty.sty {
|
|
ty::ty_trait(box ty::TyTrait { ref principal, ref bounds }) => {
|
|
debug!("mutbl={} b_mutbl={}", mutbl, b_mutbl);
|
|
let tr = ty::mk_trait(tcx, principal.clone(), bounds.clone());
|
|
try!(self.subtype(mk_ty(tr), b));
|
|
Ok(Some(AdjustDerefRef(AutoDerefRef {
|
|
autoderefs: 1,
|
|
autoref: Some(mk_adjust())
|
|
})))
|
|
}
|
|
_ => {
|
|
self.subtype(a, b)
|
|
}
|
|
},
|
|
_ => {
|
|
self.subtype(a, b)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn coerce_borrowed_fn(&self,
|
|
a: Ty<'tcx>,
|
|
b: Ty<'tcx>)
|
|
-> CoerceResult<'tcx> {
|
|
debug!("coerce_borrowed_fn(a={}, b={})",
|
|
a.repr(self.tcx()),
|
|
b.repr(self.tcx()));
|
|
|
|
match a.sty {
|
|
ty::ty_bare_fn(Some(a_def_id), f) => {
|
|
self.coerce_from_fn_item(a, a_def_id, f, b)
|
|
}
|
|
_ => {
|
|
self.subtype(a, b)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn coerce_from_fn_item(&self,
|
|
a: Ty<'tcx>,
|
|
fn_def_id_a: ast::DefId,
|
|
fn_ty_a: &'tcx ty::BareFnTy<'tcx>,
|
|
b: Ty<'tcx>)
|
|
-> CoerceResult<'tcx> {
|
|
/*!
|
|
* Attempts to coerce from the type of a Rust function item
|
|
* into a closure or a `proc`.
|
|
*/
|
|
|
|
self.unpack_actual_value(b, |b| {
|
|
debug!("coerce_from_fn_item(a={}, b={})",
|
|
a.repr(self.tcx()), b.repr(self.tcx()));
|
|
|
|
match b.sty {
|
|
ty::ty_closure(ref f) => {
|
|
if fn_ty_a.abi != abi::Rust || fn_ty_a.unsafety != ast::Unsafety::Normal {
|
|
return self.subtype(a, b);
|
|
}
|
|
|
|
let fn_ty_b = (*f).clone();
|
|
let adj = ty::AdjustAddEnv(fn_def_id_a, fn_ty_b.store);
|
|
let a_closure = ty::mk_closure(self.tcx(),
|
|
ty::ClosureTy {
|
|
sig: fn_ty_a.sig.clone(),
|
|
.. *fn_ty_b
|
|
});
|
|
try!(self.subtype(a_closure, b));
|
|
Ok(Some(adj))
|
|
}
|
|
ty::ty_bare_fn(None, _) => {
|
|
let a_fn_pointer = ty::mk_bare_fn(self.tcx(), None, fn_ty_a);
|
|
try!(self.subtype(a_fn_pointer, b));
|
|
Ok(Some(ty::AdjustReifyFnPointer(fn_def_id_a)))
|
|
}
|
|
_ => {
|
|
return self.subtype(a, b)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn coerce_unsafe_ptr(&self,
|
|
a: Ty<'tcx>,
|
|
b: Ty<'tcx>,
|
|
mutbl_b: ast::Mutability)
|
|
-> CoerceResult<'tcx> {
|
|
debug!("coerce_unsafe_ptr(a={}, b={})",
|
|
a.repr(self.tcx()),
|
|
b.repr(self.tcx()));
|
|
|
|
let mt_a = match a.sty {
|
|
ty::ty_rptr(_, mt) | ty::ty_ptr(mt) => mt,
|
|
_ => {
|
|
return self.subtype(a, b);
|
|
}
|
|
};
|
|
|
|
// Check that the types which they point at are compatible.
|
|
let a_unsafe = ty::mk_ptr(self.tcx(), ty::mt{ mutbl: mutbl_b, ty: mt_a.ty });
|
|
try!(self.subtype(a_unsafe, b));
|
|
if !can_coerce_mutbls(mt_a.mutbl, mutbl_b) {
|
|
return Err(ty::terr_mutability);
|
|
}
|
|
|
|
// Although references and unsafe ptrs have the same
|
|
// representation, we still register an AutoDerefRef so that
|
|
// regionck knows that the region for `a` must be valid here.
|
|
Ok(Some(AdjustDerefRef(AutoDerefRef {
|
|
autoderefs: 1,
|
|
autoref: Some(ty::AutoUnsafe(mutbl_b, None))
|
|
})))
|
|
}
|
|
}
|
|
|
|
fn can_coerce_mutbls(from_mutbl: ast::Mutability,
|
|
to_mutbl: ast::Mutability)
|
|
-> bool {
|
|
match (from_mutbl, to_mutbl) {
|
|
(ast::MutMutable, ast::MutMutable) => true,
|
|
(ast::MutImmutable, ast::MutImmutable) => true,
|
|
(ast::MutMutable, ast::MutImmutable) => true,
|
|
(ast::MutImmutable, ast::MutMutable) => false,
|
|
}
|
|
}
|