coercion.rs 18.1 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, UnresolvedTypeAction};
64

M
Ms2ger 已提交
65
use middle::infer::{self, Coercion, TypeOrigin};
N
Nick Cameron 已提交
66 67
use middle::traits::{self, ObligationCause};
use middle::traits::{predicate_for_trait_def, report_selection_error};
68 69
use middle::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef};
use middle::ty::adjustment::{AutoPtr, AutoUnsafe, AdjustReifyFnPointer};
70
use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
N
Niko Matsakis 已提交
71 72
use middle::ty::{self, LvaluePreference, TypeAndMut, Ty};
use middle::ty::fold::TypeFoldable;
73
use middle::ty::error::TypeError;
74
use middle::ty::relate::RelateResult;
75
use util::common::indent;
76

N
Nick Cameron 已提交
77 78
use std::cell::RefCell;
use std::collections::VecDeque;
79
use rustc_front::hir;
80

81 82
struct Coerce<'a, 'tcx: 'a> {
    fcx: &'a FnCtxt<'a, 'tcx>,
83
    origin: infer::TypeOrigin,
N
Nick Cameron 已提交
84
    unsizing_obligations: RefCell<Vec<traits::PredicateObligation<'tcx>>>,
85 86
}

87
type CoerceResult<'tcx> = RelateResult<'tcx, Option<AutoAdjustment<'tcx>>>;
88

89
impl<'f, 'tcx> Coerce<'f, 'tcx> {
90 91
    fn tcx(&self) -> &ty::ctxt<'tcx> {
        self.fcx.tcx()
92 93
    }

94
    fn subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
95
        try!(self.fcx.infcx().sub_types(false, self.origin.clone(), a, b));
96
        Ok(None) // No coercion required.
97 98
    }

99 100 101 102 103 104
    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))
    }

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

114 115 116 117 118 119 120
        let a = self.fcx.infcx().shallow_resolve(a);

        // Just ignore error types.
        if a.references_error() || b.references_error() {
            return Ok(None);
        }

N
Nick Cameron 已提交
121
        // Consider coercing the subtype to a DST
122
        let unsize = self.coerce_unsized(a, b);
N
Nick Cameron 已提交
123 124 125 126
        if unsize.is_ok() {
            return unsize;
        }

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

136
            ty::TyRef(_, mt_b) => {
137
                return self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl);
138 139
            }

140 141
            _ => {}
        }
142

143 144 145 146 147 148
        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)
149
            }
150 151 152 153 154 155 156 157 158 159
            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)
            }
        }
160 161
    }

162 163 164
    /// 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.
165
    fn coerce_borrowed_pointer(&self,
166
                               expr_a: &hir::Expr,
167 168
                               a: Ty<'tcx>,
                               b: Ty<'tcx>,
169
                               mutbl_b: hir::Mutability)
170
                               -> CoerceResult<'tcx> {
171 172 173
        debug!("coerce_borrowed_pointer(a={:?}, b={:?})",
               a,
               b);
174

175 176 177 178 179 180
        // 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.

181
        match a.sty {
182
            ty::TyRef(_, mt_a) => {
183
                try!(coerce_mutbls(mt_a.mutbl, mutbl_b));
S
Seo Sanghyeon 已提交
184
            }
185
            _ => return self.subtype(a, b)
186
        }
187

188
        let coercion = Coercion(self.origin.span());
189 190
        let r_borrow = self.fcx.infcx().next_region_var(coercion);
        let r_borrow = self.tcx().mk_region(r_borrow);
191
        let autoref = Some(AutoPtr(r_borrow, mutbl_b));
192

193
        let lvalue_pref = LvaluePreference::from_mutbl(mutbl_b);
194 195 196 197 198 199 200 201 202 203 204 205 206
        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;
            }
207
            let ty = self.tcx().mk_ref(r_borrow,
208
                                        TypeAndMut {ty: inner_ty, mutbl: mutbl_b});
209
            if let Err(err) = self.subtype(ty, b) {
210 211 212 213 214 215 216 217 218 219 220 221 222
                if first_error.is_none() {
                    first_error = Some(err);
                }
                None
            } else {
                Some(())
            }
        });

        match success {
            Some(_) => {
                Ok(Some(AdjustDerefRef(AutoDerefRef {
                    autoderefs: autoderefs,
223 224
                    autoref: autoref,
                    unsize: None
225 226 227 228 229 230 231 232
                })))
            }
            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?"))
            }
        }
233 234 235
    }


236 237
    // &[T; n] or &mut [T; n] -> &[T]
    // or &mut [T; n] -> &mut [T]
