rust/src/librustc/ty/adjustment.rs

263 lines
10 KiB
Rust
Raw Normal View History

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::*;
use ty::{self, Ty, TyCtxt, TypeAndMut, TypeFoldable};
use ty::LvaluePreference::{NoPreference};
2015-09-14 06:55:56 -05:00
use syntax::ast;
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> {
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
/// 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 {
AdjustNeverToAny(ty) => ty.is_never(),
2015-09-14 06:55:56 -05:00
AdjustReifyFnPointer |
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)
}
impl<'a, 'gcx, 'tcx> ty::TyS<'tcx> {
2015-09-14 06:55:56 -05:00
/// See `expr_ty_adjusted`
pub fn adjust<F>(&'tcx self,
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 {
AdjustNeverToAny(ref ty) => ty,
2016-06-25 05:42:52 -05: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
_ => {
bug!("AdjustReifyFnPointer adjustment on non-fn-item: {:?}",
self);
2015-09-14 06:55:56 -05: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 => {
bug!("AdjustUnsafeFnPointer adjustment on non-fn-ptr: {:?}",
b);
2015-09-14 06:55:56 -05:00
}
}
}
AdjustMutToConstPointer => {
match self.sty {
2016-05-02 20:02:41 -05:00
ty::TyRawPtr(mt) => tcx.mk_ptr(ty::TypeAndMut {
ty: mt.ty,
mutbl: hir::MutImmutable
}),
ref b => {
bug!("AdjustMutToConstPointer on non-raw-ptr: {:?}",
b);
}
}
}
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,
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.
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 => {
span_bug!(
2015-09-14 06:55:56 -05:00
expr_span,
"the {}th autoderef for {} failed: {}",
autoderef,
expr_id,
adjusted_ty);
2015-09-14 06:55:56 -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
}
}
}
}