coercion.rs 23.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// 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.

S
Steve Klabnik 已提交
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
//! # 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.
62

63
use check::{autoderef, FnCtxt, NoPreference, PreferMutLvalue, UnresolvedTypeAction};
64

65
use middle::infer::{self, CombineResult, Coercion, TypeTrace};
66 67
use middle::infer::combine::Combine;
use middle::infer::sub::Sub;
68
use middle::subst;
69
use middle::ty::{AutoPtr, AutoDerefRef, AdjustDerefRef, AutoUnsize, AutoUnsafe};
70
use middle::ty::{self, mt, Ty};
71
use util::common::indent;
N
Nick Cameron 已提交
72
use util::ppaux;
73
use util::ppaux::Repr;
74

75 76
use syntax::ast;

77 78 79 80 81
struct Coerce<'a, 'tcx: 'a> {
    fcx: &'a FnCtxt<'a, 'tcx>,
    trace: TypeTrace<'tcx>
}

82
type CoerceResult<'tcx> = CombineResult<'tcx, Option<ty::AutoAdjustment<'tcx>>>;
83

84
impl<'f, 'tcx> Coerce<'f, 'tcx> {
85 86
    fn tcx(&self) -> &ty::ctxt<'tcx> {
        self.fcx.tcx()
87 88
    }

89 90 91 92
    fn subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
        let sub = Sub(self.fcx.infcx().combine_fields(false, self.trace.clone()));
        try!(sub.tys(a, b));
        Ok(None) // No coercion required.
93 94
    }

95 96 97 98 99 100
    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(())
    }

101 102 103 104 105 106
    fn unpack_actual_value<T, F>(&self, a: Ty<'tcx>, f: F) -> T where
        F: FnOnce(Ty<'tcx>) -> T,
    {
        f(self.fcx.infcx().shallow_resolve(a))
    }

107 108 109 110 111
    fn coerce(&self,
              expr_a: &ast::Expr,
              a: Ty<'tcx>,
              b: Ty<'tcx>)
              -> CoerceResult<'tcx> {
112
        debug!("Coerce.tys({} => {})",
113 114
               a.repr(self.tcx()),
               b.repr(self.tcx()));
115

N
Nick Cameron 已提交
116
        // Consider coercing the subtype to a DST
117 118
        let unsize = self.unpack_actual_value(a, |a| {
            self.coerce_unsized(a, b)
N
Nick Cameron 已提交
119 120 121 122 123
        });
        if unsize.is_ok() {
            return unsize;
        }

124 125 126 127
        // Examine the supertype and consider auto-borrowing.
        //
        // Note: does not attempt to resolve type variables we encounter.
        // See above for details.
128
        match b.sty {
129
            ty::ty_ptr(mt_b) => {
130 131 132
                return self.unpack_actual_value(a, |a| {
                    self.coerce_unsafe_ptr(a, b, mt_b.mutbl)
                });
133 134
            }

N
Nick Cameron 已提交
135
            ty::ty_rptr(_, mt_b) => {
136
                return self.unpack_actual_value(a, |a| {
137
                    self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl)
138
                });
139 140
            }

141 142
            _ => {}
        }
143

144 145
        self.unpack_actual_value(a, |a| {
            match a.sty {
146
                ty::ty_bare_fn(Some(a_def_id), a_f) => {
147 148 149 150
                    // 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)
151
                }
152 153 154 155 156
                ty::ty_bare_fn(None, a_f) => {
                    // We permit coercion of fn pointers to drop the
                    // unsafe qualifier.
                    self.coerce_from_fn_pointer(a, a_f, b)
                }
157 158 159 160
                _ => {
                    // Otherwise, just use subtyping rules.
                    self.subtype(a, b)
                }
161
            }
162
        })
163 164
    }

165 166 167
    /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
    /// To match `A` with `B`, autoderef will be performed,
    /// calling `deref`/`deref_mut` where necessary.
168
    fn coerce_borrowed_pointer(&self,
169
                               expr_a: &ast::Expr,
170 171 172 173
                               a: Ty<'tcx>,
                               b: Ty<'tcx>,
                               mutbl_b: ast::Mutability)
                               -> CoerceResult<'tcx> {
174
        debug!("coerce_borrowed_pointer(a={}, b={})",
175 176
               a.repr(self.tcx()),
               b.repr(self.tcx()));
177

178 179 180 181 182 183
        // 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.

184
        match a.sty {
S
Seo Sanghyeon 已提交
185 186 187 188 189
            ty::ty_rptr(_, mt_a) => {
                if !can_coerce_mutbls(mt_a.mutbl, mutbl_b) {
                    return Err(ty::terr_mutability);
                }
            }
190
            _ => return self.subtype(a, b)
191
        }
192

193 194 195
        let coercion = Coercion(self.trace.clone());
        let r_borrow = self.fcx.infcx().next_region_var(coercion);
        let autoref = Some(AutoPtr(r_borrow, mutbl_b, None));
N
Nick Cameron 已提交
196

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
        let r_borrow = self.tcx().mk_region(r_borrow);
        let lvalue_pref = match mutbl_b {
            ast::MutMutable => PreferMutLvalue,
            ast::MutImmutable => NoPreference
        };
        let mut first_error = None;
        let (_, autoderefs, success) = autoderef(self.fcx,
                                                 expr_a.span,
                                                 a,
                                                 Some(expr_a),
                                                 UnresolvedTypeAction::Ignore,
                                                 lvalue_pref,
                                                 |inner_ty, autoderef| {
            if autoderef == 0 {
                // Don't let this pass, otherwise it would cause
                // &T to autoref to &&T.
                return None;
            }
            let ty = ty::mk_rptr(self.tcx(), r_borrow,
                                 mt {ty: inner_ty, mutbl: mutbl_b});
            if let Err(err) = self.fcx.infcx().try(|_| self.subtype(ty, b)) {
                if first_error.is_none() {
                    first_error = Some(err);
                }
                None
            } else {
                Some(())
            }
        });

        match success {
            Some(_) => {
                Ok(Some(AdjustDerefRef(AutoDerefRef {
                    autoderefs: autoderefs,
                    autoref: autoref
                })))
            }
            None => {
                // Return original error as if overloaded deref was never
                // attempted, to avoid irrelevant/confusing error messages.
                Err(first_error.expect("coerce_borrowed_pointer failed with no error?"))
            }
        }
240 241 242
    }


