]>
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 | ||
a7813a04 XL |
11 | use CrateCtxt; |
12 | use check::regionck::RegionCtxt; | |
85aaf69f | 13 | |
54a0048b | 14 | use hir::def_id::DefId; |
e9174d1e | 15 | use middle::free_region::FreeRegionMap; |
c30ab7b3 | 16 | use rustc::infer::{self, InferOk}; |
85aaf69f | 17 | use middle::region; |
9e0c209e SL |
18 | use rustc::ty::subst::{Subst, Substs}; |
19 | use rustc::ty::{self, AdtKind, Ty, TyCtxt}; | |
5bcae85e | 20 | use rustc::traits::{self, Reveal}; |
c1a9b12d | 21 | use util::nodemap::FnvHashSet; |
c34b1796 AL |
22 | |
23 | use syntax::ast; | |
3157f602 | 24 | use syntax_pos::{self, Span}; |
c34b1796 AL |
25 | |
26 | /// check_drop_impl confirms that the Drop implementation identfied by | |
27 | /// `drop_impl_did` is not any more specialized than the type it is | |
28 | /// attached to (Issue #8142). | |
29 | /// | |
30 | /// This means: | |
31 | /// | |
32 | /// 1. The self type must be nominal (this is already checked during | |
33 | /// coherence), | |
34 | /// | |
35 | /// 2. The generic region/type parameters of the impl's self-type must | |
36 | /// all be parameters of the Drop impl itself (i.e. no | |
37 | /// specialization like `impl Drop for Foo<i32>`), and, | |
38 | /// | |
39 | /// 3. Any bounds on the generic parameters must be reflected in the | |
40 | /// struct/enum definition for the nominal type itself (i.e. | |
41 | /// cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`). | |
42 | /// | |
a7813a04 | 43 | pub fn check_drop_impl(ccx: &CrateCtxt, drop_impl_did: DefId) -> Result<(), ()> { |
9e0c209e | 44 | let dtor_self_type = ccx.tcx.lookup_item_type(drop_impl_did).ty; |
a7813a04 | 45 | let dtor_predicates = ccx.tcx.lookup_predicates(drop_impl_did); |
c34b1796 | 46 | match dtor_self_type.sty { |
9e0c209e | 47 | ty::TyAdt(adt_def, self_to_impl_substs) => { |
a7813a04 | 48 | ensure_drop_params_and_item_params_correspond(ccx, |
54a0048b | 49 | drop_impl_did, |
9e0c209e | 50 | dtor_self_type, |
54a0048b | 51 | adt_def.did)?; |
c34b1796 | 52 | |
a7813a04 | 53 | ensure_drop_predicates_are_implied_by_item_defn(ccx, |
c34b1796 AL |
54 | drop_impl_did, |
55 | &dtor_predicates, | |
e9174d1e | 56 | adt_def.did, |
c34b1796 AL |
57 | self_to_impl_substs) |
58 | } | |
59 | _ => { | |
60 | // Destructors only work on nominal types. This was | |
61 | // already checked by coherence, so we can panic here. | |
3157f602 | 62 | let span = ccx.tcx.map.def_id_span(drop_impl_did, syntax_pos::DUMMY_SP); |
54a0048b SL |
63 | span_bug!(span, |
64 | "should have been rejected by coherence check: {}", | |
65 | dtor_self_type); | |
c34b1796 AL |
66 | } |
67 | } | |
68 | } | |
69 | ||
a7813a04 XL |
70 | fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>( |
71 | ccx: &CrateCtxt<'a, 'tcx>, | |
e9174d1e | 72 | drop_impl_did: DefId, |
9e0c209e | 73 | drop_impl_ty: Ty<'tcx>, |
c30ab7b3 SL |
74 | self_type_did: DefId) |
75 | -> Result<(), ()> | |
c34b1796 | 76 | { |
a7813a04 | 77 | let tcx = ccx.tcx; |
b039eaaf SL |
78 | let drop_impl_node_id = tcx.map.as_local_node_id(drop_impl_did).unwrap(); |
79 | let self_type_node_id = tcx.map.as_local_node_id(self_type_did).unwrap(); | |
e9174d1e SL |
80 | |
81 | // check that the impl type can be made to match the trait type. | |
82 | ||
b039eaaf | 83 | let impl_param_env = ty::ParameterEnvironment::for_item(tcx, self_type_node_id); |
5bcae85e | 84 | tcx.infer_ctxt(None, Some(impl_param_env), Reveal::NotSpecializable).enter(|infcx| { |
a7813a04 XL |
85 | let tcx = infcx.tcx; |
86 | let mut fulfillment_cx = traits::FulfillmentContext::new(); | |
e9174d1e | 87 | |
a7813a04 XL |
88 | let named_type = tcx.lookup_item_type(self_type_did).ty; |
89 | let named_type = named_type.subst(tcx, &infcx.parameter_environment.free_substs); | |
e9174d1e | 90 | |
3157f602 | 91 | let drop_impl_span = tcx.map.def_id_span(drop_impl_did, syntax_pos::DUMMY_SP); |
a7813a04 | 92 | let fresh_impl_substs = |
9e0c209e SL |
93 | infcx.fresh_substs_for_item(drop_impl_span, drop_impl_did); |
94 | let fresh_impl_self_ty = drop_impl_ty.subst(tcx, fresh_impl_substs); | |
e9174d1e | 95 | |
c30ab7b3 SL |
96 | match infcx.eq_types(true, infer::TypeOrigin::Misc(drop_impl_span), |
97 | named_type, fresh_impl_self_ty) { | |
98 | Ok(InferOk { obligations, .. }) => { | |
99 | // FIXME(#32730) propagate obligations | |
100 | assert!(obligations.is_empty()); | |
101 | } | |
102 | Err(_) => { | |
103 | let item_span = tcx.map.span(self_type_node_id); | |
104 | struct_span_err!(tcx.sess, drop_impl_span, E0366, | |
105 | "Implementations of Drop cannot be specialized") | |
106 | .span_note(item_span, | |
107 | "Use same sequence of generic type and region \ | |
108 | parameters that is on the struct/enum definition") | |
109 | .emit(); | |
110 | return Err(()); | |
111 | } | |
a7813a04 XL |
112 | } |
113 | ||
114 | if let Err(ref errors) = fulfillment_cx.select_all_or_error(&infcx) { | |
115 | // this could be reached when we get lazy normalization | |
116 | infcx.report_fulfillment_errors(errors); | |
117 | return Err(()); | |
118 | } | |
119 | ||
a7813a04 XL |
120 | let free_regions = FreeRegionMap::new(); |
121 | infcx.resolve_regions_and_report_errors(&free_regions, drop_impl_node_id); | |
122 | Ok(()) | |
123 | }) | |
c34b1796 AL |
124 | } |
125 | ||
126 | /// Confirms that every predicate imposed by dtor_predicates is | |
127 | /// implied by assuming the predicates attached to self_type_did. | |
a7813a04 XL |
128 | fn ensure_drop_predicates_are_implied_by_item_defn<'a, 'tcx>( |
129 | ccx: &CrateCtxt<'a, 'tcx>, | |
e9174d1e | 130 | drop_impl_did: DefId, |
c34b1796 | 131 | dtor_predicates: &ty::GenericPredicates<'tcx>, |
e9174d1e | 132 | self_type_did: DefId, |
c30ab7b3 SL |
133 | self_to_impl_substs: &Substs<'tcx>) |
134 | -> Result<(), ()> | |
135 | { | |
c34b1796 AL |
136 | |
137 | // Here is an example, analogous to that from | |
138 | // `compare_impl_method`. | |
139 | // | |
140 | // Consider a struct type: | |
141 | // | |
142 | // struct Type<'c, 'b:'c, 'a> { | |
143 | // x: &'a Contents // (contents are irrelevant; | |
144 | // y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.) | |
145 | // } | |
146 | // | |
147 | // and a Drop impl: | |
148 | // | |
149 | // impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> { | |
150 | // fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y) | |
151 | // } | |
152 | // | |
153 | // We start out with self_to_impl_substs, that maps the generic | |
154 | // parameters of Type to that of the Drop impl. | |
155 | // | |
156 | // self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x} | |
157 | // | |
158 | // Applying this to the predicates (i.e. assumptions) provided by the item | |
159 | // definition yields the instantiated assumptions: | |
160 | // | |
161 | // ['y : 'z] | |
162 | // | |
163 | // We then check all of the predicates of the Drop impl: | |
164 | // | |
165 | // ['y:'z, 'x:'y] | |
166 | // | |
167 | // and ensure each is in the list of instantiated | |
168 | // assumptions. Here, `'y:'z` is present, but `'x:'y` is | |
169 | // absent. So we report an error that the Drop impl injected a | |
170 | // predicate that is not present on the struct definition. | |
171 | ||
a7813a04 XL |
172 | let tcx = ccx.tcx; |
173 | ||
b039eaaf | 174 | let self_type_node_id = tcx.map.as_local_node_id(self_type_did).unwrap(); |
c34b1796 | 175 | |
3157f602 | 176 | let drop_impl_span = tcx.map.def_id_span(drop_impl_did, syntax_pos::DUMMY_SP); |
c34b1796 AL |
177 | |
178 | // We can assume the predicates attached to struct/enum definition | |
179 | // hold. | |
c1a9b12d | 180 | let generic_assumptions = tcx.lookup_predicates(self_type_did); |
c34b1796 AL |
181 | |
182 | let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs); | |
9e0c209e | 183 | let assumptions_in_impl_context = assumptions_in_impl_context.predicates; |
c34b1796 AL |
184 | |
185 | // An earlier version of this code attempted to do this checking | |
186 | // via the traits::fulfill machinery. However, it ran into trouble | |
187 | // since the fulfill machinery merely turns outlives-predicates | |
188 | // 'a:'b and T:'b into region inference constraints. It is simpler | |
189 | // just to look for all the predicates directly. | |
190 | ||
9e0c209e SL |
191 | assert_eq!(dtor_predicates.parent, None); |
192 | for predicate in &dtor_predicates.predicates { | |
c34b1796 AL |
193 | // (We do not need to worry about deep analysis of type |
194 | // expressions etc because the Drop impls are already forced | |
b039eaaf | 195 | // to take on a structure that is roughly an alpha-renaming of |
c34b1796 AL |
196 | // the generic parameters of the item definition.) |
197 | ||
198 | // This path now just checks *all* predicates via the direct | |
199 | // lookup, rather than using fulfill machinery. | |
200 | // | |
201 | // However, it may be more efficient in the future to batch | |
202 | // the analysis together via the fulfill , rather than the | |
203 | // repeated `contains` calls. | |
204 | ||
205 | if !assumptions_in_impl_context.contains(&predicate) { | |
b039eaaf | 206 | let item_span = tcx.map.span(self_type_node_id); |
9cc50fc6 SL |
207 | struct_span_err!(tcx.sess, drop_impl_span, E0367, |
208 | "The requirement `{}` is added only by the Drop impl.", predicate) | |
209 | .span_note(item_span, | |
210 | "The same requirement must be part of \ | |
211 | the struct/enum definition") | |
212 | .emit(); | |
c34b1796 AL |
213 | } |
214 | } | |
85aaf69f | 215 | |
c34b1796 AL |
216 | if tcx.sess.has_errors() { |
217 | return Err(()); | |
218 | } | |
219 | Ok(()) | |
220 | } | |
85aaf69f | 221 | |
c34b1796 AL |
222 | /// check_safety_of_destructor_if_necessary confirms that the type |
223 | /// expression `typ` conforms to the "Drop Check Rule" from the Sound | |
224 | /// Generic Drop (RFC 769). | |
225 | /// | |
226 | /// ---- | |
227 | /// | |
b039eaaf | 228 | /// The simplified (*) Drop Check Rule is the following: |
c34b1796 AL |
229 | /// |
230 | /// Let `v` be some value (either temporary or named) and 'a be some | |
231 | /// lifetime (scope). If the type of `v` owns data of type `D`, where | |
232 | /// | |
b039eaaf SL |
233 | /// * (1.) `D` has a lifetime- or type-parametric Drop implementation, |
234 | /// (where that `Drop` implementation does not opt-out of | |
235 | /// this check via the `unsafe_destructor_blind_to_params` | |
236 | /// attribute), and | |
237 | /// * (2.) the structure of `D` can reach a reference of type `&'a _`, | |
c34b1796 AL |
238 | /// |
239 | /// then 'a must strictly outlive the scope of v. | |
240 | /// | |
241 | /// ---- | |
242 | /// | |
243 | /// This function is meant to by applied to the type for every | |
244 | /// expression in the program. | |
b039eaaf SL |
245 | /// |
246 | /// ---- | |
247 | /// | |
248 | /// (*) The qualifier "simplified" is attached to the above | |
249 | /// definition of the Drop Check Rule, because it is a simplification | |
250 | /// of the original Drop Check rule, which attempted to prove that | |
251 | /// some `Drop` implementations could not possibly access data even if | |
252 | /// it was technically reachable, due to parametricity. | |
253 | /// | |
254 | /// However, (1.) parametricity on its own turned out to be a | |
255 | /// necessary but insufficient condition, and (2.) future changes to | |
256 | /// the language are expected to make it impossible to ensure that a | |
257 | /// `Drop` implementation is actually parametric with respect to any | |
258 | /// particular type parameter. (In particular, impl specialization is | |
259 | /// expected to break the needed parametricity property beyond | |
260 | /// repair.) | |
261 | /// | |
262 | /// Therefore we have scaled back Drop-Check to a more conservative | |
263 | /// rule that does not attempt to deduce whether a `Drop` | |
264 | /// implementation could not possible access data of a given lifetime; | |
265 | /// instead Drop-Check now simply assumes that if a destructor has | |
266 | /// access (direct or indirect) to a lifetime parameter, then that | |
267 | /// lifetime must be forced to outlive that destructor's dynamic | |
268 | /// extent. We then provide the `unsafe_destructor_blind_to_params` | |
269 | /// attribute as a way for destructor implementations to opt-out of | |
270 | /// this conservative assumption (and thus assume the obligation of | |
271 | /// ensuring that they do not access data nor invoke methods of | |
272 | /// values that have been previously dropped). | |
273 | /// | |
a7813a04 XL |
274 | pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>( |
275 | rcx: &mut RegionCtxt<'a, 'gcx, 'tcx>, | |
276 | typ: ty::Ty<'tcx>, | |
277 | span: Span, | |
278 | scope: region::CodeExtent) | |
279 | { | |
62682a34 SL |
280 | debug!("check_safety_of_destructor_if_necessary typ: {:?} scope: {:?}", |
281 | typ, scope); | |
85aaf69f | 282 | |
a7813a04 | 283 | let parent_scope = rcx.tcx.region_maps.opt_encl_scope(scope).unwrap_or_else(|| { |
54a0048b | 284 | span_bug!(span, "no enclosing scope found for scope: {:?}", scope) |
c1a9b12d | 285 | }); |
85aaf69f | 286 | |
c34b1796 | 287 | let result = iterate_over_potentially_unsafe_regions_in_type( |
c1a9b12d SL |
288 | &mut DropckContext { |
289 | rcx: rcx, | |
290 | span: span, | |
291 | parent_scope: parent_scope, | |
292 | breadcrumbs: FnvHashSet() | |
293 | }, | |
c34b1796 | 294 | TypeContext::Root, |
85aaf69f | 295 | typ, |
85aaf69f | 296 | 0); |
c34b1796 AL |
297 | match result { |
298 | Ok(()) => {} | |
299 | Err(Error::Overflow(ref ctxt, ref detected_on_typ)) => { | |
a7813a04 | 300 | let tcx = rcx.tcx; |
9cc50fc6 SL |
301 | let mut err = struct_span_err!(tcx.sess, span, E0320, |
302 | "overflow while adding drop-check rules for {}", typ); | |
c34b1796 AL |
303 | match *ctxt { |
304 | TypeContext::Root => { | |
305 | // no need for an additional note if the overflow | |
306 | // was somehow on the root. | |
307 | } | |
54a0048b | 308 | TypeContext::ADT { def_id, variant, field } => { |
e9174d1e SL |
309 | let adt = tcx.lookup_adt_def(def_id); |
310 | let variant_name = match adt.adt_kind() { | |
9e0c209e SL |
311 | AdtKind::Enum => format!("enum {} variant {}", |
312 | tcx.item_path_str(def_id), | |
313 | variant), | |
314 | AdtKind::Struct => format!("struct {}", | |
315 | tcx.item_path_str(def_id)), | |
316 | AdtKind::Union => format!("union {}", | |
317 | tcx.item_path_str(def_id)), | |
e9174d1e | 318 | }; |
c34b1796 | 319 | span_note!( |
9cc50fc6 | 320 | &mut err, |
c34b1796 | 321 | span, |
e9174d1e SL |
322 | "overflowed on {} field {} type: {}", |
323 | variant_name, | |
54a0048b | 324 | field, |
62682a34 | 325 | detected_on_typ); |
c34b1796 AL |
326 | } |
327 | } | |
9cc50fc6 | 328 | err.emit(); |
c34b1796 AL |
329 | } |
330 | } | |
331 | } | |
332 | ||
333 | enum Error<'tcx> { | |
334 | Overflow(TypeContext, ty::Ty<'tcx>), | |
85aaf69f SL |
335 | } |
336 | ||
c1a9b12d | 337 | #[derive(Copy, Clone)] |
c34b1796 AL |
338 | enum TypeContext { |
339 | Root, | |
e9174d1e SL |
340 | ADT { |
341 | def_id: DefId, | |
c34b1796 | 342 | variant: ast::Name, |
c34b1796 AL |
343 | field: ast::Name, |
344 | } | |
345 | } | |
346 | ||
a7813a04 XL |
347 | struct DropckContext<'a, 'b: 'a, 'gcx: 'b+'tcx, 'tcx: 'b> { |
348 | rcx: &'a mut RegionCtxt<'b, 'gcx, 'tcx>, | |
c1a9b12d SL |
349 | /// types that have already been traversed |
350 | breadcrumbs: FnvHashSet<Ty<'tcx>>, | |
351 | /// span for error reporting | |
85aaf69f | 352 | span: Span, |
c1a9b12d SL |
353 | /// the scope reachable dtorck types must outlive |
354 | parent_scope: region::CodeExtent | |
355 | } | |
356 | ||
357 | // `context` is used for reporting overflow errors | |
a7813a04 XL |
358 | fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>( |
359 | cx: &mut DropckContext<'a, 'b, 'gcx, 'tcx>, | |
c1a9b12d SL |
360 | context: TypeContext, |
361 | ty: Ty<'tcx>, | |
c30ab7b3 SL |
362 | depth: usize) |
363 | -> Result<(), Error<'tcx>> | |
85aaf69f | 364 | { |
a7813a04 | 365 | let tcx = cx.rcx.tcx; |
c34b1796 AL |
366 | // Issue #22443: Watch out for overflow. While we are careful to |
367 | // handle regular types properly, non-regular ones cause problems. | |
c1a9b12d SL |
368 | let recursion_limit = tcx.sess.recursion_limit.get(); |
369 | if depth / 4 >= recursion_limit { | |
370 | // This can get into rather deep recursion, especially in the | |
371 | // presence of things like Vec<T> -> Unique<T> -> PhantomData<T> -> T. | |
372 | // use a higher recursion limit to avoid errors. | |
373 | return Err(Error::Overflow(context, ty)) | |
c34b1796 AL |
374 | } |
375 | ||
9cc50fc6 SL |
376 | // canoncialize the regions in `ty` before inserting - infinitely many |
377 | // region variables can refer to the same region. | |
a7813a04 | 378 | let ty = cx.rcx.resolve_type_and_region_vars_if_possible(&ty); |
9cc50fc6 | 379 | |
c1a9b12d SL |
380 | if !cx.breadcrumbs.insert(ty) { |
381 | debug!("iterate_over_potentially_unsafe_regions_in_type \ | |
382 | {}ty: {} scope: {:?} - cached", | |
383 | (0..depth).map(|_| ' ').collect::<String>(), | |
384 | ty, cx.parent_scope); | |
385 | return Ok(()); // we already visited this type | |
386 | } | |
387 | debug!("iterate_over_potentially_unsafe_regions_in_type \ | |
388 | {}ty: {} scope: {:?}", | |
389 | (0..depth).map(|_| ' ').collect::<String>(), | |
390 | ty, cx.parent_scope); | |
391 | ||
392 | // If `typ` has a destructor, then we must ensure that all | |
393 | // borrowed data reachable via `typ` must outlive the parent | |
394 | // of `scope`. This is handled below. | |
395 | // | |
b039eaaf SL |
396 | // However, there is an important special case: for any Drop |
397 | // impl that is tagged as "blind" to their parameters, | |
398 | // we assume that data borrowed via such type parameters | |
399 | // remains unreachable via that Drop impl. | |
400 | // | |
401 | // For example, consider: | |
402 | // | |
403 | // ```rust | |
404 | // #[unsafe_destructor_blind_to_params] | |
405 | // impl<T> Drop for Vec<T> { ... } | |
406 | // ``` | |
c1a9b12d | 407 | // |
c1a9b12d SL |
408 | // which does have to be able to drop instances of `T`, but |
409 | // otherwise cannot read data from `T`. | |
410 | // | |
411 | // Of course, for the type expression passed in for any such | |
412 | // unbounded type parameter `T`, we must resume the recursive | |
413 | // analysis on `T` (since it would be ignored by | |
414 | // type_must_outlive). | |
c30ab7b3 SL |
415 | let dropck_kind = has_dtor_of_interest(tcx, ty); |
416 | debug!("iterate_over_potentially_unsafe_regions_in_type \ | |
417 | ty: {:?} dropck_kind: {:?}", ty, dropck_kind); | |
418 | match dropck_kind { | |
419 | DropckKind::NoBorrowedDataAccessedInMyDtor => { | |
420 | // The maximally blind attribute. | |
421 | } | |
422 | DropckKind::BorrowedDataMustStrictlyOutliveSelf => { | |
423 | cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span), | |
424 | ty, tcx.mk_region(ty::ReScope(cx.parent_scope))); | |
425 | return Ok(()); | |
426 | } | |
427 | DropckKind::RevisedSelf(revised_ty) => { | |
428 | cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span), | |
429 | revised_ty, tcx.mk_region(ty::ReScope(cx.parent_scope))); | |
430 | // Do not return early from this case; we want | |
431 | // to recursively process the internal structure of Self | |
432 | // (because even though the Drop for Self has been asserted | |
433 | // safe, the types instantiated for the generics of Self | |
434 | // may themselves carry dropck constraints.) | |
435 | } | |
c1a9b12d | 436 | } |
c34b1796 | 437 | |
c1a9b12d SL |
438 | debug!("iterate_over_potentially_unsafe_regions_in_type \ |
439 | {}ty: {} scope: {:?} - checking interior", | |
440 | (0..depth).map(|_| ' ').collect::<String>(), | |
441 | ty, cx.parent_scope); | |
442 | ||
443 | // We still need to ensure all referenced data is safe. | |
444 | match ty.sty { | |
445 | ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) | | |
5bcae85e | 446 | ty::TyFloat(_) | ty::TyStr | ty::TyNever => { |
c1a9b12d SL |
447 | // primitive - definitely safe |
448 | Ok(()) | |
449 | } | |
85aaf69f | 450 | |
c1a9b12d SL |
451 | ty::TyBox(ity) | ty::TyArray(ity, _) | ty::TySlice(ity) => { |
452 | // single-element containers, behave like their element | |
453 | iterate_over_potentially_unsafe_regions_in_type( | |
454 | cx, context, ity, depth+1) | |
455 | } | |
85aaf69f | 456 | |
9e0c209e | 457 | ty::TyAdt(def, substs) if def.is_phantom_data() => { |
c1a9b12d | 458 | // PhantomData<T> - behaves identically to T |
9e0c209e | 459 | let ity = substs.type_at(0); |
c1a9b12d SL |
460 | iterate_over_potentially_unsafe_regions_in_type( |
461 | cx, context, ity, depth+1) | |
462 | } | |
85aaf69f | 463 | |
9e0c209e | 464 | ty::TyAdt(def, substs) => { |
e9174d1e SL |
465 | let did = def.did; |
466 | for variant in &def.variants { | |
54a0048b | 467 | for field in variant.fields.iter() { |
e9174d1e | 468 | let fty = field.ty(tcx, substs); |
a7813a04 | 469 | let fty = cx.rcx.fcx.resolve_type_vars_with_obligations( |
c1a9b12d | 470 | cx.rcx.fcx.normalize_associated_types_in(cx.span, &fty)); |
54a0048b | 471 | iterate_over_potentially_unsafe_regions_in_type( |
c1a9b12d | 472 | cx, |
e9174d1e | 473 | TypeContext::ADT { |
c1a9b12d | 474 | def_id: did, |
e9174d1e SL |
475 | field: field.name, |
476 | variant: variant.name, | |
c1a9b12d SL |
477 | }, |
478 | fty, | |
54a0048b | 479 | depth+1)? |
85aaf69f | 480 | } |
c1a9b12d SL |
481 | } |
482 | Ok(()) | |
483 | } | |
85aaf69f | 484 | |
a7813a04 XL |
485 | ty::TyTuple(tys) | |
486 | ty::TyClosure(_, ty::ClosureSubsts { upvar_tys: tys, .. }) => { | |
c1a9b12d | 487 | for ty in tys { |
54a0048b | 488 | iterate_over_potentially_unsafe_regions_in_type(cx, context, ty, depth+1)? |
c1a9b12d SL |
489 | } |
490 | Ok(()) | |
85aaf69f | 491 | } |
c34b1796 | 492 | |
c1a9b12d SL |
493 | ty::TyRawPtr(..) | ty::TyRef(..) | ty::TyParam(..) => { |
494 | // these always come with a witness of liveness (references | |
495 | // explicitly, pointers implicitly, parameters by the | |
496 | // caller). | |
497 | Ok(()) | |
498 | } | |
bd371182 | 499 | |
54a0048b | 500 | ty::TyFnDef(..) | ty::TyFnPtr(_) => { |
c1a9b12d SL |
501 | // FIXME(#26656): this type is always destruction-safe, but |
502 | // it implicitly witnesses Self: Fn, which can be false. | |
503 | Ok(()) | |
504 | } | |
bd371182 | 505 | |
c1a9b12d SL |
506 | ty::TyInfer(..) | ty::TyError => { |
507 | tcx.sess.delay_span_bug(cx.span, "unresolved type in regionck"); | |
508 | Ok(()) | |
509 | } | |
bd371182 | 510 | |
c1a9b12d | 511 | // these are always dtorck |
5bcae85e | 512 | ty::TyTrait(..) | ty::TyProjection(_) | ty::TyAnon(..) => bug!(), |
c1a9b12d | 513 | } |
bd371182 AL |
514 | } |
515 | ||
c30ab7b3 SL |
516 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
517 | enum DropckKind<'tcx> { | |
518 | /// The "safe" kind; i.e. conservatively assume any borrow | |
519 | /// accessed by dtor, and therefore such data must strictly | |
520 | /// outlive self. | |
521 | /// | |
522 | /// Equivalent to RevisedTy with no change to the self type. | |
523 | BorrowedDataMustStrictlyOutliveSelf, | |
524 | ||
525 | /// The nearly completely-unsafe kind. | |
526 | /// | |
527 | /// Equivalent to RevisedSelf with *all* parameters remapped to () | |
528 | /// (maybe...?) | |
529 | NoBorrowedDataAccessedInMyDtor, | |
530 | ||
531 | /// Assume all borrowed data access by dtor occurs as if Self has the | |
532 | /// type carried by this variant. In practice this means that some | |
533 | /// of the type parameters are remapped to `()` (and some lifetime | |
534 | /// parameters remapped to `'static`), because the developer has asserted | |
535 | /// that the destructor will not access their contents. | |
536 | RevisedSelf(Ty<'tcx>), | |
537 | } | |
538 | ||
539 | /// Returns the classification of what kind of check should be applied | |
540 | /// to `ty`, which may include a revised type where some of the type | |
541 | /// parameters are re-mapped to `()` to reflect the destructor's | |
542 | /// "purity" with respect to their actual contents. | |
a7813a04 | 543 | fn has_dtor_of_interest<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, |
c30ab7b3 SL |
544 | ty: Ty<'tcx>) |
545 | -> DropckKind<'tcx> { | |
c1a9b12d | 546 | match ty.sty { |
c30ab7b3 SL |
547 | ty::TyAdt(adt_def, substs) => { |
548 | if !adt_def.is_dtorck(tcx) { | |
549 | return DropckKind::NoBorrowedDataAccessedInMyDtor; | |
550 | } | |
551 | ||
552 | // Find the `impl<..> Drop for _` to inspect any | |
553 | // attributes attached to the impl's generics. | |
554 | let dtor_method = adt_def.destructor() | |
555 | .expect("dtorck type without destructor impossible"); | |
556 | let method = tcx.impl_or_trait_item(dtor_method); | |
557 | let impl_id: DefId = method.container().id(); | |
558 | let revised_ty = revise_self_ty(tcx, adt_def, impl_id, substs); | |
559 | return DropckKind::RevisedSelf(revised_ty); | |
bd371182 | 560 | } |
5bcae85e | 561 | ty::TyTrait(..) | ty::TyProjection(..) | ty::TyAnon(..) => { |
c1a9b12d | 562 | debug!("ty: {:?} isn't known, and therefore is a dropck type", ty); |
c30ab7b3 | 563 | return DropckKind::BorrowedDataMustStrictlyOutliveSelf; |
c1a9b12d | 564 | }, |
c30ab7b3 SL |
565 | _ => { |
566 | return DropckKind::NoBorrowedDataAccessedInMyDtor; | |
567 | } | |
bd371182 | 568 | } |
bd371182 | 569 | } |
c30ab7b3 SL |
570 | |
571 | // Constructs new Ty just like the type defined by `adt_def` coupled | |
572 | // with `substs`, except each type and lifetime parameter marked as | |
573 | // `#[may_dangle]` in the Drop impl (identified by `impl_id`) is | |
574 | // respectively mapped to `()` or `'static`. | |
575 | // | |
576 | // For example: If the `adt_def` maps to: | |
577 | // | |
578 | // enum Foo<'a, X, Y> { ... } | |
579 | // | |
580 | // and the `impl_id` maps to: | |
581 | // | |
582 | // impl<#[may_dangle] 'a, X, #[may_dangle] Y> Drop for Foo<'a, X, Y> { ... } | |
583 | // | |
584 | // then revises input: `Foo<'r,i64,&'r i64>` to: `Foo<'static,i64,()>` | |
585 | fn revise_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, | |
586 | adt_def: ty::AdtDef<'tcx>, | |
587 | impl_id: DefId, | |
588 | substs: &Substs<'tcx>) | |
589 | -> Ty<'tcx> { | |
590 | // Get generics for `impl Drop` to query for `#[may_dangle]` attr. | |
591 | let impl_bindings = tcx.lookup_generics(impl_id); | |
592 | ||
593 | // Get Substs attached to Self on `impl Drop`; process in parallel | |
594 | // with `substs`, replacing dangling entries as appropriate. | |
595 | let self_substs = { | |
596 | let impl_self_ty: Ty<'tcx> = tcx.lookup_item_type(impl_id).ty; | |
597 | if let ty::TyAdt(self_adt_def, self_substs) = impl_self_ty.sty { | |
598 | assert_eq!(adt_def, self_adt_def); | |
599 | self_substs | |
600 | } else { | |
601 | bug!("Self in `impl Drop for _` must be an Adt."); | |
602 | } | |
603 | }; | |
604 | ||
605 | // Walk `substs` + `self_substs`, build new substs appropriate for | |
606 | // `adt_def`; each non-dangling param reuses entry from `substs`. | |
607 | // | |
608 | // Note: The manner we map from a right-hand side (i.e. Region or | |
609 | // Ty) for a given `def` to generic parameter associated with that | |
610 | // right-hand side is tightly coupled to `Drop` impl constraints. | |
611 | // | |
612 | // E.g. we know such a Ty must be `TyParam`, because a destructor | |
613 | // for `struct Foo<X>` is defined via `impl<Y> Drop for Foo<Y>`, | |
614 | // and never by (for example) `impl<Z> Drop for Foo<Vec<Z>>`. | |
615 | let substs = Substs::for_item( | |
616 | tcx, | |
617 | adt_def.did, | |
618 | |def, _| { | |
619 | let r_orig = substs.region_for_def(def); | |
620 | let impl_self_orig = self_substs.region_for_def(def); | |
621 | let r = if let ty::Region::ReEarlyBound(ref ebr) = *impl_self_orig { | |
622 | if impl_bindings.region_param(ebr).pure_wrt_drop { | |
623 | tcx.mk_region(ty::ReStatic) | |
624 | } else { | |
625 | r_orig | |
626 | } | |
627 | } else { | |
628 | bug!("substs for an impl must map regions to ReEarlyBound"); | |
629 | }; | |
630 | debug!("has_dtor_of_interest mapping def {:?} orig {:?} to {:?}", | |
631 | def, r_orig, r); | |
632 | r | |
633 | }, | |
634 | |def, _| { | |
635 | let t_orig = substs.type_for_def(def); | |
636 | let impl_self_orig = self_substs.type_for_def(def); | |
637 | let t = if let ty::TypeVariants::TyParam(ref pt) = impl_self_orig.sty { | |
638 | if impl_bindings.type_param(pt).pure_wrt_drop { | |
639 | tcx.mk_nil() | |
640 | } else { | |
641 | t_orig | |
642 | } | |
643 | } else { | |
644 | bug!("substs for an impl must map types to TyParam"); | |
645 | }; | |
646 | debug!("has_dtor_of_interest mapping def {:?} orig {:?} {:?} to {:?} {:?}", | |
647 | def, t_orig, t_orig.sty, t, t.sty); | |
648 | t | |
649 | }); | |
650 | ||
651 | return tcx.mk_adt(adt_def, &substs); | |
652 | } |