]>
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 | 11 | use check::regionck::RegionCtxt; |
85aaf69f | 12 | |
54a0048b | 13 | use hir::def_id::DefId; |
c30ab7b3 | 14 | use rustc::infer::{self, InferOk}; |
ff7c6d11 | 15 | use rustc::infer::outlives::env::OutlivesEnvironment; |
ea8adc8c | 16 | use rustc::middle::region; |
9e0c209e | 17 | use rustc::ty::subst::{Subst, Substs}; |
cc61c64b | 18 | use rustc::ty::{self, Ty, TyCtxt}; |
ff7c6d11 | 19 | use rustc::traits::{self, Reveal, ObligationCause}; |
8bb4bdeb | 20 | use util::common::ErrorReported; |
476ff2be | 21 | use util::nodemap::FxHashSet; |
c34b1796 | 22 | |
476ff2be | 23 | use syntax_pos::Span; |
c34b1796 | 24 | |
3b2f2976 | 25 | /// check_drop_impl confirms that the Drop implementation identified by |
c34b1796 AL |
26 | /// `drop_impl_did` is not any more specialized than the type it is |
27 | /// attached to (Issue #8142). | |
28 | /// | |
29 | /// This means: | |
30 | /// | |
31 | /// 1. The self type must be nominal (this is already checked during | |
32 | /// coherence), | |
33 | /// | |
34 | /// 2. The generic region/type parameters of the impl's self-type must | |
35 | /// all be parameters of the Drop impl itself (i.e. no | |
36 | /// specialization like `impl Drop for Foo<i32>`), and, | |
37 | /// | |
38 | /// 3. Any bounds on the generic parameters must be reflected in the | |
39 | /// struct/enum definition for the nominal type itself (i.e. | |
40 | /// cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`). | |
41 | /// | |
8bb4bdeb XL |
42 | pub fn check_drop_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
43 | drop_impl_did: DefId) | |
44 | -> Result<(), ErrorReported> { | |
7cac9316 XL |
45 | let dtor_self_type = tcx.type_of(drop_impl_did); |
46 | let dtor_predicates = tcx.predicates_of(drop_impl_did); | |
c34b1796 | 47 | match dtor_self_type.sty { |
9e0c209e | 48 | ty::TyAdt(adt_def, self_to_impl_substs) => { |
8bb4bdeb | 49 | ensure_drop_params_and_item_params_correspond(tcx, |
54a0048b | 50 | drop_impl_did, |
9e0c209e | 51 | dtor_self_type, |
54a0048b | 52 | adt_def.did)?; |
c34b1796 | 53 | |
8bb4bdeb | 54 | ensure_drop_predicates_are_implied_by_item_defn(tcx, |
c34b1796 AL |
55 | drop_impl_did, |
56 | &dtor_predicates, | |
e9174d1e | 57 | adt_def.did, |
c34b1796 AL |
58 | self_to_impl_substs) |
59 | } | |
60 | _ => { | |
61 | // Destructors only work on nominal types. This was | |
62 | // already checked by coherence, so we can panic here. | |
8bb4bdeb | 63 | let span = tcx.def_span(drop_impl_did); |
54a0048b SL |
64 | span_bug!(span, |
65 | "should have been rejected by coherence check: {}", | |
66 | dtor_self_type); | |
c34b1796 AL |
67 | } |
68 | } | |
69 | } | |
70 | ||
a7813a04 | 71 | fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>( |
8bb4bdeb | 72 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
e9174d1e | 73 | drop_impl_did: DefId, |
9e0c209e | 74 | drop_impl_ty: Ty<'tcx>, |
c30ab7b3 | 75 | self_type_did: DefId) |
8bb4bdeb | 76 | -> Result<(), ErrorReported> |
c34b1796 | 77 | { |
32a655c1 | 78 | let drop_impl_node_id = tcx.hir.as_local_node_id(drop_impl_did).unwrap(); |
e9174d1e SL |
79 | |
80 | // check that the impl type can be made to match the trait type. | |
81 | ||
041b39d2 | 82 | tcx.infer_ctxt().enter(|ref infcx| { |
7cac9316 | 83 | let impl_param_env = tcx.param_env(self_type_did); |
a7813a04 XL |
84 | let tcx = infcx.tcx; |
85 | let mut fulfillment_cx = traits::FulfillmentContext::new(); | |
e9174d1e | 86 | |
7cac9316 | 87 | let named_type = tcx.type_of(self_type_did); |
e9174d1e | 88 | |
476ff2be | 89 | let drop_impl_span = tcx.def_span(drop_impl_did); |
a7813a04 | 90 | let fresh_impl_substs = |
9e0c209e SL |
91 | infcx.fresh_substs_for_item(drop_impl_span, drop_impl_did); |
92 | let fresh_impl_self_ty = drop_impl_ty.subst(tcx, fresh_impl_substs); | |
e9174d1e | 93 | |
476ff2be | 94 | let cause = &ObligationCause::misc(drop_impl_span, drop_impl_node_id); |
7cac9316 | 95 | match infcx.at(cause, impl_param_env).eq(named_type, fresh_impl_self_ty) { |
c30ab7b3 | 96 | Ok(InferOk { obligations, .. }) => { |
cc61c64b | 97 | fulfillment_cx.register_predicate_obligations(infcx, obligations); |
c30ab7b3 SL |
98 | } |
99 | Err(_) => { | |
7cac9316 | 100 | let item_span = tcx.def_span(self_type_did); |
c30ab7b3 SL |
101 | struct_span_err!(tcx.sess, drop_impl_span, E0366, |
102 | "Implementations of Drop cannot be specialized") | |
103 | .span_note(item_span, | |
104 | "Use same sequence of generic type and region \ | |
105 | parameters that is on the struct/enum definition") | |
106 | .emit(); | |
8bb4bdeb | 107 | return Err(ErrorReported); |
c30ab7b3 | 108 | } |
a7813a04 XL |
109 | } |
110 | ||
111 | if let Err(ref errors) = fulfillment_cx.select_all_or_error(&infcx) { | |
112 | // this could be reached when we get lazy normalization | |
041b39d2 | 113 | infcx.report_fulfillment_errors(errors, None); |
8bb4bdeb | 114 | return Err(ErrorReported); |
a7813a04 XL |
115 | } |
116 | ||
ea8adc8c | 117 | let region_scope_tree = region::ScopeTree::default(); |
ff7c6d11 XL |
118 | |
119 | // NB. It seems a bit... suspicious to use an empty param-env | |
120 | // here. The correct thing, I imagine, would be | |
121 | // `OutlivesEnvironment::new(impl_param_env)`, which would | |
122 | // allow region solving to take any `a: 'b` relations on the | |
123 | // impl into account. But I could not create a test case where | |
124 | // it did the wrong thing, so I chose to preserve existing | |
125 | // behavior, since it ought to be simply more | |
126 | // conservative. -nmatsakis | |
127 | let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty(Reveal::UserFacing)); | |
128 | ||
129 | infcx.resolve_regions_and_report_errors(drop_impl_did, ®ion_scope_tree, &outlives_env); | |
a7813a04 XL |
130 | Ok(()) |
131 | }) | |
c34b1796 AL |
132 | } |
133 | ||
134 | /// Confirms that every predicate imposed by dtor_predicates is | |
135 | /// implied by assuming the predicates attached to self_type_did. | |
a7813a04 | 136 | fn ensure_drop_predicates_are_implied_by_item_defn<'a, 'tcx>( |
8bb4bdeb | 137 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
e9174d1e | 138 | drop_impl_did: DefId, |
c34b1796 | 139 | dtor_predicates: &ty::GenericPredicates<'tcx>, |
e9174d1e | 140 | self_type_did: DefId, |
c30ab7b3 | 141 | self_to_impl_substs: &Substs<'tcx>) |
8bb4bdeb | 142 | -> Result<(), ErrorReported> |
c30ab7b3 | 143 | { |
8bb4bdeb | 144 | let mut result = Ok(()); |
c34b1796 AL |
145 | |
146 | // Here is an example, analogous to that from | |
147 | // `compare_impl_method`. | |
148 | // | |
149 | // Consider a struct type: | |
150 | // | |
151 | // struct Type<'c, 'b:'c, 'a> { | |
152 | // x: &'a Contents // (contents are irrelevant; | |
153 | // y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.) | |
154 | // } | |
155 | // | |
156 | // and a Drop impl: | |
157 | // | |
158 | // impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> { | |
159 | // fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y) | |
160 | // } | |
161 | // | |
162 | // We start out with self_to_impl_substs, that maps the generic | |
163 | // parameters of Type to that of the Drop impl. | |
164 | // | |
165 | // self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x} | |
166 | // | |
167 | // Applying this to the predicates (i.e. assumptions) provided by the item | |
168 | // definition yields the instantiated assumptions: | |
169 | // | |
170 | // ['y : 'z] | |
171 | // | |
172 | // We then check all of the predicates of the Drop impl: | |
173 | // | |
174 | // ['y:'z, 'x:'y] | |
175 | // | |
176 | // and ensure each is in the list of instantiated | |
177 | // assumptions. Here, `'y:'z` is present, but `'x:'y` is | |
178 | // absent. So we report an error that the Drop impl injected a | |
179 | // predicate that is not present on the struct definition. | |
180 | ||
32a655c1 | 181 | let self_type_node_id = tcx.hir.as_local_node_id(self_type_did).unwrap(); |
c34b1796 | 182 | |
476ff2be | 183 | let drop_impl_span = tcx.def_span(drop_impl_did); |
c34b1796 AL |
184 | |
185 | // We can assume the predicates attached to struct/enum definition | |
186 | // hold. | |
7cac9316 | 187 | let generic_assumptions = tcx.predicates_of(self_type_did); |
c34b1796 AL |
188 | |
189 | let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs); | |
9e0c209e | 190 | let assumptions_in_impl_context = assumptions_in_impl_context.predicates; |
c34b1796 AL |
191 | |
192 | // An earlier version of this code attempted to do this checking | |
193 | // via the traits::fulfill machinery. However, it ran into trouble | |
194 | // since the fulfill machinery merely turns outlives-predicates | |
195 | // 'a:'b and T:'b into region inference constraints. It is simpler | |
196 | // just to look for all the predicates directly. | |
197 | ||
9e0c209e SL |
198 | assert_eq!(dtor_predicates.parent, None); |
199 | for predicate in &dtor_predicates.predicates { | |
c34b1796 AL |
200 | // (We do not need to worry about deep analysis of type |
201 | // expressions etc because the Drop impls are already forced | |
b039eaaf | 202 | // to take on a structure that is roughly an alpha-renaming of |
c34b1796 AL |
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) { | |
32a655c1 | 213 | let item_span = tcx.hir.span(self_type_node_id); |
9cc50fc6 SL |
214 | struct_span_err!(tcx.sess, drop_impl_span, E0367, |
215 | "The requirement `{}` is added only by the Drop impl.", predicate) | |
216 | .span_note(item_span, | |
217 | "The same requirement must be part of \ | |
218 | the struct/enum definition") | |
219 | .emit(); | |
8bb4bdeb | 220 | result = Err(ErrorReported); |
c34b1796 AL |
221 | } |
222 | } | |
85aaf69f | 223 | |
8bb4bdeb | 224 | result |
c34b1796 | 225 | } |
85aaf69f | 226 | |
c34b1796 AL |
227 | /// check_safety_of_destructor_if_necessary confirms that the type |
228 | /// expression `typ` conforms to the "Drop Check Rule" from the Sound | |
229 | /// Generic Drop (RFC 769). | |
230 | /// | |
231 | /// ---- | |
232 | /// | |
b039eaaf | 233 | /// The simplified (*) Drop Check Rule is the following: |
c34b1796 AL |
234 | /// |
235 | /// Let `v` be some value (either temporary or named) and 'a be some | |
236 | /// lifetime (scope). If the type of `v` owns data of type `D`, where | |
237 | /// | |
b039eaaf SL |
238 | /// * (1.) `D` has a lifetime- or type-parametric Drop implementation, |
239 | /// (where that `Drop` implementation does not opt-out of | |
240 | /// this check via the `unsafe_destructor_blind_to_params` | |
241 | /// attribute), and | |
242 | /// * (2.) the structure of `D` can reach a reference of type `&'a _`, | |
c34b1796 AL |
243 | /// |
244 | /// then 'a must strictly outlive the scope of v. | |
245 | /// | |
246 | /// ---- | |
247 | /// | |
248 | /// This function is meant to by applied to the type for every | |
249 | /// expression in the program. | |
b039eaaf SL |
250 | /// |
251 | /// ---- | |
252 | /// | |
253 | /// (*) The qualifier "simplified" is attached to the above | |
254 | /// definition of the Drop Check Rule, because it is a simplification | |
255 | /// of the original Drop Check rule, which attempted to prove that | |
256 | /// some `Drop` implementations could not possibly access data even if | |
257 | /// it was technically reachable, due to parametricity. | |
258 | /// | |
259 | /// However, (1.) parametricity on its own turned out to be a | |
260 | /// necessary but insufficient condition, and (2.) future changes to | |
261 | /// the language are expected to make it impossible to ensure that a | |
262 | /// `Drop` implementation is actually parametric with respect to any | |
263 | /// particular type parameter. (In particular, impl specialization is | |
264 | /// expected to break the needed parametricity property beyond | |
265 | /// repair.) | |
266 | /// | |
267 | /// Therefore we have scaled back Drop-Check to a more conservative | |
268 | /// rule that does not attempt to deduce whether a `Drop` | |
269 | /// implementation could not possible access data of a given lifetime; | |
270 | /// instead Drop-Check now simply assumes that if a destructor has | |
271 | /// access (direct or indirect) to a lifetime parameter, then that | |
272 | /// lifetime must be forced to outlive that destructor's dynamic | |
273 | /// extent. We then provide the `unsafe_destructor_blind_to_params` | |
274 | /// attribute as a way for destructor implementations to opt-out of | |
275 | /// this conservative assumption (and thus assume the obligation of | |
276 | /// ensuring that they do not access data nor invoke methods of | |
277 | /// values that have been previously dropped). | |
278 | /// | |
a7813a04 XL |
279 | pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>( |
280 | rcx: &mut RegionCtxt<'a, 'gcx, 'tcx>, | |
ea8adc8c | 281 | ty: Ty<'tcx>, |
a7813a04 | 282 | span: Span, |
ea8adc8c | 283 | scope: region::Scope) |
cc61c64b | 284 | -> Result<(), ErrorReported> |
a7813a04 | 285 | { |
62682a34 | 286 | debug!("check_safety_of_destructor_if_necessary typ: {:?} scope: {:?}", |
cc61c64b | 287 | ty, scope); |
c34b1796 | 288 | |
85aaf69f | 289 | |
ea8adc8c | 290 | let parent_scope = match rcx.region_scope_tree.opt_encl_scope(scope) { |
cc61c64b XL |
291 | Some(parent_scope) => parent_scope, |
292 | // If no enclosing scope, then it must be the root scope | |
293 | // which cannot be outlived. | |
294 | None => return Ok(()) | |
295 | }; | |
296 | let parent_scope = rcx.tcx.mk_region(ty::ReScope(parent_scope)); | |
297 | let origin = || infer::SubregionOrigin::SafeDestructor(span); | |
298 | ||
299 | let ty = rcx.fcx.resolve_type_vars_if_possible(&ty); | |
300 | let for_ty = ty; | |
301 | let mut types = vec![(ty, 0)]; | |
302 | let mut known = FxHashSet(); | |
303 | while let Some((ty, depth)) = types.pop() { | |
304 | let ty::DtorckConstraint { | |
305 | dtorck_types, outlives | |
306 | } = rcx.tcx.dtorck_constraint_for_ty(span, for_ty, depth, ty)?; | |
307 | ||
308 | for ty in dtorck_types { | |
309 | let ty = rcx.fcx.normalize_associated_types_in(span, &ty); | |
310 | let ty = rcx.fcx.resolve_type_vars_with_obligations(ty); | |
311 | let ty = rcx.fcx.resolve_type_and_region_vars_if_possible(&ty); | |
312 | match ty.sty { | |
313 | // All parameters live for the duration of the | |
314 | // function. | |
315 | ty::TyParam(..) => {} | |
316 | ||
317 | // A projection that we couldn't resolve - it | |
318 | // might have a destructor. | |
319 | ty::TyProjection(..) | ty::TyAnon(..) => { | |
320 | rcx.type_must_outlive(origin(), ty, parent_scope); | |
85aaf69f | 321 | } |
85aaf69f | 322 | |
cc61c64b XL |
323 | _ => { |
324 | if let None = known.replace(ty) { | |
325 | types.push((ty, depth+1)); | |
326 | } | |
327 | } | |
c1a9b12d | 328 | } |
c1a9b12d | 329 | } |
bd371182 | 330 | |
cc61c64b XL |
331 | for outlive in outlives { |
332 | if let Some(r) = outlive.as_region() { | |
333 | rcx.sub_regions(origin(), parent_scope, r); | |
334 | } else if let Some(ty) = outlive.as_type() { | |
335 | rcx.type_must_outlive(origin(), ty, parent_scope); | |
c30ab7b3 | 336 | } |
c30ab7b3 | 337 | } |
bd371182 | 338 | } |
c30ab7b3 | 339 | |
cc61c64b | 340 | Ok(()) |
c30ab7b3 | 341 | } |