2015-09-14 06:55:56 -05:00
|
|
|
// Copyright 2012-2015 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.
|
|
|
|
|
|
|
|
pub use self::AutoAdjustment::*;
|
|
|
|
pub use self::AutoRef::*;
|
|
|
|
|
2016-08-02 02:56:20 -05:00
|
|
|
use ty::{self, Ty, TyCtxt, TypeAndMut, TypeFoldable};
|
2016-03-22 10:30:57 -05:00
|
|
|
use ty::LvaluePreference::{NoPreference};
|
2015-09-14 06:55:56 -05:00
|
|
|
|
|
|
|
use syntax::ast;
|
2016-06-21 17:08:13 -05:00
|
|
|
use syntax_pos::Span;
|
2015-09-14 06:55:56 -05:00
|
|
|
|
2016-03-29 00:50:44 -05:00
|
|
|
use hir;
|
2015-09-14 06:55:56 -05:00
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum AutoAdjustment<'tcx> {
|
2016-08-02 02:56:20 -05:00
|
|
|
AdjustNeverToAny(Ty<'tcx>), // go from ! to any type
|
2016-06-25 05:42:52 -05:00
|
|
|
AdjustReifyFnPointer, // go from a fn-item type to a fn-pointer type
|
|
|
|
AdjustUnsafeFnPointer, // go from a safe fn pointer to an unsafe fn pointer
|
|
|
|
AdjustMutToConstPointer, // go from a mut raw pointer to a const raw pointer
|
2015-09-14 06:55:56 -05:00
|
|
|
AdjustDerefRef(AutoDerefRef<'tcx>),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Represents coercing a pointer to a different kind of pointer - where 'kind'
|
|
|
|
/// here means either or both of raw vs borrowed vs unique and fat vs thin.
|
|
|
|
///
|
|
|
|
/// We transform pointers by following the following steps in order:
|
|
|
|
/// 1. Deref the pointer `self.autoderefs` times (may be 0).
|
|
|
|
/// 2. If `autoref` is `Some(_)`, then take the address and produce either a
|
|
|
|
/// `&` or `*` pointer.
|
|
|
|
/// 3. If `unsize` is `Some(_)`, then apply the unsize transformation,
|
|
|
|
/// which will do things like convert thin pointers to fat
|
|
|
|
/// pointers, or convert structs containing thin pointers to
|
|
|
|
/// structs containing fat pointers, or convert between fat
|
|
|
|
/// pointers. We don't store the details of how the transform is
|
|
|
|
/// done (in fact, we don't know that, because it might depend on
|
|
|
|
/// the precise type parameters). We just store the target
|
|
|
|
/// type. Trans figures out what has to be done at monomorphization
|
|
|
|
/// time based on the precise source/target type at hand.
|
|
|
|
///
|
|
|
|
/// To make that more concrete, here are some common scenarios:
|
|
|
|
///
|
|
|
|
/// 1. The simplest cases are where the pointer is not adjusted fat vs thin.
|
|
|
|
/// Here the pointer will be dereferenced N times (where a dereference can
|
2015-10-07 17:11:25 -05:00
|
|
|
/// happen to raw or borrowed pointers or any smart pointer which implements
|
2015-09-14 06:55:56 -05:00
|
|
|
/// Deref, including Box<_>). The number of dereferences is given by
|
|
|
|
/// `autoderefs`. It can then be auto-referenced zero or one times, indicated
|
|
|
|
/// by `autoref`, to either a raw or borrowed pointer. In these cases unsize is
|
|
|
|
/// None.
|
|
|
|
///
|
|
|
|
/// 2. A thin-to-fat coercon involves unsizing the underlying data. We start
|
|
|
|
/// with a thin pointer, deref a number of times, unsize the underlying data,
|
|
|
|
/// then autoref. The 'unsize' phase may change a fixed length array to a
|
|
|
|
/// dynamically sized one, a concrete object to a trait object, or statically
|
|
|
|
/// sized struct to a dyncamically sized one. E.g., &[i32; 4] -> &[i32] is
|
|
|
|
/// represented by:
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// AutoDerefRef {
|
|
|
|
/// autoderefs: 1, // &[i32; 4] -> [i32; 4]
|
|
|
|
/// autoref: Some(AutoPtr), // [i32] -> &[i32]
|
|
|
|
/// unsize: Some([i32]), // [i32; 4] -> [i32]
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// Note that for a struct, the 'deep' unsizing of the struct is not recorded.
|
|
|
|
/// E.g., `struct Foo<T> { x: T }` we can coerce &Foo<[i32; 4]> to &Foo<[i32]>
|
|
|
|
/// The autoderef and -ref are the same as in the above example, but the type
|
|
|
|
/// stored in `unsize` is `Foo<[i32]>`, we don't store any further detail about
|
|
|
|
/// the underlying conversions from `[i32; 4]` to `[i32]`.
|
|
|
|
///
|
|
|
|
/// 3. Coercing a `Box<T>` to `Box<Trait>` is an interesting special case. In
|
|
|
|
/// that case, we have the pointer we need coming in, so there are no
|
|
|
|
/// autoderefs, and no autoref. Instead we just do the `Unsize` transformation.
|
|
|
|
/// At some point, of course, `Box` should move out of the compiler, in which
|
|
|
|
/// case this is analogous to transformating a struct. E.g., Box<[i32; 4]> ->
|
|
|
|
/// Box<[i32]> is represented by:
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// AutoDerefRef {
|
|
|
|
/// autoderefs: 0,
|
|
|
|
/// autoref: None,
|
|
|
|
/// unsize: Some(Box<[i32]>),
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub struct AutoDerefRef<'tcx> {
|
|
|
|
/// Step 1. Apply a number of dereferences, producing an lvalue.
|
|
|
|
pub autoderefs: usize,
|
|
|
|
|
|
|
|
/// Step 2. Optionally produce a pointer/reference from the value.
|
|
|
|
pub autoref: Option<AutoRef<'tcx>>,
|
|
|
|
|
|
|
|
/// Step 3. Unsize a pointer/reference value, e.g. `&[T; n]` to
|
|
|
|
/// `&[T]`. The stored type is the target pointer type. Note that
|
|
|
|
/// the source could be a thin or fat pointer.
|
|
|
|
pub unsize: Option<Ty<'tcx>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> AutoAdjustment<'tcx> {
|
|
|
|
pub fn is_identity(&self) -> bool {
|
|
|
|
match *self {
|
2016-08-02 02:56:20 -05:00
|
|
|
AdjustNeverToAny(ty) => ty.is_never(),
|
2015-09-14 06:55:56 -05:00
|
|
|
AdjustReifyFnPointer |
|
2016-02-11 10:31:20 -06:00
|
|
|
AdjustUnsafeFnPointer |
|
|
|
|
AdjustMutToConstPointer => false,
|
2015-09-14 06:55:56 -05:00
|
|
|
AdjustDerefRef(ref r) => r.is_identity(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<'tcx> AutoDerefRef<'tcx> {
|
|
|
|
pub fn is_identity(&self) -> bool {
|
|
|
|
self.autoderefs == 0 && self.unsize.is_none() && self.autoref.is_none()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
|
|
pub enum AutoRef<'tcx> {
|
|
|
|
/// Convert from T to &T.
|
|
|
|
AutoPtr(&'tcx ty::Region, hir::Mutability),
|
|
|
|
|
|
|
|
/// Convert from T to *T.
|
|
|
|
/// Value to thin pointer.
|
|
|
|
AutoUnsafe(hir::Mutability),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)]
|
|
|
|
pub enum CustomCoerceUnsized {
|
|
|
|
/// Records the index of the field being coerced.
|
|
|
|
Struct(usize)
|
|
|
|
}
|
|
|
|
|
2016-04-28 22:00:23 -05:00
|
|
|
impl<'a, 'gcx, 'tcx> ty::TyS<'tcx> {
|
2015-09-14 06:55:56 -05:00
|
|
|
/// See `expr_ty_adjusted`
|
2016-05-02 20:56:42 -05:00
|
|
|
pub fn adjust<F>(&'tcx self,
|
2016-04-28 22:00:23 -05:00
|
|
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
2015-09-14 06:55:56 -05:00
|
|
|
span: Span,
|
|
|
|
expr_id: ast::NodeId,
|
|
|
|
adjustment: Option<&AutoAdjustment<'tcx>>,
|
|
|
|
mut method_type: F)
|
|
|
|
-> Ty<'tcx> where
|
|
|
|
F: FnMut(ty::MethodCall) -> Option<Ty<'tcx>>,
|
|
|
|
{
|
|
|
|
if let ty::TyError = self.sty {
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
return match adjustment {
|
|
|
|
Some(adjustment) => {
|
|
|
|
match *adjustment {
|
2016-08-02 02:56:20 -05:00
|
|
|
AdjustNeverToAny(ref ty) => ty,
|
2016-06-25 05:42:52 -05:00
|
|
|
|
2016-02-11 10:31:20 -06:00
|
|
|
AdjustReifyFnPointer => {
|
2015-09-14 06:55:56 -05:00
|
|
|
match self.sty {
|
2016-08-26 11:23:42 -05:00
|
|
|
ty::TyFnDef(.., f) => tcx.mk_fn_ptr(f),
|
2015-09-14 06:55:56 -05:00
|
|
|
_ => {
|
2016-03-24 19:14:29 -05:00
|
|
|
bug!("AdjustReifyFnPointer adjustment on non-fn-item: {:?}",
|
|
|
|
self);
|
2015-09-14 06:55:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-11 10:31:20 -06:00
|
|
|
AdjustUnsafeFnPointer => {
|
2015-09-14 06:55:56 -05:00
|
|
|
match self.sty {
|
2016-05-02 20:02:41 -05:00
|
|
|
ty::TyFnPtr(b) => tcx.safe_to_unsafe_fn_ty(b),
|
2015-09-14 06:55:56 -05:00
|
|
|
ref b => {
|
2016-03-24 19:14:29 -05:00
|
|
|
bug!("AdjustUnsafeFnPointer adjustment on non-fn-ptr: {:?}",
|
|
|
|
b);
|
2015-09-14 06:55:56 -05:00
|
|
|
}
|
|
|
|
}
|
2016-02-11 10:31:20 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
AdjustMutToConstPointer => {
|
|
|
|
match self.sty {
|
2016-05-02 20:02:41 -05:00
|
|
|
ty::TyRawPtr(mt) => tcx.mk_ptr(ty::TypeAndMut {
|
2016-02-11 10:31:20 -06:00
|
|
|
ty: mt.ty,
|
|
|
|
mutbl: hir::MutImmutable
|
|
|
|
}),
|
|
|
|
ref b => {
|
2016-03-24 19:14:29 -05:00
|
|
|
bug!("AdjustMutToConstPointer on non-raw-ptr: {:?}",
|
|
|
|
b);
|
2016-02-11 10:31:20 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-09-14 06:55:56 -05:00
|
|
|
|
|
|
|
AdjustDerefRef(ref adj) => {
|
|
|
|
let mut adjusted_ty = self;
|
|
|
|
|
|
|
|
if !adjusted_ty.references_error() {
|
|
|
|
for i in 0..adj.autoderefs {
|
|
|
|
adjusted_ty =
|
2016-05-02 20:02:41 -05:00
|
|
|
adjusted_ty.adjust_for_autoderef(tcx,
|
2015-09-14 06:55:56 -05:00
|
|
|
expr_id,
|
|
|
|
span,
|
|
|
|
i as u32,
|
|
|
|
&mut method_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(target) = adj.unsize {
|
|
|
|
target
|
|
|
|
} else {
|
2016-05-02 20:02:41 -05:00
|
|
|
adjusted_ty.adjust_for_autoref(tcx, adj.autoref)
|
2015-09-14 06:55:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => self
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn adjust_for_autoderef<F>(&'tcx self,
|
2016-04-28 22:00:23 -05:00
|
|
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
2015-09-14 06:55:56 -05:00
|
|
|
expr_id: ast::NodeId,
|
|
|
|
expr_span: Span,
|
|
|
|
autoderef: u32, // how many autoderefs so far?
|
|
|
|
mut method_type: F)
|
|
|
|
-> Ty<'tcx> where
|
|
|
|
F: FnMut(ty::MethodCall) -> Option<Ty<'tcx>>,
|
|
|
|
{
|
|
|
|
let method_call = ty::MethodCall::autoderef(expr_id, autoderef);
|
|
|
|
let mut adjusted_ty = self;
|
|
|
|
if let Some(method_ty) = method_type(method_call) {
|
|
|
|
// Method calls always have all late-bound regions
|
|
|
|
// fully instantiated.
|
2016-07-31 09:33:41 -05:00
|
|
|
adjusted_ty = tcx.no_late_bound_regions(&method_ty.fn_ret()).unwrap();
|
2015-09-14 06:55:56 -05:00
|
|
|
}
|
|
|
|
match adjusted_ty.builtin_deref(true, NoPreference) {
|
|
|
|
Some(mt) => mt.ty,
|
|
|
|
None => {
|
2016-03-25 12:31:27 -05:00
|
|
|
span_bug!(
|
2015-09-14 06:55:56 -05:00
|
|
|
expr_span,
|
2016-05-24 06:37:11 -05:00
|
|
|
"the {}th autoderef for {} failed: {}",
|
2016-03-25 12:31:27 -05:00
|
|
|
autoderef,
|
2016-05-24 06:37:11 -05:00
|
|
|
expr_id,
|
2016-03-25 12:31:27 -05:00
|
|
|
adjusted_ty);
|
2015-09-14 06:55:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-28 22:00:23 -05:00
|
|
|
pub fn adjust_for_autoref(&'tcx self, tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
2015-09-14 06:55:56 -05:00
|
|
|
autoref: Option<AutoRef<'tcx>>)
|
|
|
|
-> Ty<'tcx> {
|
|
|
|
match autoref {
|
|
|
|
None => self,
|
|
|
|
Some(AutoPtr(r, m)) => {
|
2016-05-02 20:02:41 -05:00
|
|
|
tcx.mk_ref(r, TypeAndMut { ty: self, mutbl: m })
|
2015-09-14 06:55:56 -05:00
|
|
|
}
|
|
|
|
Some(AutoUnsafe(m)) => {
|
2016-05-02 20:02:41 -05:00
|
|
|
tcx.mk_ptr(TypeAndMut { ty: self, mutbl: m })
|
2015-09-14 06:55:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|