]>
Commit | Line | Data |
---|---|---|
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 | ||
54a0048b SL |
11 | use hir::def_id::{DefId}; |
12 | use ty::{self, Ty, TyCtxt}; | |
9cc50fc6 | 13 | use util::common::MemoizationMap; |
476ff2be | 14 | use util::nodemap::FxHashMap; |
e9174d1e SL |
15 | |
16 | use std::fmt; | |
17 | use std::ops; | |
18 | ||
b039eaaf | 19 | use syntax::ast; |
e9174d1e SL |
20 | |
21 | /// Type contents is how the type checker reasons about kinds. | |
22 | /// They track what kinds of things are found within a type. You can | |
23 | /// think of them as kind of an "anti-kind". They track the kinds of values | |
24 | /// and thinks that are contained in types. Having a larger contents for | |
25 | /// a type tends to rule that type *out* from various kinds. For example, | |
26 | /// a type that contains a reference is not sendable. | |
27 | /// | |
28 | /// The reason we compute type contents and not kinds is that it is | |
29 | /// easier for me (nmatsakis) to think about what is contained within | |
30 | /// a type than to think about what is *not* contained within a type. | |
31 | #[derive(Clone, Copy)] | |
32 | pub struct TypeContents { | |
33 | pub bits: u64 | |
34 | } | |
35 | ||
36 | macro_rules! def_type_content_sets { | |
37 | (mod $mname:ident { $($name:ident = $bits:expr),+ }) => { | |
38 | #[allow(non_snake_case)] | |
39 | mod $mname { | |
40 | use super::TypeContents; | |
41 | $( | |
42 | #[allow(non_upper_case_globals)] | |
43 | pub const $name: TypeContents = TypeContents { bits: $bits }; | |
44 | )+ | |
45 | } | |
46 | } | |
47 | } | |
48 | ||
49 | def_type_content_sets! { | |
50 | mod TC { | |
51 | None = 0b0000_0000__0000_0000__0000, | |
52 | ||
53 | // Things that are interior to the value (first nibble): | |
54 | InteriorUnsafe = 0b0000_0000__0000_0000__0010, | |
55 | InteriorParam = 0b0000_0000__0000_0000__0100, | |
56 | // InteriorAll = 0b00000000__00000000__1111, | |
57 | ||
58 | // Things that are owned by the value (second and third nibbles): | |
e9174d1e | 59 | OwnsDtor = 0b0000_0000__0000_0010__0000, |
32a655c1 | 60 | // OwnsAll = 0b0000_0000__1111_1111__0000, |
e9174d1e SL |
61 | |
62 | // All bits | |
63 | All = 0b1111_1111__1111_1111__1111 | |
64 | } | |
65 | } | |
66 | ||
67 | impl TypeContents { | |
68 | pub fn when(&self, cond: bool) -> TypeContents { | |
69 | if cond {*self} else {TC::None} | |
70 | } | |
71 | ||
72 | pub fn intersects(&self, tc: TypeContents) -> bool { | |
73 | (self.bits & tc.bits) != 0 | |
74 | } | |
75 | ||
e9174d1e SL |
76 | pub fn interior_param(&self) -> bool { |
77 | self.intersects(TC::InteriorParam) | |
78 | } | |
79 | ||
80 | pub fn interior_unsafe(&self) -> bool { | |
81 | self.intersects(TC::InteriorUnsafe) | |
82 | } | |
83 | ||
a7813a04 | 84 | pub fn needs_drop(&self, _: TyCtxt) -> bool { |
32a655c1 | 85 | self.intersects(TC::OwnsDtor) |
e9174d1e SL |
86 | } |
87 | ||
476ff2be SL |
88 | pub fn union<I, T, F>(v: I, mut f: F) -> TypeContents where |
89 | I: IntoIterator<Item=T>, | |
90 | F: FnMut(T) -> TypeContents, | |
e9174d1e | 91 | { |
476ff2be | 92 | v.into_iter().fold(TC::None, |tc, ty| tc | f(ty)) |
e9174d1e | 93 | } |
e9174d1e SL |
94 | } |
95 | ||
96 | impl ops::BitOr for TypeContents { | |
97 | type Output = TypeContents; | |
98 | ||
99 | fn bitor(self, other: TypeContents) -> TypeContents { | |
100 | TypeContents {bits: self.bits | other.bits} | |
101 | } | |
102 | } | |
103 | ||
104 | impl ops::BitAnd for TypeContents { | |
105 | type Output = TypeContents; | |
106 | ||
107 | fn bitand(self, other: TypeContents) -> TypeContents { | |
108 | TypeContents {bits: self.bits & other.bits} | |
109 | } | |
110 | } | |
111 | ||
112 | impl ops::Sub for TypeContents { | |
113 | type Output = TypeContents; | |
114 | ||
115 | fn sub(self, other: TypeContents) -> TypeContents { | |
116 | TypeContents {bits: self.bits & !other.bits} | |
117 | } | |
118 | } | |
119 | ||
120 | impl fmt::Debug for TypeContents { | |
121 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
122 | write!(f, "TypeContents({:b})", self.bits) | |
123 | } | |
124 | } | |
125 | ||
a7813a04 XL |
126 | impl<'a, 'tcx> ty::TyS<'tcx> { |
127 | pub fn type_contents(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> TypeContents { | |
476ff2be | 128 | return tcx.tc_cache.memoize(self, || tc_ty(tcx, self, &mut FxHashMap())); |
e9174d1e | 129 | |
a7813a04 XL |
130 | fn tc_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
131 | ty: Ty<'tcx>, | |
476ff2be | 132 | cache: &mut FxHashMap<Ty<'tcx>, TypeContents>) -> TypeContents |
e9174d1e | 133 | { |
a7813a04 | 134 | // Subtle: Note that we are *not* using tcx.tc_cache here but rather a |
e9174d1e SL |
135 | // private cache for this walk. This is needed in the case of cyclic |
136 | // types like: | |
137 | // | |
138 | // struct List { next: Box<Option<List>>, ... } | |
139 | // | |
140 | // When computing the type contents of such a type, we wind up deeply | |
141 | // recursing as we go. So when we encounter the recursive reference | |
142 | // to List, we temporarily use TC::None as its contents. Later we'll | |
143 | // patch up the cache with the correct value, once we've computed it | |
144 | // (this is basically a co-inductive process, if that helps). So in | |
145 | // the end we'll compute TC::OwnsOwned, in this case. | |
146 | // | |
147 | // The problem is, as we are doing the computation, we will also | |
148 | // compute an *intermediate* contents for, e.g., Option<List> of | |
149 | // TC::None. This is ok during the computation of List itself, but if | |
a7813a04 | 150 | // we stored this intermediate value into tcx.tc_cache, then later |
e9174d1e SL |
151 | // requests for the contents of Option<List> would also yield TC::None |
152 | // which is incorrect. This value was computed based on the crutch | |
153 | // value for the type contents of list. The correct value is | |
154 | // TC::OwnsOwned. This manifested as issue #4821. | |
3157f602 XL |
155 | if let Some(tc) = cache.get(&ty) { |
156 | return *tc; | |
e9174d1e | 157 | } |
3157f602 XL |
158 | // Must check both caches! |
159 | if let Some(tc) = tcx.tc_cache.borrow().get(&ty) { | |
160 | return *tc; | |
e9174d1e SL |
161 | } |
162 | cache.insert(ty, TC::None); | |
163 | ||
164 | let result = match ty.sty { | |
165 | // usize and isize are ffi-unsafe | |
7453a54e | 166 | ty::TyUint(ast::UintTy::Us) | ty::TyInt(ast::IntTy::Is) => { |
e9174d1e SL |
167 | TC::None |
168 | } | |
169 | ||
170 | // Scalar and unique types are sendable, and durable | |
171 | ty::TyInfer(ty::FreshIntTy(_)) | ty::TyInfer(ty::FreshFloatTy(_)) | | |
5bcae85e | 172 | ty::TyBool | ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) | ty::TyNever | |
54a0048b | 173 | ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar => { |
e9174d1e SL |
174 | TC::None |
175 | } | |
176 | ||
476ff2be | 177 | ty::TyDynamic(..) => { |
e9174d1e SL |
178 | TC::All - TC::InteriorParam |
179 | } | |
180 | ||
181 | ty::TyRawPtr(_) => { | |
182 | TC::None | |
183 | } | |
184 | ||
9e0c209e | 185 | ty::TyRef(..) => { |
e9174d1e SL |
186 | TC::None |
187 | } | |
188 | ||
189 | ty::TyArray(ty, _) => { | |
a7813a04 | 190 | tc_ty(tcx, ty, cache) |
e9174d1e SL |
191 | } |
192 | ||
193 | ty::TySlice(ty) => { | |
a7813a04 | 194 | tc_ty(tcx, ty, cache) |
e9174d1e SL |
195 | } |
196 | ty::TyStr => TC::None, | |
197 | ||
476ff2be SL |
198 | ty::TyClosure(def_id, ref substs) => { |
199 | TypeContents::union( | |
200 | substs.upvar_tys(def_id, tcx), | |
201 | |ty| tc_ty(tcx, &ty, cache)) | |
e9174d1e SL |
202 | } |
203 | ||
8bb4bdeb | 204 | ty::TyTuple(ref tys, _) => { |
e9174d1e | 205 | TypeContents::union(&tys[..], |
a7813a04 | 206 | |ty| tc_ty(tcx, *ty, cache)) |
e9174d1e SL |
207 | } |
208 | ||
9e0c209e | 209 | ty::TyAdt(def, substs) => { |
e9174d1e SL |
210 | let mut res = |
211 | TypeContents::union(&def.variants, |v| { | |
212 | TypeContents::union(&v.fields, |f| { | |
a7813a04 | 213 | tc_ty(tcx, f.ty(tcx, substs), cache) |
e9174d1e SL |
214 | }) |
215 | }); | |
216 | ||
32a655c1 SL |
217 | if def.is_union() { |
218 | // unions don't have destructors regardless of the child types | |
219 | res = res - TC::OwnsDtor; | |
220 | } | |
221 | ||
8bb4bdeb | 222 | if def.has_dtor(tcx) { |
e9174d1e SL |
223 | res = res | TC::OwnsDtor; |
224 | } | |
225 | ||
a7813a04 | 226 | apply_lang_items(tcx, def.did, res) |
e9174d1e SL |
227 | } |
228 | ||
229 | ty::TyProjection(..) | | |
5bcae85e SL |
230 | ty::TyParam(_) | |
231 | ty::TyAnon(..) => { | |
e9174d1e SL |
232 | TC::All |
233 | } | |
234 | ||
235 | ty::TyInfer(_) | | |
236 | ty::TyError => { | |
54a0048b | 237 | bug!("asked to compute contents of error type"); |
e9174d1e SL |
238 | } |
239 | }; | |
240 | ||
241 | cache.insert(ty, result); | |
242 | result | |
243 | } | |
244 | ||
a7813a04 XL |
245 | fn apply_lang_items<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
246 | did: DefId, tc: TypeContents) | |
247 | -> TypeContents { | |
248 | if Some(did) == tcx.lang_items.unsafe_cell_type() { | |
e9174d1e SL |
249 | tc | TC::InteriorUnsafe |
250 | } else { | |
251 | tc | |
252 | } | |
253 | } | |
254 | } | |
255 | } |