]> git.proxmox.com Git - rustc.git/blob - src/librustc_typeck/check/dropck.rs
Imported Upstream version 1.0.0-alpha.2
[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;
16 use middle::ty::{self, Ty};
17 use util::ppaux::{Repr};
18
19 use syntax::codemap::Span;
20
21 pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
22 typ: ty::Ty<'tcx>,
23 span: Span,
24 scope: region::CodeExtent) {
25 debug!("check_safety_of_destructor_if_necessary typ: {} scope: {:?}",
26 typ.repr(rcx.tcx()), scope);
27
28 // types that have been traversed so far by `traverse_type_if_unseen`
29 let mut breadcrumbs: Vec<Ty<'tcx>> = Vec::new();
30
31 iterate_over_potentially_unsafe_regions_in_type(
32 rcx,
33 &mut breadcrumbs,
34 typ,
35 span,
36 scope,
37 0);
38 }
39
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>,
44 span: Span,
45 scope: region::CodeExtent,
46 depth: uint)
47 {
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();
51
52 let destructor_for_type = rcx.tcx().destructor_for_type.borrow();
53
54 while let Some(typ) = walker.next() {
55 // Avoid recursing forever.
56 if breadcrumbs.contains(&typ) {
57 continue;
58 }
59 breadcrumbs.push(typ);
60
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()));
72 new_typ
73 } else {
74 typ
75 }
76 } else {
77 typ
78 };
79
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),
83 _ => None,
84 };
85
86 let opt_dtor =
87 opt_type_did.and_then(|did| destructor_for_type.get(&did));
88
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);
93
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.
97 //
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.
103 //
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`.
107 //
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).
112 //
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).
122
123 let has_dtor_of_interest;
124
125 if let Some(&dtor_method_did) = opt_dtor {
126 let impl_did = ty::impl_of_method(rcx.tcx(), dtor_method_did)
127 .unwrap_or_else(|| {
128 rcx.tcx().sess.span_bug(
129 span, "no Drop impl found for drop method")
130 });
131
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);
135
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.
143
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,
152 _ => true,
153 }
154 }
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.
162 true
163 }
164 };
165
166 if result {
167 debug!("typ: {} has interesting dtor due to generic preds, e.g. {}",
168 typ.repr(rcx.tcx()), pred.repr(rcx.tcx()));
169 }
170
171 result
172 });
173
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.
177 //
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);
184
185 has_dtor_of_interest =
186 has_region_param_of_interest ||
187 has_pred_of_interest;
188
189 if has_dtor_of_interest {
190 debug!("typ: {} has interesting dtor, due to \
191 region params: {} or pred: {}",
192 typ.repr(rcx.tcx()),
193 has_region_param_of_interest,
194 has_pred_of_interest);
195 } else {
196 debug!("typ: {} has dtor, but it is uninteresting",
197 typ.repr(rcx.tcx()));
198 }
199
200 } else {
201 debug!("typ: {} has no dtor, and thus is uninteresting",
202 typ.repr(rcx.tcx()));
203 has_dtor_of_interest = false;
204 }
205
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.)
213
214 let parent_region =
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: {:?}",
219 scope).as_slice()),
220 };
221
222 regionck::type_must_outlive(rcx, origin(), typ, parent_region);
223
224 } else {
225 // Okay, `typ` itself is itself not reachable by a
226 // destructor; but it may contain substructure that has a
227 // destructor.
228
229 match typ.sty {
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();
234
235 let fields =
236 ty::lookup_struct_fields(rcx.tcx(), struct_did);
237 for field in fields.iter() {
238 let field_type =
239 ty::lookup_field_type(rcx.tcx(),
240 struct_did,
241 field.id,
242 substs);
243 iterate_over_potentially_unsafe_regions_in_type(
244 rcx,
245 breadcrumbs,
246 field_type,
247 span,
248 scope,
249 depth+1)
250 }
251 }
252
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();
257
258 let all_variant_info =
259 ty::substd_enum_variants(rcx.tcx(),
260 enum_did,
261 substs);
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(
265 rcx,
266 breadcrumbs,
267 *argument_type,
268 span,
269 scope,
270 depth+1)
271 }
272 }
273 }
274
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();
280 }
281 _ => {}
282 };
283
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
290 // is done.
291 }
292 }
293 }