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