N
Nick Cameron 已提交
238 239
    // or &Concrete -> &Trait, etc.
    fn coerce_unsized(&self,
N
Nick Cameron 已提交
240 241
                      source: Ty<'tcx>,
                      target: Ty<'tcx>)
242
                      -> CoerceResult<'tcx> {
243 244 245
        debug!("coerce_unsized(source={:?}, target={:?})",
               source,
               target);
N
Nick Cameron 已提交
246 247 248 249 250 251 252

        let traits = (self.tcx().lang_items.unsize_trait(),
                      self.tcx().lang_items.coerce_unsized_trait());
        let (unsize_did, coerce_unsized_did) = if let (Some(u), Some(cu)) = traits {
            (u, cu)
        } else {
            debug!("Missing Unsize or CoerceUnsized traits");
253
            return Err(TypeError::Mismatch);
N
Nick Cameron 已提交
254
        };
255

N
Nick Cameron 已提交
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
N
Nick Cameron 已提交
259
        // that, at which point we will need extra checks on the target here.
N
Nick Cameron 已提交
260

N
Nick Cameron 已提交
261 262
        // Handle reborrows before selecting `Source: CoerceUnsized<Target>`.
        let (source, reborrow) = match (&source.sty, &target.sty) {
263
            (&ty::TyRef(_, mt_a), &ty::TyRef(_, mt_b)) => {
N
Nick Cameron 已提交
264 265 266 267 268
                try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl));

                let coercion = Coercion(self.origin.span());
                let r_borrow = self.fcx.infcx().next_region_var(coercion);
                let region = self.tcx().mk_region(r_borrow);
269
                (mt_a.ty, Some(AutoPtr(region, mt_b.mutbl)))
270
            }
271
            (&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) => {
N
Nick Cameron 已提交
272
                try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl));
273
                (mt_a.ty, Some(AutoUnsafe(mt_b.mutbl)))
274
            }
N
Nick Cameron 已提交
275 276
            _ => (source, None)
        };
277
        let source = source.adjust_for_autoref(self.tcx(), reborrow);
N
Nick Cameron 已提交
278

J
Jared Roesch 已提交
279
        let mut selcx = traits::SelectionContext::new(self.fcx.infcx());
N
Nick Cameron 已提交
280 281 282 283 284 285 286

        // Use a FIFO queue for this custom fulfillment procedure.
        let mut queue = VecDeque::new();
        let mut leftover_predicates = vec![];

        // Create an obligation for `Source: CoerceUnsized<Target>`.
        let cause = ObligationCause::misc(self.origin.span(), self.fcx.body_id);
N
Nick Cameron 已提交
287 288 289 290 291 292
        queue.push_back(predicate_for_trait_def(self.tcx(),
                                                cause,
                                                coerce_unsized_did,
                                                0,
                                                source,
                                                vec![target]));
N
Nick Cameron 已提交
293 294 295 296 297 298

        // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
        // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
        // inference might unify those two inner type variables later.
        let traits = [coerce_unsized_did, unsize_did];
        while let Some(obligation) = queue.pop_front() {
299
            debug!("coerce_unsized resolve step: {:?}", obligation);
N
Nick Cameron 已提交
300 301 302 303 304 305 306 307 308 309 310 311
            let trait_ref =  match obligation.predicate {
                ty::Predicate::Trait(ref tr) if traits.contains(&tr.def_id()) => {
                    tr.clone()
                }
                _ => {
                    leftover_predicates.push(obligation);
                    continue;
                }
            };
            match selcx.select(&obligation.with(trait_ref)) {
                // Uncertain or unimplemented.
                Ok(None) | Err(traits::Unimplemented) => {
N
Nick Cameron 已提交
312
                    debug!("coerce_unsized: early return - can't prove obligation");
313
                    return Err(TypeError::Mismatch);
N
Nick Cameron 已提交
314
                }
N
Nick Cameron 已提交
315 316 317 318 319 320 321 322 323 324

                // Object safety violations or miscellaneous.
                Err(err) => {
                    report_selection_error(self.fcx.infcx(), &obligation, &err);
                    // Treat this like an obligation and follow through
                    // with the unsizing - the lack of a coercion should
                    // be silent, as it causes a type mismatch later.
                }

                Ok(Some(vtable)) => {
325 326 327
                    for obligation in vtable.nested_obligations() {
                        queue.push_back(obligation);
                    }
N
Nick Cameron 已提交
328
                }
N
Nick Cameron 已提交
329
            }
N
Nick Cameron 已提交
330 331 332 333 334
        }

        let mut obligations = self.unsizing_obligations.borrow_mut();
        assert!(obligations.is_empty());
        *obligations = leftover_predicates;
335 336 337 338 339 340

        let adjustment = AutoDerefRef {
            autoderefs: if reborrow.is_some() { 1 } else { 0 },
            autoref: reborrow,
            unsize: Some(target)
        };
341
        debug!("Success, coerced with {:?}", adjustment);
342
        Ok(Some(AdjustDerefRef(adjustment)))
N
Nick Cameron 已提交
343
    }
344

345 346 347 348 349 350 351 352 353 354 355 356
    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| {
357 358
            debug!("coerce_from_fn_pointer(a={:?}, b={:?})",
                   a, b);
359

360
            if let ty::TyBareFn(None, fn_ty_b) = b.sty {
361
                match (fn_ty_a.unsafety, fn_ty_b.unsafety) {
362
                    (hir::Unsafety::Normal, hir::Unsafety::Unsafe) => {
363 364
                        let unsafe_a = self.tcx().safe_to_unsafe_fn_ty(fn_ty_a);
                        try!(self.subtype(unsafe_a, b));
365
                        return Ok(Some(AdjustUnsafeFnPointer));
366
                    }
367
                    _ => {}
368 369
                }
            }
370
            self.subtype(a, b)
371 372 373
        })
    }

