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.
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.
11 use check
::regionck
::{self, Rcx}
;
16 use middle
::ty
::{self, Ty}
;
17 use util
::ppaux
::{Repr}
;
19 use syntax
::codemap
::Span
;
21 pub fn check_safety_of_destructor_if_necessary
<'a
, 'tcx
>(rcx
: &mut Rcx
<'a
, 'tcx
>,
24 scope
: region
::CodeExtent
) {
25 debug
!("check_safety_of_destructor_if_necessary typ: {} scope: {:?}",
26 typ
.repr(rcx
.tcx()), scope
);
28 // types that have been traversed so far by `traverse_type_if_unseen`
29 let mut breadcrumbs
: Vec
<Ty
<'tcx
>> = Vec
::new();
31 iterate_over_potentially_unsafe_regions_in_type(
40 fn iterate_over_potentially_unsafe_regions_in_type
<'a
, 'tcx
>(
41 rcx
: &mut Rcx
<'a
, 'tcx
>,
42 breadcrumbs
: &mut Vec
<Ty
<'tcx
>>,
43 ty_root
: ty
::Ty
<'tcx
>,
45 scope
: region
::CodeExtent
,
48 let origin
= |&:| infer
::SubregionOrigin
::SafeDestructor(span
);
49 let mut walker
= ty_root
.walk();
50 let opt_phantom_data_def_id
= rcx
.tcx().lang_items
.phantom_data();
52 let destructor_for_type
= rcx
.tcx().destructor_for_type
.borrow();
54 while let Some(typ
) = walker
.next() {
55 // Avoid recursing forever.
56 if breadcrumbs
.contains(&typ
) {
59 breadcrumbs
.push(typ
);
61 // If we encounter `PhantomData<T>`, then we should replace it
62 // with `T`, the type it represents as owned by the
63 // surrounding context, before doing further analysis.
64 let typ
= if let ty
::ty_struct(struct_did
, substs
) = typ
.sty
{
65 if opt_phantom_data_def_id
== Some(struct_did
) {
66 let item_type
= ty
::lookup_item_type(rcx
.tcx(), struct_did
);
67 let tp_def
= item_type
.generics
.types
68 .opt_get(subst
::TypeSpace
, 0).unwrap();
69 let new_typ
= substs
.type_for_def(tp_def
);
70 debug
!("replacing phantom {} with {}",
71 typ
.repr(rcx
.tcx()), new_typ
.repr(rcx
.tcx()));
80 let opt_type_did
= match typ
.sty
{
81 ty
::ty_struct(struct_did
, _
) => Some(struct_did
),
82 ty
::ty_enum(enum_did
, _
) => Some(enum_did
),
87 opt_type_did
.and_then(|did
| destructor_for_type
.get(&did
));
89 debug
!("iterate_over_potentially_unsafe_regions_in_type \
90 {}typ: {} scope: {:?} opt_dtor: {:?}",
91 (0..depth
).map(|_
| ' '
).collect
::<String
>(),
92 typ
.repr(rcx
.tcx()), scope
, opt_dtor
);
94 // If `typ` has a destructor, then we must ensure that all
95 // borrowed data reachable via `typ` must outlive the parent
96 // of `scope`. This is handled below.
98 // However, there is an important special case: by
99 // parametricity, any generic type parameters have *no* trait
100 // bounds in the Drop impl can not be used in any way (apart
101 // from being dropped), and thus we can treat data borrowed
102 // via such type parameters remains unreachable.
104 // For example, consider `impl<T> Drop for Vec<T> { ... }`,
105 // which does have to be able to drop instances of `T`, but
106 // otherwise cannot read data from `T`.
108 // Of course, for the type expression passed in for any such
109 // unbounded type parameter `T`, we must resume the recursive
110 // analysis on `T` (since it would be ignored by
111 // type_must_outlive).
113 // FIXME (pnkfelix): Long term, we could be smart and actually
114 // feed which generic parameters can be ignored *into* `fn
115 // type_must_outlive` (or some generalization thereof). But
116 // for the short term, it probably covers most cases of
117 // interest to just special case Drop impls where: (1.) there
118 // are no generic lifetime parameters and (2.) *all* generic
119 // type parameters are unbounded. If both conditions hold, we
120 // simply skip the `type_must_outlive` call entirely (but
121 // resume the recursive checking of the type-substructure).
123 let has_dtor_of_interest
;
125 if let Some(&dtor_method_did
) = opt_dtor
{
126 let impl_did
= ty
::impl_of_method(rcx
.tcx(), dtor_method_did
)
128 rcx
.tcx().sess
.span_bug(
129 span
, "no Drop impl found for drop method")
132 let dtor_typescheme
= ty
::lookup_item_type(rcx
.tcx(), impl_did
);
133 let dtor_generics
= dtor_typescheme
.generics
;
134 let dtor_predicates
= ty
::lookup_predicates(rcx
.tcx(), impl_did
);
136 let has_pred_of_interest
= dtor_predicates
.predicates
.iter().any(|pred
| {
137 // In `impl<T> Drop where ...`, we automatically
138 // assume some predicate will be meaningful and thus
139 // represents a type through which we could reach
140 // borrowed data. However, there can be implicit
141 // predicates (namely for Sized), and so we still need
142 // to walk through and filter out those cases.
144 let result
= match *pred
{
145 ty
::Predicate
::Trait(ty
::Binder(ref t_pred
)) => {
146 let def_id
= t_pred
.trait_ref
.def_id
;
147 match rcx
.tcx().lang_items
.to_builtin_kind(def_id
) {
148 Some(ty
::BoundSend
) |
149 Some(ty
::BoundSized
) |
150 Some(ty
::BoundCopy
) |
151 Some(ty
::BoundSync
) => false,
155 ty
::Predicate
::Equate(..) |
156 ty
::Predicate
::RegionOutlives(..) |
157 ty
::Predicate
::TypeOutlives(..) |
158 ty
::Predicate
::Projection(..) => {
159 // we assume all of these where-clauses may
160 // give the drop implementation the capabilty
161 // to access borrowed data.
167 debug
!("typ: {} has interesting dtor due to generic preds, e.g. {}",
168 typ
.repr(rcx
.tcx()), pred
.repr(rcx
.tcx()));
174 // In `impl<'a> Drop ...`, we automatically assume
175 // `'a` is meaningful and thus represents a bound
176 // through which we could reach borrowed data.
178 // FIXME (pnkfelix): In the future it would be good to
179 // extend the language to allow the user to express,
180 // in the impl signature, that a lifetime is not
181 // actually used (something like `where 'a: ?Live`).
182 let has_region_param_of_interest
=
183 dtor_generics
.has_region_params(subst
::TypeSpace
);
185 has_dtor_of_interest
=
186 has_region_param_of_interest
||
187 has_pred_of_interest
;
189 if has_dtor_of_interest
{
190 debug
!("typ: {} has interesting dtor, due to \
191 region params: {} or pred: {}",
193 has_region_param_of_interest
,
194 has_pred_of_interest
);
196 debug
!("typ: {} has dtor, but it is uninteresting",
197 typ
.repr(rcx
.tcx()));
201 debug
!("typ: {} has no dtor, and thus is uninteresting",
202 typ
.repr(rcx
.tcx()));
203 has_dtor_of_interest
= false;
206 if has_dtor_of_interest
{
207 // If `typ` has a destructor, then we must ensure that all
208 // borrowed data reachable via `typ` must outlive the
209 // parent of `scope`. (It does not suffice for it to
210 // outlive `scope` because that could imply that the
211 // borrowed data is torn down in between the end of
212 // `scope` and when the destructor itself actually runs.)
215 match rcx
.tcx().region_maps
.opt_encl_scope(scope
) {
216 Some(parent_scope
) => ty
::ReScope(parent_scope
),
217 None
=> rcx
.tcx().sess
.span_bug(
218 span
, format
!("no enclosing scope found for scope: {:?}",
222 regionck
::type_must_outlive(rcx
, origin(), typ
, parent_region
);
225 // Okay, `typ` itself is itself not reachable by a
226 // destructor; but it may contain substructure that has a
230 ty
::ty_struct(struct_did
, substs
) => {
231 // Don't recurse; we extract type's substructure,
232 // so do not process subparts of type expression.
233 walker
.skip_current_subtree();
236 ty
::lookup_struct_fields(rcx
.tcx(), struct_did
);
237 for field
in fields
.iter() {
239 ty
::lookup_field_type(rcx
.tcx(),
243 iterate_over_potentially_unsafe_regions_in_type(
253 ty
::ty_enum(enum_did
, substs
) => {
254 // Don't recurse; we extract type's substructure,
255 // so do not process subparts of type expression.
256 walker
.skip_current_subtree();
258 let all_variant_info
=
259 ty
::substd_enum_variants(rcx
.tcx(),
262 for variant_info
in all_variant_info
.iter() {
263 for argument_type
in variant_info
.args
.iter() {
264 iterate_over_potentially_unsafe_regions_in_type(
275 ty
::ty_rptr(..) | ty
::ty_ptr(_
) | ty
::ty_bare_fn(..) => {
276 // Don't recurse, since references, pointers,
277 // boxes, and bare functions don't own instances
278 // of the types appearing within them.
279 walker
.skip_current_subtree();
284 // You might be tempted to pop breadcrumbs here after
285 // processing type's internals above, but then you hit
286 // exponential time blowup e.g. on
287 // compile-fail/huge-struct.rs. Instead, we do not remove
288 // anything from the breadcrumbs vector during any particular
289 // traversal, and instead clear it after the whole traversal