coercion.rs 19.8 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 66 67
use middle::infer::{self, cres, Coercion, TypeTrace};
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 82
struct Coerce<'a, 'tcx: 'a> {
    fcx: &'a FnCtxt<'a, 'tcx>,
    trace: TypeTrace<'tcx>
}

type CoerceResult<'tcx> = cres<'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 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))
    }

101 102 103 104 105
    fn coerce(&self,
              expr_a: &ast::Expr,
              a: Ty<'tcx>,
              b: Ty<'tcx>)
              -> CoerceResult<'tcx> {
106
        debug!("Coerce.tys({} => {})",
107 108
               a.repr(self.tcx()),
               b.repr(self.tcx()));
109

N
Nick Cameron 已提交
110
        // Consider coercing the subtype to a DST
111 112
        let unsize = self.unpack_actual_value(a, |a| {
            self.coerce_unsized(a, b)
N
Nick Cameron 已提交
113 114 115 116 117
        });
        if unsize.is_ok() {
            return unsize;
        }

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

N
Nick Cameron 已提交
129
            ty::ty_rptr(_, mt_b) => {
130
                return self.unpack_actual_value(a, |a| {
131
                    self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl)
132
                });
133 134
            }

135 136
            _ => {}
        }
137

138 139
        self.unpack_actual_value(a, |a| {
            match a.sty {
140
                ty::ty_bare_fn(Some(a_def_id), a_f) => {
141 142 143 144
                    // 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)
145 146 147 148 149
                }
                _ => {
                    // Otherwise, just use subtyping rules.
                    self.subtype(a, b)
                }
150
            }
151
        })
152 153
    }

154 155 156
    /// 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.
157
    fn coerce_borrowed_pointer(&self,
158
                               expr_a: &ast::Expr,
159 160 161 162
                               a: Ty<'tcx>,
                               b: Ty<'tcx>,
                               mutbl_b: ast::Mutability)
                               -> CoerceResult<'tcx> {
163
        debug!("coerce_borrowed_pointer(a={}, b={})",
164 165
               a.repr(self.tcx()),
               b.repr(self.tcx()));
166

167 168 169 170 171 172
        // 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.

173
        match a.sty {
S
Seo Sanghyeon 已提交
174 175 176 177 178
            ty::ty_rptr(_, mt_a) => {
                if !can_coerce_mutbls(mt_a.mutbl, mutbl_b) {
                    return Err(ty::terr_mutability);
                }
            }
179
            _ => return self.subtype(a, b)
180
        }
181

182 183 184
        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 已提交
185

186 187 188 189 190 191 192 193 194 195 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
        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?"))
            }
        }
229 230 231
    }


