]> git.proxmox.com Git - rustc.git/blame - src/librustc/middle/ty/adjustment.rs
Imported Upstream version 1.8.0+dfsg1
[rustc.git] / src / librustc / middle / ty / adjustment.rs
CommitLineData
e9174d1e
SL
1// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11pub use self::AutoAdjustment::*;
12pub use self::AutoRef::*;
13
9cc50fc6 14use middle::ty::{self, Ty, TypeAndMut, TypeFoldable};
e9174d1e
SL
15use middle::ty::LvaluePreference::{NoPreference};
16
17use syntax::ast;
18use syntax::codemap::Span;
19
20use rustc_front::hir;
21
22#[derive(Copy, Clone)]
23pub enum AutoAdjustment<'tcx> {
7453a54e
SL
24 AdjustReifyFnPointer, // go from a fn-item type to a fn-pointer type
25 AdjustUnsafeFnPointer, // go from a safe fn pointer to an unsafe fn pointer
26 AdjustMutToConstPointer, // go from a mut raw pointer to a const raw pointer
e9174d1e
SL
27 AdjustDerefRef(AutoDerefRef<'tcx>),
28}
29
30/// Represents coercing a pointer to a different kind of pointer - where 'kind'
31/// here means either or both of raw vs borrowed vs unique and fat vs thin.
32///
33/// We transform pointers by following the following steps in order:
34/// 1. Deref the pointer `self.autoderefs` times (may be 0).
35/// 2. If `autoref` is `Some(_)`, then take the address and produce either a
36/// `&` or `*` pointer.
37/// 3. If `unsize` is `Some(_)`, then apply the unsize transformation,
38/// which will do things like convert thin pointers to fat
39/// pointers, or convert structs containing thin pointers to
40/// structs containing fat pointers, or convert between fat
41/// pointers. We don't store the details of how the transform is
42/// done (in fact, we don't know that, because it might depend on
43/// the precise type parameters). We just store the target
44/// type. Trans figures out what has to be done at monomorphization
45/// time based on the precise source/target type at hand.
46///
47/// To make that more concrete, here are some common scenarios:
48///
49/// 1. The simplest cases are where the pointer is not adjusted fat vs thin.
50/// Here the pointer will be dereferenced N times (where a dereference can
b039eaaf 51/// happen to raw or borrowed pointers or any smart pointer which implements
e9174d1e
SL
52/// Deref, including Box<_>). The number of dereferences is given by
53/// `autoderefs`. It can then be auto-referenced zero or one times, indicated
54/// by `autoref`, to either a raw or borrowed pointer. In these cases unsize is
55/// None.
56///
57/// 2. A thin-to-fat coercon involves unsizing the underlying data. We start
58/// with a thin pointer, deref a number of times, unsize the underlying data,
59/// then autoref. The 'unsize' phase may change a fixed length array to a
60/// dynamically sized one, a concrete object to a trait object, or statically
61/// sized struct to a dyncamically sized one. E.g., &[i32; 4] -> &[i32] is
62/// represented by:
63///
64/// ```
65/// AutoDerefRef {
66/// autoderefs: 1, // &[i32; 4] -> [i32; 4]
67/// autoref: Some(AutoPtr), // [i32] -> &[i32]
68/// unsize: Some([i32]), // [i32; 4] -> [i32]
69/// }
70/// ```
71///
72/// Note that for a struct, the 'deep' unsizing of the struct is not recorded.
73/// E.g., `struct Foo<T> { x: T }` we can coerce &Foo<[i32; 4]> to &Foo<[i32]>
74/// The autoderef and -ref are the same as in the above example, but the type
75/// stored in `unsize` is `Foo<[i32]>`, we don't store any further detail about
76/// the underlying conversions from `[i32; 4]` to `[i32]`.
77///
78/// 3. Coercing a `Box<T>` to `Box<Trait>` is an interesting special case. In
79/// that case, we have the pointer we need coming in, so there are no
80/// autoderefs, and no autoref. Instead we just do the `Unsize` transformation.
81/// At some point, of course, `Box` should move out of the compiler, in which
82/// case this is analogous to transformating a struct. E.g., Box<[i32; 4]> ->
83/// Box<[i32]> is represented by:
84///
85/// ```
86/// AutoDerefRef {
87/// autoderefs: 0,
88/// autoref: None,
89/// unsize: Some(Box<[i32]>),
90/// }
91/// ```
92#[derive(Copy, Clone)]
93pub struct AutoDerefRef<'tcx> {
94 /// Step 1. Apply a number of dereferences, producing an lvalue.
95 pub autoderefs: usize,
96
97 /// Step 2. Optionally produce a pointer/reference from the value.
98 pub autoref: Option<AutoRef<'tcx>>,
99
100 /// Step 3. Unsize a pointer/reference value, e.g. `&[T; n]` to
101 /// `&[T]`. The stored type is the target pointer type. Note that
102 /// the source could be a thin or fat pointer.
103 pub unsize: Option<Ty<'tcx>>,
104}
105
106impl<'tcx> AutoAdjustment<'tcx> {
107 pub fn is_identity(&self) -> bool {
108 match *self {
109 AdjustReifyFnPointer |
7453a54e
SL
110 AdjustUnsafeFnPointer |
111 AdjustMutToConstPointer => false,
e9174d1e
SL
112 AdjustDerefRef(ref r) => r.is_identity(),
113 }
114 }
115}
116impl<'tcx> AutoDerefRef<'tcx> {
117 pub fn is_identity(&self) -> bool {
118 self.autoderefs == 0 && self.unsize.is_none() && self.autoref.is_none()
119 }
120}
121
122
123#[derive(Copy, Clone, PartialEq, Debug)]
124pub enum AutoRef<'tcx> {
125 /// Convert from T to &T.
126 AutoPtr(&'tcx ty::Region, hir::Mutability),
127
128 /// Convert from T to *T.
129 /// Value to thin pointer.
130 AutoUnsafe(hir::Mutability),
131}
132
133#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)]
134pub enum CustomCoerceUnsized {
135 /// Records the index of the field being coerced.
136 Struct(usize)
137}
138
139impl<'tcx> ty::TyS<'tcx> {
140 /// See `expr_ty_adjusted`
141 pub fn adjust<F>(&'tcx self, cx: &ty::ctxt<'tcx>,
142 span: Span,
143 expr_id: ast::NodeId,
144 adjustment: Option<&AutoAdjustment<'tcx>>,
145 mut method_type: F)
146 -> Ty<'tcx> where
147 F: FnMut(ty::MethodCall) -> Option<Ty<'tcx>>,
148 {
149 if let ty::TyError = self.sty {
150 return self;
151 }
152
153 return match adjustment {
154 Some(adjustment) => {
155 match *adjustment {
7453a54e 156 AdjustReifyFnPointer => {
e9174d1e
SL
157 match self.sty {
158 ty::TyBareFn(Some(_), b) => {
159 cx.mk_fn(None, b)
160 }
161 _ => {
162 cx.sess.bug(
163 &format!("AdjustReifyFnPointer adjustment on non-fn-item: \
164 {:?}", self));
165 }
166 }
167 }
168
7453a54e 169 AdjustUnsafeFnPointer => {
e9174d1e
SL
170 match self.sty {
171 ty::TyBareFn(None, b) => cx.safe_to_unsafe_fn_ty(b),
172 ref b => {
173 cx.sess.bug(
7453a54e 174 &format!("AdjustUnsafeFnPointer adjustment on non-fn-ptr: \
e9174d1e
SL
175 {:?}",
176 b));
177 }
178 }
7453a54e
SL
179 }
180
181 AdjustMutToConstPointer => {
182 match self.sty {
183 ty::TyRawPtr(mt) => cx.mk_ptr(ty::TypeAndMut {
184 ty: mt.ty,
185 mutbl: hir::MutImmutable
186 }),
187 ref b => {
188 cx.sess.bug(
189 &format!("AdjustMutToConstPointer on non-raw-ptr: \
190 {:?}",
191 b));
192 }
193 }
194 }
e9174d1e
SL
195
196 AdjustDerefRef(ref adj) => {
197 let mut adjusted_ty = self;
198
199 if !adjusted_ty.references_error() {
200 for i in 0..adj.autoderefs {
201 adjusted_ty =
202 adjusted_ty.adjust_for_autoderef(cx,
203 expr_id,
204 span,
205 i as u32,
206 &mut method_type);
207 }
208 }
209
210 if let Some(target) = adj.unsize {
211 target
212 } else {
213 adjusted_ty.adjust_for_autoref(cx, adj.autoref)
214 }
215 }
216 }
217 }
218 None => self
219 };
220 }
221
222 pub fn adjust_for_autoderef<F>(&'tcx self,
223 cx: &ty::ctxt<'tcx>,
224 expr_id: ast::NodeId,
225 expr_span: Span,
226 autoderef: u32, // how many autoderefs so far?
227 mut method_type: F)
228 -> Ty<'tcx> where
229 F: FnMut(ty::MethodCall) -> Option<Ty<'tcx>>,
230 {
231 let method_call = ty::MethodCall::autoderef(expr_id, autoderef);
232 let mut adjusted_ty = self;
233 if let Some(method_ty) = method_type(method_call) {
234 // Method calls always have all late-bound regions
235 // fully instantiated.
236 let fn_ret = cx.no_late_bound_regions(&method_ty.fn_ret()).unwrap();
237 adjusted_ty = fn_ret.unwrap();
238 }
239 match adjusted_ty.builtin_deref(true, NoPreference) {
240 Some(mt) => mt.ty,
241 None => {
242 cx.sess.span_bug(
243 expr_span,
244 &format!("the {}th autoderef failed: {}",
245 autoderef,
246 adjusted_ty)
247 );
248 }
249 }
250 }
251
252 pub fn adjust_for_autoref(&'tcx self, cx: &ty::ctxt<'tcx>,
253 autoref: Option<AutoRef<'tcx>>)
254 -> Ty<'tcx> {
255 match autoref {
256 None => self,
257 Some(AutoPtr(r, m)) => {
258 cx.mk_ref(r, TypeAndMut { ty: self, mutbl: m })
259 }
260 Some(AutoUnsafe(m)) => {
261 cx.mk_ptr(TypeAndMut { ty: self, mutbl: m })
262 }
263 }
264 }
265}