]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | // Copyright 2014-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 | use check::regionck::{self, Rcx}; | |
12 | ||
13 | use middle::infer; | |
14 | use middle::region; | |
c34b1796 | 15 | use middle::subst::{self, Subst}; |
85aaf69f | 16 | use middle::ty::{self, Ty}; |
c34b1796 AL |
17 | |
18 | use syntax::ast; | |
19 | use syntax::codemap::{self, Span}; | |
20 | ||
21 | /// check_drop_impl confirms that the Drop implementation identfied by | |
22 | /// `drop_impl_did` is not any more specialized than the type it is | |
23 | /// attached to (Issue #8142). | |
24 | /// | |
25 | /// This means: | |
26 | /// | |
27 | /// 1. The self type must be nominal (this is already checked during | |
28 | /// coherence), | |
29 | /// | |
30 | /// 2. The generic region/type parameters of the impl's self-type must | |
31 | /// all be parameters of the Drop impl itself (i.e. no | |
32 | /// specialization like `impl Drop for Foo<i32>`), and, | |
33 | /// | |
34 | /// 3. Any bounds on the generic parameters must be reflected in the | |
35 | /// struct/enum definition for the nominal type itself (i.e. | |
36 | /// cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`). | |
37 | /// | |
38 | pub fn check_drop_impl(tcx: &ty::ctxt, drop_impl_did: ast::DefId) -> Result<(), ()> { | |
39 | let ty::TypeScheme { generics: ref dtor_generics, | |
62682a34 | 40 | ty: dtor_self_type } = ty::lookup_item_type(tcx, drop_impl_did); |
c34b1796 AL |
41 | let dtor_predicates = ty::lookup_predicates(tcx, drop_impl_did); |
42 | match dtor_self_type.sty { | |
62682a34 SL |
43 | ty::TyEnum(self_type_did, self_to_impl_substs) | |
44 | ty::TyStruct(self_type_did, self_to_impl_substs) | | |
45 | ty::TyClosure(self_type_did, self_to_impl_substs) => { | |
c34b1796 AL |
46 | try!(ensure_drop_params_and_item_params_correspond(tcx, |
47 | drop_impl_did, | |
48 | dtor_generics, | |
62682a34 | 49 | &dtor_self_type, |
c34b1796 AL |
50 | self_type_did)); |
51 | ||
52 | ensure_drop_predicates_are_implied_by_item_defn(tcx, | |
53 | drop_impl_did, | |
54 | &dtor_predicates, | |
55 | self_type_did, | |
56 | self_to_impl_substs) | |
57 | } | |
58 | _ => { | |
59 | // Destructors only work on nominal types. This was | |
60 | // already checked by coherence, so we can panic here. | |
61 | let span = tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP); | |
62 | tcx.sess.span_bug( | |
63 | span, &format!("should have been rejected by coherence check: {}", | |
62682a34 | 64 | dtor_self_type)); |
c34b1796 AL |
65 | } |
66 | } | |
67 | } | |
68 | ||
69 | fn ensure_drop_params_and_item_params_correspond<'tcx>( | |
70 | tcx: &ty::ctxt<'tcx>, | |
71 | drop_impl_did: ast::DefId, | |
72 | drop_impl_generics: &ty::Generics<'tcx>, | |
73 | drop_impl_ty: &ty::Ty<'tcx>, | |
74 | self_type_did: ast::DefId) -> Result<(), ()> | |
75 | { | |
76 | // New strategy based on review suggestion from nikomatsakis. | |
77 | // | |
78 | // (In the text and code below, "named" denotes "struct/enum", and | |
79 | // "generic params" denotes "type and region params") | |
80 | // | |
81 | // 1. Create fresh skolemized type/region "constants" for each of | |
82 | // the named type's generic params. Instantiate the named type | |
83 | // with the fresh constants, yielding `named_skolem`. | |
84 | // | |
85 | // 2. Create unification variables for each of the Drop impl's | |
86 | // generic params. Instantiate the impl's Self's type with the | |
87 | // unification-vars, yielding `drop_unifier`. | |
88 | // | |
89 | // 3. Attempt to unify Self_unif with Type_skolem. If unification | |
90 | // succeeds, continue (i.e. with the predicate checks). | |
91 | ||
92 | let ty::TypeScheme { generics: ref named_type_generics, | |
93 | ty: named_type } = | |
94 | ty::lookup_item_type(tcx, self_type_did); | |
95 | ||
96 | let infcx = infer::new_infer_ctxt(tcx); | |
97 | infcx.commit_if_ok(|snapshot| { | |
98 | let (named_type_to_skolem, skol_map) = | |
99 | infcx.construct_skolemized_subst(named_type_generics, snapshot); | |
100 | let named_type_skolem = named_type.subst(tcx, &named_type_to_skolem); | |
101 | ||
102 | let drop_impl_span = tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP); | |
103 | let drop_to_unifier = | |
104 | infcx.fresh_substs_for_generics(drop_impl_span, drop_impl_generics); | |
105 | let drop_unifier = drop_impl_ty.subst(tcx, &drop_to_unifier); | |
106 | ||
107 | if let Ok(()) = infer::mk_eqty(&infcx, true, infer::TypeOrigin::Misc(drop_impl_span), | |
108 | named_type_skolem, drop_unifier) { | |
109 | // Even if we did manage to equate the types, the process | |
110 | // may have just gathered unsolvable region constraints | |
111 | // like `R == 'static` (represented as a pair of subregion | |
112 | // constraints) for some skolemization constant R. | |
113 | // | |
114 | // However, the leak_check method allows us to confirm | |
115 | // that no skolemized regions escaped (i.e. were related | |
116 | // to other regions in the constraint graph). | |
117 | if let Ok(()) = infcx.leak_check(&skol_map, snapshot) { | |
118 | return Ok(()) | |
119 | } | |
120 | } | |
121 | ||
122 | span_err!(tcx.sess, drop_impl_span, E0366, | |
123 | "Implementations of Drop cannot be specialized"); | |
124 | let item_span = tcx.map.span(self_type_did.node); | |
125 | tcx.sess.span_note(item_span, | |
126 | "Use same sequence of generic type and region \ | |
127 | parameters that is on the struct/enum definition"); | |
128 | return Err(()); | |
129 | }) | |
130 | } | |
131 | ||
132 | /// Confirms that every predicate imposed by dtor_predicates is | |
133 | /// implied by assuming the predicates attached to self_type_did. | |
134 | fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( | |
135 | tcx: &ty::ctxt<'tcx>, | |
136 | drop_impl_did: ast::DefId, | |
137 | dtor_predicates: &ty::GenericPredicates<'tcx>, | |
138 | self_type_did: ast::DefId, | |
139 | self_to_impl_substs: &subst::Substs<'tcx>) -> Result<(), ()> { | |
140 | ||
141 | // Here is an example, analogous to that from | |
142 | // `compare_impl_method`. | |
143 | // | |
144 | // Consider a struct type: | |
145 | // | |
146 | // struct Type<'c, 'b:'c, 'a> { | |
147 | // x: &'a Contents // (contents are irrelevant; | |
148 | // y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.) | |
149 | // } | |
150 | // | |
151 | // and a Drop impl: | |
152 | // | |
153 | // impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> { | |
154 | // fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y) | |
155 | // } | |
156 | // | |
157 | // We start out with self_to_impl_substs, that maps the generic | |
158 | // parameters of Type to that of the Drop impl. | |
159 | // | |
160 | // self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x} | |
161 | // | |
162 | // Applying this to the predicates (i.e. assumptions) provided by the item | |
163 | // definition yields the instantiated assumptions: | |
164 | // | |
165 | // ['y : 'z] | |
166 | // | |
167 | // We then check all of the predicates of the Drop impl: | |
168 | // | |
169 | // ['y:'z, 'x:'y] | |
170 | // | |
171 | // and ensure each is in the list of instantiated | |
172 | // assumptions. Here, `'y:'z` is present, but `'x:'y` is | |
173 | // absent. So we report an error that the Drop impl injected a | |
174 | // predicate that is not present on the struct definition. | |
175 | ||
176 | assert_eq!(self_type_did.krate, ast::LOCAL_CRATE); | |
177 | ||
178 | let drop_impl_span = tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP); | |
179 | ||
180 | // We can assume the predicates attached to struct/enum definition | |
181 | // hold. | |
182 | let generic_assumptions = ty::lookup_predicates(tcx, self_type_did); | |
183 | ||
184 | let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs); | |
185 | assert!(assumptions_in_impl_context.predicates.is_empty_in(subst::SelfSpace)); | |
186 | assert!(assumptions_in_impl_context.predicates.is_empty_in(subst::FnSpace)); | |
187 | let assumptions_in_impl_context = | |
188 | assumptions_in_impl_context.predicates.get_slice(subst::TypeSpace); | |
189 | ||
190 | // An earlier version of this code attempted to do this checking | |
191 | // via the traits::fulfill machinery. However, it ran into trouble | |
192 | // since the fulfill machinery merely turns outlives-predicates | |
193 | // 'a:'b and T:'b into region inference constraints. It is simpler | |
194 | // just to look for all the predicates directly. | |
195 | ||
196 | assert!(dtor_predicates.predicates.is_empty_in(subst::SelfSpace)); | |
197 | assert!(dtor_predicates.predicates.is_empty_in(subst::FnSpace)); | |
198 | let predicates = dtor_predicates.predicates.get_slice(subst::TypeSpace); | |
199 | for predicate in predicates { | |
200 | // (We do not need to worry about deep analysis of type | |
201 | // expressions etc because the Drop impls are already forced | |
202 | // to take on a structure that is roughly a alpha-renaming of | |
203 | // the generic parameters of the item definition.) | |
204 | ||
205 | // This path now just checks *all* predicates via the direct | |
206 | // lookup, rather than using fulfill machinery. | |
207 | // | |
208 | // However, it may be more efficient in the future to batch | |
209 | // the analysis together via the fulfill , rather than the | |
210 | // repeated `contains` calls. | |
211 | ||
212 | if !assumptions_in_impl_context.contains(&predicate) { | |
213 | let item_span = tcx.map.span(self_type_did.node); | |
c34b1796 | 214 | span_err!(tcx.sess, drop_impl_span, E0367, |
62682a34 | 215 | "The requirement `{}` is added only by the Drop impl.", predicate); |
c34b1796 AL |
216 | tcx.sess.span_note(item_span, |
217 | "The same requirement must be part of \ | |
218 | the struct/enum definition"); | |
219 | } | |
220 | } | |
85aaf69f | 221 | |
c34b1796 AL |
222 | if tcx.sess.has_errors() { |
223 | return Err(()); | |
224 | } | |
225 | Ok(()) | |
226 | } | |
85aaf69f | 227 | |
c34b1796 AL |
228 | /// check_safety_of_destructor_if_necessary confirms that the type |
229 | /// expression `typ` conforms to the "Drop Check Rule" from the Sound | |
230 | /// Generic Drop (RFC 769). | |
231 | /// | |
232 | /// ---- | |
233 | /// | |
234 | /// The Drop Check Rule is the following: | |
235 | /// | |
236 | /// Let `v` be some value (either temporary or named) and 'a be some | |
237 | /// lifetime (scope). If the type of `v` owns data of type `D`, where | |
238 | /// | |
62682a34 SL |
239 | /// * (1.) `D` has a lifetime- or type-parametric Drop implementation, and |
240 | /// * (2.) the structure of `D` can reach a reference of type `&'a _`, and | |
241 | /// * (3.) either: | |
242 | /// * (A.) the Drop impl for `D` instantiates `D` at 'a directly, | |
c34b1796 | 243 | /// i.e. `D<'a>`, or, |
62682a34 | 244 | /// * (B.) the Drop impl for `D` has some type parameter with a |
c34b1796 AL |
245 | /// trait bound `T` where `T` is a trait that has at least |
246 | /// one method, | |
247 | /// | |
248 | /// then 'a must strictly outlive the scope of v. | |
249 | /// | |
250 | /// ---- | |
251 | /// | |
252 | /// This function is meant to by applied to the type for every | |
253 | /// expression in the program. | |
85aaf69f SL |
254 | pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, |
255 | typ: ty::Ty<'tcx>, | |
256 | span: Span, | |
257 | scope: region::CodeExtent) { | |
62682a34 SL |
258 | debug!("check_safety_of_destructor_if_necessary typ: {:?} scope: {:?}", |
259 | typ, scope); | |
85aaf69f SL |
260 | |
261 | // types that have been traversed so far by `traverse_type_if_unseen` | |
262 | let mut breadcrumbs: Vec<Ty<'tcx>> = Vec::new(); | |
263 | ||
c34b1796 | 264 | let result = iterate_over_potentially_unsafe_regions_in_type( |
85aaf69f SL |
265 | rcx, |
266 | &mut breadcrumbs, | |
c34b1796 | 267 | TypeContext::Root, |
85aaf69f SL |
268 | typ, |
269 | span, | |
270 | scope, | |
c34b1796 | 271 | 0, |
85aaf69f | 272 | 0); |
c34b1796 AL |
273 | match result { |
274 | Ok(()) => {} | |
275 | Err(Error::Overflow(ref ctxt, ref detected_on_typ)) => { | |
276 | let tcx = rcx.tcx(); | |
277 | span_err!(tcx.sess, span, E0320, | |
62682a34 | 278 | "overflow while adding drop-check rules for {}", typ); |
c34b1796 AL |
279 | match *ctxt { |
280 | TypeContext::Root => { | |
281 | // no need for an additional note if the overflow | |
282 | // was somehow on the root. | |
283 | } | |
284 | TypeContext::EnumVariant { def_id, variant, arg_index } => { | |
285 | // FIXME (pnkfelix): eventually lookup arg_name | |
286 | // for the given index on struct variants. | |
287 | span_note!( | |
288 | rcx.tcx().sess, | |
289 | span, | |
290 | "overflowed on enum {} variant {} argument {} type: {}", | |
291 | ty::item_path_str(tcx, def_id), | |
292 | variant, | |
293 | arg_index, | |
62682a34 | 294 | detected_on_typ); |
c34b1796 AL |
295 | } |
296 | TypeContext::Struct { def_id, field } => { | |
297 | span_note!( | |
298 | rcx.tcx().sess, | |
299 | span, | |
300 | "overflowed on struct {} field {} type: {}", | |
301 | ty::item_path_str(tcx, def_id), | |
302 | field, | |
62682a34 | 303 | detected_on_typ); |
c34b1796 AL |
304 | } |
305 | } | |
306 | } | |
307 | } | |
308 | } | |
309 | ||
310 | enum Error<'tcx> { | |
311 | Overflow(TypeContext, ty::Ty<'tcx>), | |
85aaf69f SL |
312 | } |
313 | ||
c34b1796 AL |
314 | enum TypeContext { |
315 | Root, | |
316 | EnumVariant { | |
317 | def_id: ast::DefId, | |
318 | variant: ast::Name, | |
319 | arg_index: usize, | |
320 | }, | |
321 | Struct { | |
322 | def_id: ast::DefId, | |
323 | field: ast::Name, | |
324 | } | |
325 | } | |
326 | ||
327 | // The `depth` counts the number of calls to this function; | |
328 | // the `xref_depth` counts the subset of such calls that go | |
329 | // across a `Box<T>` or `PhantomData<T>`. | |
85aaf69f SL |
330 | fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>( |
331 | rcx: &mut Rcx<'a, 'tcx>, | |
332 | breadcrumbs: &mut Vec<Ty<'tcx>>, | |
c34b1796 | 333 | context: TypeContext, |
85aaf69f SL |
334 | ty_root: ty::Ty<'tcx>, |
335 | span: Span, | |
336 | scope: region::CodeExtent, | |
c34b1796 AL |
337 | depth: usize, |
338 | xref_depth: usize) -> Result<(), Error<'tcx>> | |
85aaf69f | 339 | { |
c34b1796 AL |
340 | // Issue #22443: Watch out for overflow. While we are careful to |
341 | // handle regular types properly, non-regular ones cause problems. | |
342 | let recursion_limit = rcx.tcx().sess.recursion_limit.get(); | |
343 | if xref_depth >= recursion_limit { | |
344 | return Err(Error::Overflow(context, ty_root)) | |
345 | } | |
346 | ||
347 | let origin = || infer::SubregionOrigin::SafeDestructor(span); | |
85aaf69f SL |
348 | let mut walker = ty_root.walk(); |
349 | let opt_phantom_data_def_id = rcx.tcx().lang_items.phantom_data(); | |
350 | ||
351 | let destructor_for_type = rcx.tcx().destructor_for_type.borrow(); | |
352 | ||
c34b1796 AL |
353 | let xref_depth_orig = xref_depth; |
354 | ||
85aaf69f SL |
355 | while let Some(typ) = walker.next() { |
356 | // Avoid recursing forever. | |
357 | if breadcrumbs.contains(&typ) { | |
358 | continue; | |
359 | } | |
360 | breadcrumbs.push(typ); | |
361 | ||
362 | // If we encounter `PhantomData<T>`, then we should replace it | |
363 | // with `T`, the type it represents as owned by the | |
364 | // surrounding context, before doing further analysis. | |
c34b1796 | 365 | let (typ, xref_depth) = match typ.sty { |
62682a34 | 366 | ty::TyStruct(struct_did, substs) => { |
c34b1796 AL |
367 | if opt_phantom_data_def_id == Some(struct_did) { |
368 | let item_type = ty::lookup_item_type(rcx.tcx(), struct_did); | |
369 | let tp_def = item_type.generics.types | |
370 | .opt_get(subst::TypeSpace, 0).unwrap(); | |
371 | let new_typ = substs.type_for_def(tp_def); | |
62682a34 SL |
372 | debug!("replacing phantom {:?} with {:?}", |
373 | typ, new_typ); | |
c34b1796 AL |
374 | (new_typ, xref_depth_orig + 1) |
375 | } else { | |
376 | (typ, xref_depth_orig) | |
377 | } | |
378 | } | |
379 | ||
62682a34 | 380 | // Note: When TyBox is removed from compiler, the |
c34b1796 AL |
381 | // definition of `Box<T>` must carry a PhantomData that |
382 | // puts us into the previous case. | |
62682a34 SL |
383 | ty::TyBox(new_typ) => { |
384 | debug!("replacing TyBox {:?} with {:?}", | |
385 | typ, new_typ); | |
c34b1796 AL |
386 | (new_typ, xref_depth_orig + 1) |
387 | } | |
388 | ||
389 | _ => { | |
390 | (typ, xref_depth_orig) | |
85aaf69f | 391 | } |
85aaf69f SL |
392 | }; |
393 | ||
bd371182 | 394 | let dtor_kind = match typ.sty { |
62682a34 SL |
395 | ty::TyEnum(def_id, _) | |
396 | ty::TyStruct(def_id, _) => { | |
bd371182 AL |
397 | match destructor_for_type.get(&def_id) { |
398 | Some(def_id) => DtorKind::KnownDropMethod(*def_id), | |
399 | None => DtorKind::PureRecur, | |
400 | } | |
401 | } | |
62682a34 | 402 | ty::TyTrait(ref ty_trait) => { |
bd371182 AL |
403 | DtorKind::Unknown(ty_trait.bounds.clone()) |
404 | } | |
405 | _ => DtorKind::PureRecur, | |
85aaf69f SL |
406 | }; |
407 | ||
85aaf69f | 408 | debug!("iterate_over_potentially_unsafe_regions_in_type \ |
bd371182 | 409 | {}typ: {} scope: {:?} xref: {}", |
85aaf69f | 410 | (0..depth).map(|_| ' ').collect::<String>(), |
62682a34 | 411 | typ, scope, xref_depth); |
85aaf69f SL |
412 | |
413 | // If `typ` has a destructor, then we must ensure that all | |
414 | // borrowed data reachable via `typ` must outlive the parent | |
415 | // of `scope`. This is handled below. | |
416 | // | |
417 | // However, there is an important special case: by | |
418 | // parametricity, any generic type parameters have *no* trait | |
419 | // bounds in the Drop impl can not be used in any way (apart | |
420 | // from being dropped), and thus we can treat data borrowed | |
421 | // via such type parameters remains unreachable. | |
422 | // | |
423 | // For example, consider `impl<T> Drop for Vec<T> { ... }`, | |
424 | // which does have to be able to drop instances of `T`, but | |
425 | // otherwise cannot read data from `T`. | |
426 | // | |
427 | // Of course, for the type expression passed in for any such | |
428 | // unbounded type parameter `T`, we must resume the recursive | |
429 | // analysis on `T` (since it would be ignored by | |
430 | // type_must_outlive). | |
431 | // | |
432 | // FIXME (pnkfelix): Long term, we could be smart and actually | |
433 | // feed which generic parameters can be ignored *into* `fn | |
434 | // type_must_outlive` (or some generalization thereof). But | |
435 | // for the short term, it probably covers most cases of | |
436 | // interest to just special case Drop impls where: (1.) there | |
437 | // are no generic lifetime parameters and (2.) *all* generic | |
438 | // type parameters are unbounded. If both conditions hold, we | |
439 | // simply skip the `type_must_outlive` call entirely (but | |
440 | // resume the recursive checking of the type-substructure). | |
441 | ||
bd371182 | 442 | if has_dtor_of_interest(rcx.tcx(), dtor_kind, typ, span) { |
85aaf69f SL |
443 | // If `typ` has a destructor, then we must ensure that all |
444 | // borrowed data reachable via `typ` must outlive the | |
445 | // parent of `scope`. (It does not suffice for it to | |
446 | // outlive `scope` because that could imply that the | |
447 | // borrowed data is torn down in between the end of | |
448 | // `scope` and when the destructor itself actually runs.) | |
449 | ||
450 | let parent_region = | |
451 | match rcx.tcx().region_maps.opt_encl_scope(scope) { | |
452 | Some(parent_scope) => ty::ReScope(parent_scope), | |
453 | None => rcx.tcx().sess.span_bug( | |
c34b1796 AL |
454 | span, &format!("no enclosing scope found for scope: {:?}", |
455 | scope)), | |
85aaf69f SL |
456 | }; |
457 | ||
458 | regionck::type_must_outlive(rcx, origin(), typ, parent_region); | |
459 | ||
460 | } else { | |
461 | // Okay, `typ` itself is itself not reachable by a | |
462 | // destructor; but it may contain substructure that has a | |
463 | // destructor. | |
464 | ||
465 | match typ.sty { | |
62682a34 SL |
466 | ty::TyStruct(struct_did, substs) => { |
467 | debug!("typ: {:?} is struct; traverse structure and not type-expression", | |
468 | typ); | |
85aaf69f SL |
469 | // Don't recurse; we extract type's substructure, |
470 | // so do not process subparts of type expression. | |
471 | walker.skip_current_subtree(); | |
472 | ||
473 | let fields = | |
474 | ty::lookup_struct_fields(rcx.tcx(), struct_did); | |
62682a34 | 475 | for field in &fields { |
85aaf69f SL |
476 | let field_type = |
477 | ty::lookup_field_type(rcx.tcx(), | |
478 | struct_did, | |
479 | field.id, | |
480 | substs); | |
c34b1796 | 481 | try!(iterate_over_potentially_unsafe_regions_in_type( |
85aaf69f SL |
482 | rcx, |
483 | breadcrumbs, | |
c34b1796 AL |
484 | TypeContext::Struct { |
485 | def_id: struct_did, | |
486 | field: field.name, | |
487 | }, | |
85aaf69f SL |
488 | field_type, |
489 | span, | |
490 | scope, | |
c34b1796 AL |
491 | depth+1, |
492 | xref_depth)) | |
85aaf69f SL |
493 | } |
494 | } | |
495 | ||
62682a34 SL |
496 | ty::TyEnum(enum_did, substs) => { |
497 | debug!("typ: {:?} is enum; traverse structure and not type-expression", | |
498 | typ); | |
85aaf69f SL |
499 | // Don't recurse; we extract type's substructure, |
500 | // so do not process subparts of type expression. | |
501 | walker.skip_current_subtree(); | |
502 | ||
503 | let all_variant_info = | |
504 | ty::substd_enum_variants(rcx.tcx(), | |
505 | enum_did, | |
506 | substs); | |
62682a34 | 507 | for variant_info in &all_variant_info { |
c34b1796 AL |
508 | for (i, arg_type) in variant_info.args.iter().enumerate() { |
509 | try!(iterate_over_potentially_unsafe_regions_in_type( | |
85aaf69f SL |
510 | rcx, |
511 | breadcrumbs, | |
c34b1796 AL |
512 | TypeContext::EnumVariant { |
513 | def_id: enum_did, | |
514 | variant: variant_info.name, | |
515 | arg_index: i, | |
516 | }, | |
517 | *arg_type, | |
85aaf69f SL |
518 | span, |
519 | scope, | |
c34b1796 AL |
520 | depth+1, |
521 | xref_depth)); | |
85aaf69f SL |
522 | } |
523 | } | |
524 | } | |
525 | ||
62682a34 | 526 | ty::TyRef(..) | ty::TyRawPtr(_) | ty::TyBareFn(..) => { |
85aaf69f | 527 | // Don't recurse, since references, pointers, |
bd371182 | 528 | // and bare functions don't own instances |
85aaf69f SL |
529 | // of the types appearing within them. |
530 | walker.skip_current_subtree(); | |
531 | } | |
532 | _ => {} | |
533 | }; | |
534 | ||
535 | // You might be tempted to pop breadcrumbs here after | |
536 | // processing type's internals above, but then you hit | |
537 | // exponential time blowup e.g. on | |
538 | // compile-fail/huge-struct.rs. Instead, we do not remove | |
539 | // anything from the breadcrumbs vector during any particular | |
540 | // traversal, and instead clear it after the whole traversal | |
541 | // is done. | |
542 | } | |
543 | } | |
c34b1796 AL |
544 | |
545 | return Ok(()); | |
85aaf69f | 546 | } |
bd371182 AL |
547 | |
548 | enum DtorKind<'tcx> { | |
549 | // Type has an associated drop method with this def id | |
550 | KnownDropMethod(ast::DefId), | |
551 | ||
552 | // Type has no destructor (or its dtor is known to be pure | |
553 | // with respect to lifetimes), though its *substructure* | |
554 | // may carry a destructor. | |
555 | PureRecur, | |
556 | ||
557 | // Type may have impure destructor that is unknown; | |
558 | // e.g. `Box<Trait+'a>` | |
559 | Unknown(ty::ExistentialBounds<'tcx>), | |
560 | } | |
561 | ||
562 | fn has_dtor_of_interest<'tcx>(tcx: &ty::ctxt<'tcx>, | |
563 | dtor_kind: DtorKind, | |
564 | typ: ty::Ty<'tcx>, | |
565 | span: Span) -> bool { | |
566 | let has_dtor_of_interest: bool; | |
567 | ||
568 | match dtor_kind { | |
569 | DtorKind::PureRecur => { | |
d9579d0f | 570 | has_dtor_of_interest = false; |
62682a34 SL |
571 | debug!("typ: {:?} has no dtor, and thus is uninteresting", |
572 | typ); | |
bd371182 AL |
573 | } |
574 | DtorKind::Unknown(bounds) => { | |
575 | match bounds.region_bound { | |
576 | ty::ReStatic => { | |
62682a34 SL |
577 | debug!("trait: {:?} has 'static bound, and thus is uninteresting", |
578 | typ); | |
bd371182 AL |
579 | has_dtor_of_interest = false; |
580 | } | |
581 | ty::ReEmpty => { | |
62682a34 SL |
582 | debug!("trait: {:?} has empty region bound, and thus is uninteresting", |
583 | typ); | |
bd371182 AL |
584 | has_dtor_of_interest = false; |
585 | } | |
586 | r => { | |
62682a34 SL |
587 | debug!("trait: {:?} has non-static bound: {:?}; assumed interesting", |
588 | typ, r); | |
bd371182 AL |
589 | has_dtor_of_interest = true; |
590 | } | |
591 | } | |
592 | } | |
593 | DtorKind::KnownDropMethod(dtor_method_did) => { | |
594 | let impl_did = ty::impl_of_method(tcx, dtor_method_did) | |
595 | .unwrap_or_else(|| { | |
596 | tcx.sess.span_bug( | |
597 | span, "no Drop impl found for drop method") | |
598 | }); | |
599 | ||
600 | let dtor_typescheme = ty::lookup_item_type(tcx, impl_did); | |
601 | let dtor_generics = dtor_typescheme.generics; | |
d9579d0f AL |
602 | |
603 | let mut has_pred_of_interest = false; | |
604 | ||
605 | let mut seen_items = Vec::new(); | |
606 | let mut items_to_inspect = vec![impl_did]; | |
607 | 'items: while let Some(item_def_id) = items_to_inspect.pop() { | |
608 | if seen_items.contains(&item_def_id) { | |
609 | continue; | |
610 | } | |
611 | ||
612 | for pred in ty::lookup_predicates(tcx, item_def_id).predicates { | |
613 | let result = match pred { | |
614 | ty::Predicate::Equate(..) | | |
615 | ty::Predicate::RegionOutlives(..) | | |
616 | ty::Predicate::TypeOutlives(..) | | |
617 | ty::Predicate::Projection(..) => { | |
618 | // For now, assume all these where-clauses | |
619 | // may give drop implementation capabilty | |
620 | // to access borrowed data. | |
621 | true | |
bd371182 | 622 | } |
bd371182 | 623 | |
d9579d0f AL |
624 | ty::Predicate::Trait(ty::Binder(ref t_pred)) => { |
625 | let def_id = t_pred.trait_ref.def_id; | |
626 | if ty::trait_items(tcx, def_id).len() != 0 { | |
627 | // If trait has items, assume it adds | |
628 | // capability to access borrowed data. | |
629 | true | |
630 | } else { | |
631 | // Trait without items is itself | |
632 | // uninteresting from POV of dropck. | |
633 | // | |
634 | // However, may have parent w/ items; | |
635 | // so schedule checking of predicates, | |
636 | items_to_inspect.push(def_id); | |
637 | // and say "no capability found" for now. | |
638 | false | |
639 | } | |
640 | } | |
641 | }; | |
642 | ||
643 | if result { | |
644 | has_pred_of_interest = true; | |
62682a34 SL |
645 | debug!("typ: {:?} has interesting dtor due to generic preds, e.g. {:?}", |
646 | typ, pred); | |
d9579d0f AL |
647 | break 'items; |
648 | } | |
bd371182 AL |
649 | } |
650 | ||
d9579d0f AL |
651 | seen_items.push(item_def_id); |
652 | } | |
bd371182 AL |
653 | |
654 | // In `impl<'a> Drop ...`, we automatically assume | |
655 | // `'a` is meaningful and thus represents a bound | |
656 | // through which we could reach borrowed data. | |
657 | // | |
658 | // FIXME (pnkfelix): In the future it would be good to | |
659 | // extend the language to allow the user to express, | |
660 | // in the impl signature, that a lifetime is not | |
661 | // actually used (something like `where 'a: ?Live`). | |
662 | let has_region_param_of_interest = | |
663 | dtor_generics.has_region_params(subst::TypeSpace); | |
664 | ||
665 | has_dtor_of_interest = | |
666 | has_region_param_of_interest || | |
667 | has_pred_of_interest; | |
668 | ||
669 | if has_dtor_of_interest { | |
62682a34 | 670 | debug!("typ: {:?} has interesting dtor, due to \ |
bd371182 | 671 | region params: {} or pred: {}", |
62682a34 | 672 | typ, |
bd371182 AL |
673 | has_region_param_of_interest, |
674 | has_pred_of_interest); | |
675 | } else { | |
62682a34 SL |
676 | debug!("typ: {:?} has dtor, but it is uninteresting", |
677 | typ); | |
bd371182 AL |
678 | } |
679 | } | |
680 | } | |
681 | ||
682 | return has_dtor_of_interest; | |
683 | } |