N
Nick Cameron 已提交
232 233 234 235
    // &[T, ..n] or &mut [T, ..n] -> &[T]
    // or &mut [T, ..n] -> &mut [T]
    // or &Concrete -> &Trait, etc.
    fn coerce_unsized(&self,
236 237 238
                      a: Ty<'tcx>,
                      b: Ty<'tcx>)
                      -> CoerceResult<'tcx> {
239
        debug!("coerce_unsized(a={}, b={})",
240 241
               a.repr(self.tcx()),
               b.repr(self.tcx()));
242

N
Nick Cameron 已提交
243 244 245 246 247
        // 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.

248
        match (&a.sty, &b.sty) {
249
            (&ty::ty_rptr(_, ty::mt{ty: t_a, mutbl: mutbl_a}), &ty::ty_rptr(_, mt_b)) => {
250 251
                self.unpack_actual_value(t_a, |a| {
                    match self.unsize_ty(t_a, a, mt_b.ty) {
N
Nick Cameron 已提交
252
                        Some((ty, kind)) => {
253 254 255 256
                            if !can_coerce_mutbls(mutbl_a, mt_b.mutbl) {
                                return Err(ty::terr_mutability);
                            }

257 258
                            let coercion = Coercion(self.trace.clone());
                            let r_borrow = self.fcx.infcx().next_region_var(coercion);
259
                            let ty = ty::mk_rptr(self.tcx(),
H
Huon Wilson 已提交
260
                                                 self.tcx().mk_region(r_borrow),
N
Nick Cameron 已提交
261
                                                 ty::mt{ty: ty, mutbl: mt_b.mutbl});
262
                            try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
N
Nick Cameron 已提交
263
                            debug!("Success, coerced with AutoDerefRef(1, \
264
                                    AutoPtr(AutoUnsize({:?})))", kind);
265
                            Ok(Some(AdjustDerefRef(AutoDerefRef {
N
Nick Cameron 已提交
266 267 268 269 270 271 272 273
                                autoderefs: 1,
                                autoref: Some(ty::AutoPtr(r_borrow, mt_b.mutbl,
                                                          Some(box AutoUnsize(kind))))
                            })))
                        }
                        _ => Err(ty::terr_mismatch)
                    }
                })
274
            }
275
            (&ty::ty_rptr(_, ty::mt{ty: t_a, mutbl: mutbl_a}), &ty::ty_ptr(mt_b)) => {
276 277
                self.unpack_actual_value(t_a, |a| {
                    match self.unsize_ty(t_a, a, mt_b.ty) {
278
                        Some((ty, kind)) => {
279 280 281 282
                            if !can_coerce_mutbls(mutbl_a, mt_b.mutbl) {
                                return Err(ty::terr_mutability);
                            }

283
                            let ty = ty::mk_ptr(self.tcx(),
284
                                                 ty::mt{ty: ty, mutbl: mt_b.mutbl});
285
                            try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
286
                            debug!("Success, coerced with AutoDerefRef(1, \
287
                                    AutoPtr(AutoUnsize({:?})))", kind);
288
                            Ok(Some(AdjustDerefRef(AutoDerefRef {
289 290 291 292 293 294 295 296 297
                                autoderefs: 1,
                                autoref: Some(ty::AutoUnsafe(mt_b.mutbl,
                                                             Some(box AutoUnsize(kind))))
                            })))
                        }
                        _ => Err(ty::terr_mismatch)
                    }
                })
            }
N
Nick Cameron 已提交
298
            (&ty::ty_uniq(t_a), &ty::ty_uniq(t_b)) => {
299 300
                self.unpack_actual_value(t_a, |a| {
                    match self.unsize_ty(t_a, a, t_b) {
N
Nick Cameron 已提交
301
                        Some((ty, kind)) => {
302
                            let ty = ty::mk_uniq(self.tcx(), ty);
303
                            try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
N
Nick Cameron 已提交
304
                            debug!("Success, coerced with AutoDerefRef(1, \
305
                                    AutoUnsizeUniq({:?}))", kind);
306
                            Ok(Some(AdjustDerefRef(AutoDerefRef {
N
Nick Cameron 已提交
307 308 309 310 311 312 313 314 315 316 317
                                autoderefs: 1,
                                autoref: Some(ty::AutoUnsizeUniq(kind))
                            })))
                        }
                        _ => Err(ty::terr_mismatch)
                    }
                })
            }
            _ => Err(ty::terr_mismatch)
        }
    }
318

N
Nick Cameron 已提交
319 320 321 322
    // 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,
323
                 ty_a: Ty<'tcx>,
324
                 a: Ty<'tcx>,