N
Nick Cameron 已提交
243 244 245 246
    // &[T, ..n] or &mut [T, ..n] -> &[T]
    // or &mut [T, ..n] -> &mut [T]
    // or &Concrete -> &Trait, etc.
    fn coerce_unsized(&self,
247 248 249
                      a: Ty<'tcx>,
                      b: Ty<'tcx>)
                      -> CoerceResult<'tcx> {
250
        debug!("coerce_unsized(a={}, b={})",
251 252
               a.repr(self.tcx()),
               b.repr(self.tcx()));
253

N
Nick Cameron 已提交
254 255 256 257 258
        // 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.

259
        match (&a.sty, &b.sty) {
260
            (&ty::ty_rptr(_, ty::mt{ty: t_a, mutbl: mutbl_a}), &ty::ty_rptr(_, mt_b)) => {
N
Nick Cameron 已提交
261 262 263 264
                match self.unsize_ty(t_a, mt_b.ty) {
                    Some((ty, kind)) => {
                        if !can_coerce_mutbls(mutbl_a, mt_b.mutbl) {
                            return Err(ty::terr_mutability);
N
Nick Cameron 已提交
265
                        }
N
Nick Cameron 已提交
266 267 268 269 270 271 272 273 274 275 276 277 278 279

                        let coercion = Coercion(self.trace.clone());
                        let r_borrow = self.fcx.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.fcx.infcx().try(|_| self.subtype(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))))
                        })))
N
Nick Cameron 已提交
280
                    }
N
Nick Cameron 已提交
281 282
                    _ => Err(ty::terr_mismatch)
                }
283
            }