374 375
    fn coerce_from_fn_item(&self,
                           a: Ty<'tcx>,
H
Huon Wilson 已提交
376
                           fn_ty_a: &'tcx ty::BareFnTy<'tcx>,
377
                           b: Ty<'tcx>)
378
                           -> CoerceResult<'tcx> {
379 380 381 382
        /*!
         * Attempts to coerce from the type of a Rust function item
         * into a closure or a `proc`.
         */
383

384
        self.unpack_actual_value(b, |b| {
385 386
            debug!("coerce_from_fn_item(a={:?}, b={:?})",
                   a, b);
387

388
            match b.sty {
389
                ty::TyBareFn(None, _) => {
390
                    let a_fn_pointer = self.tcx().mk_fn(None, fn_ty_a);
391
                    try!(self.subtype(a_fn_pointer, b));
392
                    Ok(Some(AdjustReifyFnPointer))
393
                }
394
                _ => self.subtype(a, b)
395
            }
396
        })
397 398
    }

399 400 401
    fn coerce_unsafe_ptr(&self,
                         a: Ty<'tcx>,
                         b: Ty<'tcx>,
402
                         mutbl_b: hir::Mutability)
403
                         -> CoerceResult<'tcx> {
404 405 406
        debug!("coerce_unsafe_ptr(a={:?}, b={:?})",
               a,
               b);
407

408 409 410
        let (is_ref, mt_a) = match a.sty {
            ty::TyRef(_, mt) => (true, mt),
            ty::TyRawPtr(mt) => (false, mt),
411 412 413 414 415
            _ => {
                return self.subtype(a, b);
            }
        };

416
        // Check that the types which they point at are compatible.
417
        let a_unsafe = self.tcx().mk_ptr(ty::TypeAndMut{ mutbl: mutbl_b, ty: mt_a.ty });
418
        try!(self.subtype(a_unsafe, b));
419
        try!(coerce_mutbls(mt_a.mutbl, mutbl_b));
N
Niko Matsakis 已提交
420

421
        // Although references and unsafe ptrs have the same
N
Niko Matsakis 已提交
422
        // representation, we still register an AutoDerefRef so that
423
        // regionck knows that the region for `a` must be valid here.
424 425 426
        if is_ref {
            Ok(Some(AdjustDerefRef(AutoDerefRef {
                autoderefs: 1,
427
                autoref: Some(AutoUnsafe(mutbl_b)),
428 429
                unsize: None
            })))
430 431
        } else if mt_a.mutbl != mutbl_b {
            Ok(Some(AdjustMutToConstPointer))
432 433 434
        } else {
            Ok(None)
        }
435 436
    }
}
437

438
pub fn mk_assignty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
439
                             expr: &hir::Expr,
440 441
                             a: Ty<'tcx>,
                             b: Ty<'tcx>)
442
                             -> RelateResult<'tcx, ()> {
443
    debug!("mk_assignty({:?} -> {:?})", a, b);
N
Nick Cameron 已提交
444 445
    let mut unsizing_obligations = vec![];
    let adjustment = try!(indent(|| {
N
Rebased  
Nick Cameron 已提交
446
        fcx.infcx().commit_if_ok(|_| {
447
            let coerce = Coerce {
448
                fcx: fcx,
M
Ms2ger 已提交
449
                origin: TypeOrigin::ExprAssignable(expr.span),
N
Nick Cameron 已提交
450
                unsizing_obligations: RefCell::new(vec![])
451
            };
N
Nick Cameron 已提交
452 453 454
            let adjustment = try!(coerce.coerce(expr, a, b));
            unsizing_obligations = coerce.unsizing_obligations.into_inner();
            Ok(adjustment)
455
        })
456
    }));
457 458

    if let Some(AdjustDerefRef(auto)) = adjustment {
N
Nick Cameron 已提交
459 460 461
        if auto.unsize.is_some() {
            for obligation in unsizing_obligations {
                fcx.register_predicate(obligation);
462 463 464 465
            }
        }
    }

466
    if let Some(adjustment) = adjustment {
467
        debug!("Success, coerced with {:?}", adjustment);
468
        fcx.write_adjustment(expr.id, adjustment);
469 470
    }
    Ok(())
471 472
}

473 474
fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,
                       to_mutbl: hir::Mutability)
475
                       -> CoerceResult<'tcx> {
476
    match (from_mutbl, to_mutbl) {
477 478 479 480
        (hir::MutMutable, hir::MutMutable) |
        (hir::MutImmutable, hir::MutImmutable) |
        (hir::MutMutable, hir::MutImmutable) => Ok(None),
        (hir::MutImmutable, hir::MutMutable) => Err(TypeError::Mutability)
481 482
    }
}