325 326
                 ty_b: Ty<'tcx>)
                 -> Option<(Ty<'tcx>, ty::UnsizeKind<'tcx>)> {
327
        debug!("unsize_ty(a={:?}, ty_b={})", a, ty_b.repr(self.tcx()));
N
Nick Cameron 已提交
328

329
        let tcx = self.tcx();
N
Nick Cameron 已提交
330

331 332
        self.unpack_actual_value(ty_b, |b|
            match (&a.sty, &b.sty) {
333
                (&ty::ty_vec(t_a, Some(len)), &ty::ty_vec(_, None)) => {
N
Nick Cameron 已提交
334 335 336
                    let ty = ty::mk_vec(tcx, t_a, None);
                    Some((ty, ty::UnsizeLength(len)))
                }
337 338 339
                (&ty::ty_trait(..), &ty::ty_trait(..)) => {
                    None
                }
340
                (_, &ty::ty_trait(box ty::TyTrait { ref principal, ref bounds })) => {
341
                    // FIXME what is the purpose of `ty`?
342 343 344
                    let ty = ty::mk_trait(tcx, principal.clone(), bounds.clone());
                    Some((ty, ty::UnsizeVtable(ty::TyTrait { principal: principal.clone(),
                                                             bounds: bounds.clone() },
345
                                               ty_a)))
N
Nick Cameron 已提交
346
                }
H
Huon Wilson 已提交
347
                (&ty::ty_struct(did_a, substs_a), &ty::ty_struct(did_b, substs_b))
N
Nick Cameron 已提交
348 349 350
                  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.
N
Nick Cameron 已提交
351 352
                    let ty_substs_a = substs_a.types.get_slice(subst::TypeSpace);
                    let ty_substs_b = substs_b.types.get_slice(subst::TypeSpace);
N
Nick Cameron 已提交
353 354 355
                    assert!(ty_substs_a.len() == ty_substs_b.len());

                    let mut result = None;
J
Jorge Aparicio 已提交
356
                    let tps = ty_substs_a.iter().zip(ty_substs_b.iter()).enumerate();
N
Nick Cameron 已提交
357
                    for (i, (tp_a, tp_b)) in tps {
358
                        if self.fcx.infcx().try(|_| self.subtype(*tp_a, *tp_b)).is_ok() {
N
Nick Cameron 已提交
359 360
                            continue;
                        }
361 362 363 364 365
                        match
                            self.unpack_actual_value(
                                *tp_a,
                                |tp| self.unsize_ty(*tp_a, tp, *tp_b))
                        {
N
Nick Cameron 已提交
366 367 368
                            Some((new_tp, k)) => {
                                // Check that the whole types match.
                                let mut new_substs = substs_a.clone();
N
Nick Cameron 已提交
369
                                new_substs.types.get_mut_slice(subst::TypeSpace)[i] = new_tp;
H
Huon Wilson 已提交
370
                                let ty = ty::mk_struct(tcx, did_a, tcx.mk_substs(new_substs));
371
                                if self.fcx.infcx().try(|_| self.subtype(ty, ty_b)).is_err() {
N
Nick Cameron 已提交
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
                                    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
            }
        )
394 395
    }

396 397 398
    fn coerce_from_fn_item(&self,
                           a: Ty<'tcx>,
                           fn_def_id_a: ast::DefId,
H
Huon Wilson 已提交
399
                           fn_ty_a: &'tcx ty::BareFnTy<'tcx>,
400
                           b: Ty<'tcx>)
401
                           -> CoerceResult<'tcx> {
402 403 404 405
        /*!
         * Attempts to coerce from the type of a Rust function item
         * into a closure or a `proc`.
         */
406

407
        self.unpack_actual_value(b, |b| {
N
Niko Matsakis 已提交
408
            debug!("coerce_from_fn_item(a={}, b={})",
409
                   a.repr(self.tcx()), b.repr(self.tcx()));
410

411 412
            match b.sty {
                ty::ty_bare_fn(None, _) => {
H
Huon Wilson 已提交
413
                    let a_fn_pointer = ty::mk_bare_fn(self.tcx(), None, fn_ty_a);
414 415 416 417 418 419 420
                    try!(self.subtype(a_fn_pointer, b));
                    Ok(Some(ty::AdjustReifyFnPointer(fn_def_id_a)))
                }
                _ => {
                    return self.subtype(a, b)
                }
            }
421
        })
422 423
    }

424 425 426 427 428
    fn coerce_unsafe_ptr(&self,
                         a: Ty<'tcx>,
                         b: Ty<'tcx>,
                         mutbl_b: ast::Mutability)
                         -> CoerceResult<'tcx> {
429
        debug!("coerce_unsafe_ptr(a={}, b={})",
430 431
               a.repr(self.tcx()),
               b.repr(self.tcx()));
432

433
        let mt_a = match a.sty {
434
            ty::ty_rptr(_, mt) | ty::ty_ptr(mt) => mt,
435 436 437 438 439
            _ => {
                return self.subtype(a, b);
            }
        };

440
        // Check that the types which they point at are compatible.
441
        let a_unsafe = ty::mk_ptr(self.tcx(), ty::mt{ mutbl: mutbl_b, ty: mt_a.ty });
442
        try!(self.subtype(a_unsafe, b));
443 444 445
        if !can_coerce_mutbls(mt_a.mutbl, mutbl_b) {
            return Err(ty::terr_mutability);
        }
N
Niko Matsakis 已提交
446

447
        // Although references and unsafe ptrs have the same
N
Niko Matsakis 已提交
448
        // representation, we still register an AutoDerefRef so that
449
        // regionck knows that the region for `a` must be valid here.
450
        Ok(Some(AdjustDerefRef(AutoDerefRef {
N
Niko Matsakis 已提交
451
            autoderefs: 1,
452
            autoref: Some(ty::AutoUnsafe(mutbl_b, None))
N
Niko Matsakis 已提交
453
        })))
454 455
    }
}
456

457 458
pub fn mk_assignty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                             expr: &ast::Expr,
459 460
                             a: Ty<'tcx>,
                             b: Ty<'tcx>)
461 462 463 464 465 466 467 468
                             -> cres<'tcx, ()> {
    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)
469
            }.coerce(expr, a, b)
470
        })
471 472 473 474 475
    }));
    if let Some(adjustment) = adjustment {
        fcx.write_adjustment(expr.id, expr.span, adjustment);
    }
    Ok(())
476 477
}

478 479 480 481 482 483 484 485 486 487
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,
    }
}