]> git.proxmox.com Git - rustc.git/blob - src/librustc_typeck/check/dropck.rs
Imported Upstream version 1.3.0+dfsg1
[rustc.git] / src / librustc_typeck / check / dropck.rs
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;
15 use middle::subst::{self, Subst};
16 use middle::ty::{self, Ty};
17 use util::nodemap::FnvHashSet;
18
19 use syntax::ast;
20 use syntax::codemap::{self, Span};
21
22 /// check_drop_impl confirms that the Drop implementation identfied by
23 /// `drop_impl_did` is not any more specialized than the type it is
24 /// attached to (Issue #8142).
25 ///
26 /// This means:
27 ///
28 /// 1. The self type must be nominal (this is already checked during
29 /// coherence),
30 ///
31 /// 2. The generic region/type parameters of the impl's self-type must
32 /// all be parameters of the Drop impl itself (i.e. no
33 /// specialization like `impl Drop for Foo<i32>`), and,
34 ///
35 /// 3. Any bounds on the generic parameters must be reflected in the
36 /// struct/enum definition for the nominal type itself (i.e.
37 /// cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`).
38 ///
39 pub fn check_drop_impl(tcx: &ty::ctxt, drop_impl_did: ast::DefId) -> Result<(), ()> {
40 let ty::TypeScheme { generics: ref dtor_generics,
41 ty: dtor_self_type } = tcx.lookup_item_type(drop_impl_did);
42 let dtor_predicates = tcx.lookup_predicates(drop_impl_did);
43 match dtor_self_type.sty {
44 ty::TyEnum(self_type_did, self_to_impl_substs) |
45 ty::TyStruct(self_type_did, self_to_impl_substs) => {
46 try!(ensure_drop_params_and_item_params_correspond(tcx,
47 drop_impl_did,
48 dtor_generics,
49 &dtor_self_type,
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: {}",
64 dtor_self_type));
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 tcx.lookup_item_type(self_type_did);
95
96 let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false);
97
98 infcx.commit_if_ok(|snapshot| {
99 let (named_type_to_skolem, skol_map) =
100 infcx.construct_skolemized_subst(named_type_generics, snapshot);
101 let named_type_skolem = named_type.subst(tcx, &named_type_to_skolem);
102
103 let drop_impl_span = tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP);
104 let drop_to_unifier =
105 infcx.fresh_substs_for_generics(drop_impl_span, drop_impl_generics);
106 let drop_unifier = drop_impl_ty.subst(tcx, &drop_to_unifier);
107
108 if let Ok(()) = infer::mk_eqty(&infcx, true, infer::TypeOrigin::Misc(drop_impl_span),
109 named_type_skolem, drop_unifier) {
110 // Even if we did manage to equate the types, the process
111 // may have just gathered unsolvable region constraints
112 // like `R == 'static` (represented as a pair of subregion
113 // constraints) for some skolemization constant R.
114 //
115 // However, the leak_check method allows us to confirm
116 // that no skolemized regions escaped (i.e. were related
117 // to other regions in the constraint graph).
118 if let Ok(()) = infcx.leak_check(&skol_map, snapshot) {
119 return Ok(())
120 }
121 }
122
123 span_err!(tcx.sess, drop_impl_span, E0366,
124 "Implementations of Drop cannot be specialized");
125 let item_span = tcx.map.span(self_type_did.node);
126 tcx.sess.span_note(item_span,
127 "Use same sequence of generic type and region \
128 parameters that is on the struct/enum definition");
129 return Err(());
130 })
131 }
132
133 /// Confirms that every predicate imposed by dtor_predicates is
134 /// implied by assuming the predicates attached to self_type_did.
135 fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
136 tcx: &ty::ctxt<'tcx>,
137 drop_impl_did: ast::DefId,
138 dtor_predicates: &ty::GenericPredicates<'tcx>,
139 self_type_did: ast::DefId,
140 self_to_impl_substs: &subst::Substs<'tcx>) -> Result<(), ()> {
141
142 // Here is an example, analogous to that from
143 // `compare_impl_method`.
144 //
145 // Consider a struct type:
146 //
147 // struct Type<'c, 'b:'c, 'a> {
148 // x: &'a Contents // (contents are irrelevant;
149 // y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.)
150 // }
151 //
152 // and a Drop impl:
153 //
154 // impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> {
155 // fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y)
156 // }
157 //
158 // We start out with self_to_impl_substs, that maps the generic
159 // parameters of Type to that of the Drop impl.
160 //
161 // self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x}
162 //
163 // Applying this to the predicates (i.e. assumptions) provided by the item
164 // definition yields the instantiated assumptions:
165 //
166 // ['y : 'z]
167 //
168 // We then check all of the predicates of the Drop impl:
169 //
170 // ['y:'z, 'x:'y]
171 //
172 // and ensure each is in the list of instantiated
173 // assumptions. Here, `'y:'z` is present, but `'x:'y` is
174 // absent. So we report an error that the Drop impl injected a
175 // predicate that is not present on the struct definition.
176
177 assert_eq!(self_type_did.krate, ast::LOCAL_CRATE);
178
179 let drop_impl_span = tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP);
180
181 // We can assume the predicates attached to struct/enum definition
182 // hold.
183 let generic_assumptions = tcx.lookup_predicates(self_type_did);
184
185 let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs);
186 assert!(assumptions_in_impl_context.predicates.is_empty_in(subst::SelfSpace));
187 assert!(assumptions_in_impl_context.predicates.is_empty_in(subst::FnSpace));
188 let assumptions_in_impl_context =
189 assumptions_in_impl_context.predicates.get_slice(subst::TypeSpace);
190
191 // An earlier version of this code attempted to do this checking
192 // via the traits::fulfill machinery. However, it ran into trouble
193 // since the fulfill machinery merely turns outlives-predicates
194 // 'a:'b and T:'b into region inference constraints. It is simpler
195 // just to look for all the predicates directly.
196
197 assert!(dtor_predicates.predicates.is_empty_in(subst::SelfSpace));
198 assert!(dtor_predicates.predicates.is_empty_in(subst::FnSpace));
199 let predicates = dtor_predicates.predicates.get_slice(subst::TypeSpace);
200 for predicate in predicates {
201 // (We do not need to worry about deep analysis of type
202 // expressions etc because the Drop impls are already forced
203 // to take on a structure that is roughly a alpha-renaming of
204 // the generic parameters of the item definition.)
205
206 // This path now just checks *all* predicates via the direct
207 // lookup, rather than using fulfill machinery.
208 //
209 // However, it may be more efficient in the future to batch
210 // the analysis together via the fulfill , rather than the
211 // repeated `contains` calls.
212
213 if !assumptions_in_impl_context.contains(&predicate) {
214 let item_span = tcx.map.span(self_type_did.node);
215 span_err!(tcx.sess, drop_impl_span, E0367,
216 "The requirement `{}` is added only by the Drop impl.", predicate);
217 tcx.sess.span_note(item_span,
218 "The same requirement must be part of \
219 the struct/enum definition");
220 }
221 }
222
223 if tcx.sess.has_errors() {
224 return Err(());
225 }
226 Ok(())
227 }
228
229 /// check_safety_of_destructor_if_necessary confirms that the type
230 /// expression `typ` conforms to the "Drop Check Rule" from the Sound
231 /// Generic Drop (RFC 769).
232 ///
233 /// ----
234 ///
235 /// The Drop Check Rule is the following:
236 ///
237 /// Let `v` be some value (either temporary or named) and 'a be some
238 /// lifetime (scope). If the type of `v` owns data of type `D`, where
239 ///
240 /// * (1.) `D` has a lifetime- or type-parametric Drop implementation, and
241 /// * (2.) the structure of `D` can reach a reference of type `&'a _`, and
242 /// * (3.) either:
243 /// * (A.) the Drop impl for `D` instantiates `D` at 'a directly,
244 /// i.e. `D<'a>`, or,
245 /// * (B.) the Drop impl for `D` has some type parameter with a
246 /// trait bound `T` where `T` is a trait that has at least
247 /// one method,
248 ///
249 /// then 'a must strictly outlive the scope of v.
250 ///
251 /// ----
252 ///
253 /// This function is meant to by applied to the type for every
254 /// expression in the program.
255 pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
256 typ: ty::Ty<'tcx>,
257 span: Span,
258 scope: region::CodeExtent) {
259 debug!("check_safety_of_destructor_if_necessary typ: {:?} scope: {:?}",
260 typ, scope);
261
262 let parent_scope = rcx.tcx().region_maps.opt_encl_scope(scope).unwrap_or_else(|| {
263 rcx.tcx().sess.span_bug(
264 span, &format!("no enclosing scope found for scope: {:?}", scope))
265 });
266
267 let result = iterate_over_potentially_unsafe_regions_in_type(
268 &mut DropckContext {
269 rcx: rcx,
270 span: span,
271 parent_scope: parent_scope,
272 breadcrumbs: FnvHashSet()
273 },
274 TypeContext::Root,
275 typ,
276 0);
277 match result {
278 Ok(()) => {}
279 Err(Error::Overflow(ref ctxt, ref detected_on_typ)) => {
280 let tcx = rcx.tcx();
281 span_err!(tcx.sess, span, E0320,
282 "overflow while adding drop-check rules for {}", typ);
283 match *ctxt {
284 TypeContext::Root => {
285 // no need for an additional note if the overflow
286 // was somehow on the root.
287 }
288 TypeContext::EnumVariant { def_id, variant, arg_index } => {
289 // FIXME (pnkfelix): eventually lookup arg_name
290 // for the given index on struct variants.
291 span_note!(
292 rcx.tcx().sess,
293 span,
294 "overflowed on enum {} variant {} argument {} type: {}",
295 tcx.item_path_str(def_id),
296 variant,
297 arg_index,
298 detected_on_typ);
299 }
300 TypeContext::Struct { def_id, field } => {
301 span_note!(
302 rcx.tcx().sess,
303 span,
304 "overflowed on struct {} field {} type: {}",
305 tcx.item_path_str(def_id),
306 field,
307 detected_on_typ);
308 }
309 }
310 }
311 }
312 }
313
314 enum Error<'tcx> {
315 Overflow(TypeContext, ty::Ty<'tcx>),
316 }
317
318 #[derive(Copy, Clone)]
319 enum TypeContext {
320 Root,
321 EnumVariant {
322 def_id: ast::DefId,
323 variant: ast::Name,
324 arg_index: usize,
325 },
326 Struct {
327 def_id: ast::DefId,
328 field: ast::Name,
329 }
330 }
331
332 struct DropckContext<'a, 'b: 'a, 'tcx: 'b> {
333 rcx: &'a mut Rcx<'b, 'tcx>,
334 /// types that have already been traversed
335 breadcrumbs: FnvHashSet<Ty<'tcx>>,
336 /// span for error reporting
337 span: Span,
338 /// the scope reachable dtorck types must outlive
339 parent_scope: region::CodeExtent
340 }
341
342 // `context` is used for reporting overflow errors
343 fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
344 cx: &mut DropckContext<'a, 'b, 'tcx>,
345 context: TypeContext,
346 ty: Ty<'tcx>,
347 depth: usize) -> Result<(), Error<'tcx>>
348 {
349 let tcx = cx.rcx.tcx();
350 // Issue #22443: Watch out for overflow. While we are careful to
351 // handle regular types properly, non-regular ones cause problems.
352 let recursion_limit = tcx.sess.recursion_limit.get();
353 if depth / 4 >= recursion_limit {
354 // This can get into rather deep recursion, especially in the
355 // presence of things like Vec<T> -> Unique<T> -> PhantomData<T> -> T.
356 // use a higher recursion limit to avoid errors.
357 return Err(Error::Overflow(context, ty))
358 }
359
360 let opt_phantom_data_def_id = tcx.lang_items.phantom_data();
361
362 if !cx.breadcrumbs.insert(ty) {
363 debug!("iterate_over_potentially_unsafe_regions_in_type \
364 {}ty: {} scope: {:?} - cached",
365 (0..depth).map(|_| ' ').collect::<String>(),
366 ty, cx.parent_scope);
367 return Ok(()); // we already visited this type
368 }
369 debug!("iterate_over_potentially_unsafe_regions_in_type \
370 {}ty: {} scope: {:?}",
371 (0..depth).map(|_| ' ').collect::<String>(),
372 ty, cx.parent_scope);
373
374 // If `typ` has a destructor, then we must ensure that all
375 // borrowed data reachable via `typ` must outlive the parent
376 // of `scope`. This is handled below.
377 //
378 // However, there is an important special case: by
379 // parametricity, any generic type parameters have *no* trait
380 // bounds in the Drop impl can not be used in any way (apart
381 // from being dropped), and thus we can treat data borrowed
382 // via such type parameters remains unreachable.
383 //
384 // For example, consider `impl<T> Drop for Vec<T> { ... }`,
385 // which does have to be able to drop instances of `T`, but
386 // otherwise cannot read data from `T`.
387 //
388 // Of course, for the type expression passed in for any such
389 // unbounded type parameter `T`, we must resume the recursive
390 // analysis on `T` (since it would be ignored by
391 // type_must_outlive).
392 //
393 // FIXME (pnkfelix): Long term, we could be smart and actually
394 // feed which generic parameters can be ignored *into* `fn
395 // type_must_outlive` (or some generalization thereof). But
396 // for the short term, it probably covers most cases of
397 // interest to just special case Drop impls where: (1.) there
398 // are no generic lifetime parameters and (2.) *all* generic
399 // type parameters are unbounded. If both conditions hold, we
400 // simply skip the `type_must_outlive` call entirely (but
401 // resume the recursive checking of the type-substructure).
402 if has_dtor_of_interest(tcx, ty, cx.span) {
403 debug!("iterate_over_potentially_unsafe_regions_in_type \
404 {}ty: {} - is a dtorck type!",
405 (0..depth).map(|_| ' ').collect::<String>(),
406 ty);
407
408 regionck::type_must_outlive(cx.rcx,
409 infer::SubregionOrigin::SafeDestructor(cx.span),
410 ty,
411 ty::ReScope(cx.parent_scope));
412
413 return Ok(());
414 }
415
416 debug!("iterate_over_potentially_unsafe_regions_in_type \
417 {}ty: {} scope: {:?} - checking interior",
418 (0..depth).map(|_| ' ').collect::<String>(),
419 ty, cx.parent_scope);
420
421 // We still need to ensure all referenced data is safe.
422 match ty.sty {
423 ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) |
424 ty::TyFloat(_) | ty::TyStr => {
425 // primitive - definitely safe
426 Ok(())
427 }
428
429 ty::TyBox(ity) | ty::TyArray(ity, _) | ty::TySlice(ity) => {
430 // single-element containers, behave like their element
431 iterate_over_potentially_unsafe_regions_in_type(
432 cx, context, ity, depth+1)
433 }
434
435 ty::TyStruct(did, substs) if Some(did) == opt_phantom_data_def_id => {
436 // PhantomData<T> - behaves identically to T
437 let ity = *substs.types.get(subst::TypeSpace, 0);
438 iterate_over_potentially_unsafe_regions_in_type(
439 cx, context, ity, depth+1)
440 }
441
442 ty::TyStruct(did, substs) => {
443 let fields = tcx.lookup_struct_fields(did);
444 for field in &fields {
445 let fty = tcx.lookup_field_type(did, field.id, substs);
446 let fty = cx.rcx.fcx.resolve_type_vars_if_possible(
447 cx.rcx.fcx.normalize_associated_types_in(cx.span, &fty));
448 try!(iterate_over_potentially_unsafe_regions_in_type(
449 cx,
450 TypeContext::Struct {
451 def_id: did,
452 field: field.name,
453 },
454 fty,
455 depth+1))
456 }
457 Ok(())
458 }
459
460 ty::TyEnum(did, substs) => {
461 let all_variant_info = tcx.substd_enum_variants(did, substs);
462 for variant_info in &all_variant_info {
463 for (i, fty) in variant_info.args.iter().enumerate() {
464 let fty = cx.rcx.fcx.resolve_type_vars_if_possible(
465 cx.rcx.fcx.normalize_associated_types_in(cx.span, &fty));
466 try!(iterate_over_potentially_unsafe_regions_in_type(
467 cx,
468 TypeContext::EnumVariant {
469 def_id: did,
470 variant: variant_info.name,
471 arg_index: i,
472 },
473 fty,
474 depth+1));
475 }
476 }
477 Ok(())
478 }
479
480 ty::TyTuple(ref tys) |
481 ty::TyClosure(_, box ty::ClosureSubsts { upvar_tys: ref tys, .. }) => {
482 for ty in tys {
483 try!(iterate_over_potentially_unsafe_regions_in_type(
484 cx, context, ty, depth+1))
485 }
486 Ok(())
487 }
488
489 ty::TyRawPtr(..) | ty::TyRef(..) | ty::TyParam(..) => {
490 // these always come with a witness of liveness (references
491 // explicitly, pointers implicitly, parameters by the
492 // caller).
493 Ok(())
494 }
495
496 ty::TyBareFn(..) => {
497 // FIXME(#26656): this type is always destruction-safe, but
498 // it implicitly witnesses Self: Fn, which can be false.
499 Ok(())
500 }
501
502 ty::TyInfer(..) | ty::TyError => {
503 tcx.sess.delay_span_bug(cx.span, "unresolved type in regionck");
504 Ok(())
505 }
506
507 // these are always dtorck
508 ty::TyTrait(..) | ty::TyProjection(_) => unreachable!(),
509 }
510 }
511
512 fn has_dtor_of_interest<'tcx>(tcx: &ty::ctxt<'tcx>,
513 ty: ty::Ty<'tcx>,
514 span: Span) -> bool {
515 match ty.sty {
516 ty::TyEnum(def_id, _) | ty::TyStruct(def_id, _) => {
517 let dtor_method_did = match tcx.destructor_for_type.borrow().get(&def_id) {
518 Some(def_id) => *def_id,
519 None => {
520 debug!("ty: {:?} has no dtor, and thus isn't a dropck type", ty);
521 return false;
522 }
523 };
524 let impl_did = tcx.impl_of_method(dtor_method_did)
525 .unwrap_or_else(|| {
526 tcx.sess.span_bug(
527 span, "no Drop impl found for drop method")
528 });
529
530 let dtor_typescheme = tcx.lookup_item_type(impl_did);
531 let dtor_generics = dtor_typescheme.generics;
532
533 let mut has_pred_of_interest = false;
534
535 let mut seen_items = Vec::new();
536 let mut items_to_inspect = vec![impl_did];
537 'items: while let Some(item_def_id) = items_to_inspect.pop() {
538 if seen_items.contains(&item_def_id) {
539 continue;
540 }
541
542 for pred in tcx.lookup_predicates(item_def_id).predicates {
543 let result = match pred {
544 ty::Predicate::Equate(..) |
545 ty::Predicate::RegionOutlives(..) |
546 ty::Predicate::TypeOutlives(..) |
547 ty::Predicate::Projection(..) => {
548 // For now, assume all these where-clauses
549 // may give drop implementation capabilty
550 // to access borrowed data.
551 true
552 }
553
554 ty::Predicate::Trait(ty::Binder(ref t_pred)) => {
555 let def_id = t_pred.trait_ref.def_id;
556 if tcx.trait_items(def_id).len() != 0 {
557 // If trait has items, assume it adds
558 // capability to access borrowed data.
559 true
560 } else {
561 // Trait without items is itself
562 // uninteresting from POV of dropck.
563 //
564 // However, may have parent w/ items;
565 // so schedule checking of predicates,
566 items_to_inspect.push(def_id);
567 // and say "no capability found" for now.
568 false
569 }
570 }
571 };
572
573 if result {
574 has_pred_of_interest = true;
575 debug!("ty: {:?} has interesting dtor due to generic preds, e.g. {:?}",
576 ty, pred);
577 break 'items;
578 }
579 }
580
581 seen_items.push(item_def_id);
582 }
583
584 // In `impl<'a> Drop ...`, we automatically assume
585 // `'a` is meaningful and thus represents a bound
586 // through which we could reach borrowed data.
587 //
588 // FIXME (pnkfelix): In the future it would be good to
589 // extend the language to allow the user to express,
590 // in the impl signature, that a lifetime is not
591 // actually used (something like `where 'a: ?Live`).
592 let has_region_param_of_interest =
593 dtor_generics.has_region_params(subst::TypeSpace);
594
595 let has_dtor_of_interest =
596 has_region_param_of_interest ||
597 has_pred_of_interest;
598
599 if has_dtor_of_interest {
600 debug!("ty: {:?} has interesting dtor, due to \
601 region params: {} or pred: {}",
602 ty,
603 has_region_param_of_interest,
604 has_pred_of_interest);
605 } else {
606 debug!("ty: {:?} has dtor, but it is uninteresting", ty);
607 }
608 has_dtor_of_interest
609 }
610 ty::TyTrait(..) | ty::TyProjection(..) => {
611 debug!("ty: {:?} isn't known, and therefore is a dropck type", ty);
612 true
613 },
614 _ => false
615 }
616 }