coercion.rs 17.9 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

65
use middle::infer::{self, Coercion};
N
Nick Cameron 已提交
66 67
use middle::traits::{self, ObligationCause};
use middle::traits::{predicate_for_trait_def, report_selection_error};
68
use middle::ty::{AutoDerefRef, AdjustDerefRef};
69
use middle::ty::{self, LvaluePreference, TypeAndMut, Ty, TypeError};
70
use middle::ty::relate::RelateResult;
71
use util::common::indent;
72

N
Nick Cameron 已提交
73 74
use std::cell::RefCell;
use std::collections::VecDeque;
75
use rustc_front::hir;
76

77 78
struct Coerce<'a, 'tcx: 'a> {
    fcx: &'a FnCtxt<'a, 'tcx>,
79
    origin: infer::TypeOrigin,
N
Nick Cameron 已提交
80
    unsizing_obligations: RefCell<Vec<traits::PredicateObligation<'tcx>>>,
81 82
}

83
type CoerceResult<'tcx> = RelateResult<'tcx, Option<ty::AutoAdjustment<'tcx>>>;
84

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

90
    fn subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
91
        try!(self.fcx.infcx().sub_types(false, self.origin.clone(), a, b));
92
        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
    fn coerce(&self,
102
              expr_a: &hir::Expr,
103 104 105
              a: Ty<'tcx>,
              b: Ty<'tcx>)
              -> CoerceResult<'tcx> {
106 107 108
        debug!("Coerce.tys({:?} => {:?})",
               a,
               b);
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::TyRawPtr(mt_b) => {
124 125 126
                return self.unpack_actual_value(a, |a| {
                    self.coerce_unsafe_ptr(a, b, mt_b.mutbl)
                });
127 128
            }

129
            ty::TyRef(_, 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::TyBareFn(Some(_), a_f) => {
141 142 143
                    // Function items are coercible to any closure
                    // type; function pointers are not (that would
                    // require double indirection).
144
                    self.coerce_from_fn_item(a, a_f, b)
145
                }
146
                ty::TyBareFn(None, a_f) => {
147 148 149 150
                    // We permit coercion of fn pointers to drop the
                    // unsafe qualifier.
                    self.coerce_from_fn_pointer(a, a_f, b)
                }
151 152 153 154
                _ => {
                    // Otherwise, just use subtyping rules.
                    self.subtype(a, b)
                }
155
            }
156
        })
157 158
    }

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

172 173 174 175 176 177
        // 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.

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

185
        let coercion = Coercion(self.origin.span());
186 187
        let r_borrow = self.fcx.infcx().next_region_var(coercion);
        let r_borrow = self.tcx().mk_region(r_borrow);
188 189
        let autoref = Some(ty::AutoPtr(r_borrow, mutbl_b));

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

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


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

        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");
250
            return Err(TypeError::Mismatch);
N
Nick Cameron 已提交
251
        };
252

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

N
Nick Cameron 已提交
258 259
        // Handle reborrows before selecting `Source: CoerceUnsized<Target>`.
        let (source, reborrow) = match (&source.sty, &target.sty) {
260
            (&ty::TyRef(_, mt_a), &ty::TyRef(_, mt_b)) => {
N
Nick Cameron 已提交
261 262 263 264 265 266
                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);
                (mt_a.ty, Some(ty::AutoPtr(region, mt_b.mutbl)))
267
            }
268
            (&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) => {
N
Nick Cameron 已提交
269 270
                try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl));
                (mt_a.ty, Some(ty::AutoUnsafe(mt_b.mutbl)))
271
            }
N
Nick Cameron 已提交
272 273
            _ => (source, None)
        };
274
        let source = source.adjust_for_autoref(self.tcx(), reborrow);
N
Nick Cameron 已提交
275

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

        // 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 已提交
284 285 286 287 288 289
        queue.push_back(predicate_for_trait_def(self.tcx(),
                                                cause,
                                                coerce_unsized_did,
                                                0,
                                                source,
                                                vec![target]));
N
Nick Cameron 已提交
290 291 292 293 294 295

        // 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() {
296
            debug!("coerce_unsized resolve step: {:?}", obligation);
N
Nick Cameron 已提交
297 298 299 300 301 302 303 304 305 306 307 308
            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 已提交
309
                    debug!("coerce_unsized: early return - can't prove obligation");
310
                    return Err(TypeError::Mismatch);
N
Nick Cameron 已提交
311
                }
N
Nick Cameron 已提交
312 313 314 315 316 317 318 319 320 321

                // 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)) => {
322 323 324
                    for obligation in vtable.nested_obligations() {
                        queue.push_back(obligation);
                    }
N
Nick Cameron 已提交
325
                }
N
Nick Cameron 已提交
326
            }
N
Nick Cameron 已提交
327 328 329 330 331
        }

        let mut obligations = self.unsizing_obligations.borrow_mut();
        assert!(obligations.is_empty());
        *obligations = leftover_predicates;
332 333 334 335 336 337

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

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

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

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

381
        self.unpack_actual_value(b, |b| {
382 383
            debug!("coerce_from_fn_item(a={:?}, b={:?})",
                   a, b);
384

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

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

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

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

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

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

    if let Some(AdjustDerefRef(auto)) = adjustment {
N
Nick Cameron 已提交
454 455 456
        if auto.unsize.is_some() {
            for obligation in unsizing_obligations {
                fcx.register_predicate(obligation);
457 458 459 460
            }
        }
    }

461
    if let Some(adjustment) = adjustment {
462
        debug!("Success, coerced with {:?}", adjustment);
463
        fcx.write_adjustment(expr.id, adjustment);
464 465
    }
    Ok(())
466 467
}

468 469
fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,
                       to_mutbl: hir::Mutability)
470
                       -> CoerceResult<'tcx> {
471
    match (from_mutbl, to_mutbl) {
472 473 474 475
        (hir::MutMutable, hir::MutMutable) |
        (hir::MutImmutable, hir::MutImmutable) |
        (hir::MutMutable, hir::MutImmutable) => Ok(None),
        (hir::MutImmutable, hir::MutMutable) => Err(TypeError::Mutability)
476 477
    }
}