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