284
            (&ty::ty_rptr(_, ty::mt{ty: t_a, mutbl: mutbl_a}), &ty::ty_ptr(mt_b)) => {
N
Nick Cameron 已提交
285 286 287 288
                match self.unsize_ty(t_a, mt_b.ty) {
                    Some((ty, kind)) => {
                        if !can_coerce_mutbls(mutbl_a, mt_b.mutbl) {
                            return Err(ty::terr_mutability);
289
                        }
N
Nick Cameron 已提交
290 291 292 293 294 295 296 297 298 299 300

                        let ty = ty::mk_ptr(self.tcx(),
                                             ty::mt{ty: ty, mutbl: mt_b.mutbl});
                        try!(self.fcx.infcx().try(|_| self.subtype(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))))
                        })))
301
                    }
N
Nick Cameron 已提交
302 303
                    _ => Err(ty::terr_mismatch)
                }
304
            }
N
Nick Cameron 已提交
305
            (&ty::ty_uniq(t_a), &ty::ty_uniq(t_b)) => {
N
Nick Cameron 已提交
306 307 308 309 310 311 312 313 314 315
                match self.unsize_ty(t_a, t_b) {
                    Some((ty, kind)) => {
                        let ty = ty::mk_uniq(self.tcx(), ty);
                        try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
                        debug!("Success, coerced with AutoDerefRef(1, \
                                AutoUnsizeUniq({:?}))", kind);
                        Ok(Some(AdjustDerefRef(AutoDerefRef {
                            autoderefs: 1,
                            autoref: Some(ty::AutoUnsizeUniq(kind))
                        })))
N
Nick Cameron 已提交
316
                    }
N
Nick Cameron 已提交
317 318
                    _ => Err(ty::terr_mismatch)
                }
N
Nick Cameron 已提交
319 320 321 322
            }
            _ => Err(ty::terr_mismatch)
        }
    }
323

N
Nick Cameron 已提交
324 325 326 327
    // 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,
328 329
                 ty_a: Ty<'tcx>,
                 ty_b: Ty<'tcx>)
N
Nick Cameron 已提交
330 331
                 -> Option<(Ty<'tcx>, ty::UnsizeKind<'tcx>)>
    {
332
        let tcx = self.tcx();
N
Nick Cameron 已提交
333

N
Nick Cameron 已提交
334 335 336 337 338 339 340
        self.unpack_actual_value(ty_a, |a| {
            self.unpack_actual_value(ty_b, |b| {
                debug!("unsize_ty(a={}, b={})", a.repr(self.tcx()), b.repr(self.tcx()));
                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)))
341
                    }
N
Nick Cameron 已提交
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
                    (&ty::ty_trait(ref data_a), &ty::ty_trait(ref data_b)) => {
                        // 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 {
                                // 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);

                            // 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,
                            }
                        } else {
                            None
N
Nick Cameron 已提交
382
                        }
N
Nick Cameron 已提交
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
                    }
                    (_, &ty::ty_trait(ref data)) => {
                        Some((ty_b, ty::UnsizeVtable(ty::TyTrait {
                                                         principal: data.principal.clone(),
                                                         bounds: data.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 mut result = None;
                        let tps = ty_substs_a.iter().zip(ty_substs_b.iter()).enumerate();
                        for (i, (tp_a, tp_b)) in tps {
                            if self.fcx.infcx().try(|_| self.subtype(*tp_a, *tp_b)).is_ok() {
                                continue;
                            }
                            match self.unsize_ty(*tp_a, *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.fcx.infcx().try(|_| self.subtype(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)));
N
Nick Cameron 已提交
424 425
                                    break;
                                }
N
Nick Cameron 已提交
426
                                None => {}
N
Nick Cameron 已提交
427 428
                            }
                        }
N
Nick Cameron 已提交
429
                        result
N
Nick Cameron 已提交
430
                    }
N
Nick Cameron 已提交
431
                    _ => None
N
Nick Cameron 已提交
432
                }
N
Nick Cameron 已提交
433
            })
N
Nick Cameron 已提交
434
        })
435 436
    }

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
    fn coerce_from_fn_pointer(&self,
                           a: Ty<'tcx>,
                           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_pointer(a={}, b={})",
                   a.repr(self.tcx()), b.repr(self.tcx()));

            match b.sty {
                ty::ty_bare_fn(None, fn_ty_b) => {
                    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));
                            Ok(Some(ty::AdjustUnsafeFnPointer))
                        }
                        _ => {
                            self.subtype(a, b)
                        }
                    }
                }
                _ => {
                    return self.subtype(a, b)
                }
            }
        })
    }

472 473 474
    fn coerce_from_fn_item(&self,
                           a: Ty<'tcx>,
                           fn_def_id_a: ast::DefId,
H
Huon Wilson 已提交
475
                           fn_ty_a: &'tcx ty::BareFnTy<'tcx>,
476
                           b: Ty<'tcx>)
477
                           -> CoerceResult<'tcx> {
478 479 480 481
        /*!
         * Attempts to coerce from the type of a Rust function item
         * into a closure or a `proc`.
         */
482

483
        self.unpack_actual_value(b, |b| {
N
Niko Matsakis 已提交
484
            debug!("coerce_from_fn_item(a={}, b={})",
485
                   a.repr(self.tcx()), b.repr(self.tcx()));
486

487 488
            match b.sty {
                ty::ty_bare_fn(None, _) => {
H
Huon Wilson 已提交
489
                    let a_fn_pointer = ty::mk_bare_fn(self.tcx(), None, fn_ty_a);
490 491 492 493 494 495 496
                    try!(self.subtype(a_fn_pointer, b));
                    Ok(Some(ty::AdjustReifyFnPointer(fn_def_id_a)))
                }
                _ => {
                    return self.subtype(a, b)
                }
            }
497
        })
498 499
    }

500 501 502 503 504
    fn coerce_unsafe_ptr(&self,
                         a: Ty<'tcx>,
                         b: Ty<'tcx>,
                         mutbl_b: ast::Mutability)
                         -> CoerceResult<'tcx> {
505
        debug!("coerce_unsafe_ptr(a={}, b={})",
506 507
               a.repr(self.tcx()),
               b.repr(self.tcx()));
508

509
        let mt_a = match a.sty {
510
            ty::ty_rptr(_, mt) | ty::ty_ptr(mt) => mt,
511 512 513 514 515
            _ => {
                return self.subtype(a, b);
            }
        };

516
        // Check that the types which they point at are compatible.
517
        let a_unsafe = ty::mk_ptr(self.tcx(), ty::mt{ mutbl: mutbl_b, ty: mt_a.ty });
518
        try!(self.subtype(a_unsafe, b));
519 520 521
        if !can_coerce_mutbls(mt_a.mutbl, mutbl_b) {
            return Err(ty::terr_mutability);
        }
N
Niko Matsakis 已提交
522

523
        // Although references and unsafe ptrs have the same
N
Niko Matsakis 已提交
524
        // representation, we still register an AutoDerefRef so that
525
        // regionck knows that the region for `a` must be valid here.
526
        Ok(Some(AdjustDerefRef(AutoDerefRef {
N
Niko Matsakis 已提交
527
            autoderefs: 1,
528
            autoref: Some(ty::AutoUnsafe(mutbl_b, None))
N
Niko Matsakis 已提交
529
        })))
530 531
    }
}
532

533 534
pub fn mk_assignty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                             expr: &ast::Expr,
535 536
                             a: Ty<'tcx>,
                             b: Ty<'tcx>)
537
                             -> CombineResult<'tcx, ()> {
538 539 540 541 542 543 544
    debug!("mk_assignty({} -> {})", a.repr(fcx.tcx()), b.repr(fcx.tcx()));
    let adjustment = try!(indent(|| {
        fcx.infcx().commit_if_ok(|| {
            let origin = infer::ExprAssignable(expr.span);
            Coerce {
                fcx: fcx,
                trace: infer::TypeTrace::types(origin, false, a, b)
545
            }.coerce(expr, a, b)
546
        })
547 548 549 550 551
    }));
    if let Some(adjustment) = adjustment {
        fcx.write_adjustment(expr.id, expr.span, adjustment);
    }
    Ok(())
552 553
}

554 555 556 557 558 559 560 561 562 563
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,
    }
}