@@ -12,6 +12,7 @@ use check::regionck::{self, Rcx};
12
12
13
13
use middle:: infer;
14
14
use middle:: region;
15
+ use middle:: subst;
15
16
use middle:: ty:: { self , Ty } ;
16
17
use util:: ppaux:: { Repr } ;
17
18
@@ -46,31 +47,207 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
46
47
{
47
48
let origin = |& : | infer:: SubregionOrigin :: SafeDestructor ( span) ;
48
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
+
49
54
while let Some ( typ) = walker. next ( ) {
50
55
// Avoid recursing forever.
51
56
if breadcrumbs. contains ( & typ) {
52
57
continue ;
53
58
}
54
59
breadcrumbs. push ( typ) ;
55
60
56
- let has_dtor = match typ. sty {
57
- ty:: ty_struct( struct_did, _) => ty:: has_dtor ( rcx. tcx ( ) , struct_did) ,
58
- ty:: ty_enum( enum_did, _) => ty:: has_dtor ( rcx. tcx ( ) , enum_did) ,
59
- _ => false ,
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 ,
60
84
} ;
61
85
86
+ let opt_dtor =
87
+ opt_type_did. and_then ( |did| destructor_for_type. get ( & did) ) ;
88
+
62
89
debug ! ( "iterate_over_potentially_unsafe_regions_in_type \
63
- {}typ: {} scope: {:?} has_dtor : {}",
90
+ {}typ: {} scope: {:?} opt_dtor : {:? }",
64
91
( 0 ..depth) . map( |_| ' ' ) . collect:: <String >( ) ,
65
- typ. repr( rcx. tcx( ) ) , scope, has_dtor) ;
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
+
135
+ let has_pred_of_interest = dtor_generics. predicates . iter ( ) . any ( |pred| {
136
+ // In `impl<T> Drop where ...`, we automatically
137
+ // assume some predicate will be meaningful and thus
138
+ // represents a type through which we could reach
139
+ // borrowed data. However, there can be implicit
140
+ // predicates (namely for Sized), and so we still need
141
+ // to walk through and filter out those cases.
142
+
143
+ let result = match * pred {
144
+ ty:: Predicate :: Trait ( ty:: Binder ( ref t_pred) ) => {
145
+ let def_id = t_pred. trait_ref . def_id ;
146
+ match rcx. tcx ( ) . lang_items . to_builtin_kind ( def_id) {
147
+ Some ( ty:: BoundSend ) |
148
+ Some ( ty:: BoundSized ) |
149
+ Some ( ty:: BoundCopy ) |
150
+ Some ( ty:: BoundSync ) => false ,
151
+ _ => true ,
152
+ }
153
+ }
154
+ ty:: Predicate :: Equate ( ..) |
155
+ ty:: Predicate :: RegionOutlives ( ..) |
156
+ ty:: Predicate :: TypeOutlives ( ..) |
157
+ ty:: Predicate :: Projection ( ..) => {
158
+ // we assume all of these where-clauses may
159
+ // give the drop implementation the capabilty
160
+ // to access borrowed data.
161
+ true
162
+ }
163
+ } ;
164
+
165
+ if result {
166
+ debug ! ( "typ: {} has interesting dtor due to generic preds, e.g. {}" ,
167
+ typ. repr( rcx. tcx( ) ) , pred. repr( rcx. tcx( ) ) ) ;
168
+ }
169
+
170
+ result
171
+ } ) ;
172
+
173
+ let has_type_param_of_interest = dtor_generics. types . iter ( ) . any ( |t| {
174
+ let & ty:: ParamBounds {
175
+ ref region_bounds, builtin_bounds, ref trait_bounds,
176
+ ref projection_bounds,
177
+ } = & t. bounds ;
178
+
179
+ // Belt-and-suspenders: The current set of builtin
180
+ // bounds {Send, Sized, Copy, Sync} do not introduce
181
+ // any new capability to access borrowed data hidden
182
+ // behind a type parameter.
183
+ //
184
+ // In case new builtin bounds get added that do not
185
+ // satisfy that property, ensure `builtin_bounds \
186
+ // {Send,Sized,Copy,Sync}` is empty.
187
+
188
+ let mut builtin_bounds = builtin_bounds;
189
+ builtin_bounds. remove ( & ty:: BoundSend ) ;
190
+ builtin_bounds. remove ( & ty:: BoundSized ) ;
191
+ builtin_bounds. remove ( & ty:: BoundCopy ) ;
192
+ builtin_bounds. remove ( & ty:: BoundSync ) ;
193
+
194
+ let has_bounds =
195
+ !region_bounds. is_empty ( ) ||
196
+ !builtin_bounds. is_empty ( ) ||
197
+ !trait_bounds. is_empty ( ) ||
198
+ !projection_bounds. is_empty ( ) ;
199
+
200
+ if has_bounds {
201
+ debug ! ( "typ: {} has interesting dtor due to \
202
+ bounds on param {}",
203
+ typ. repr( rcx. tcx( ) ) , t. name) ;
204
+ }
205
+
206
+ has_bounds
207
+
208
+ } ) ;
209
+
210
+ // In `impl<'a> Drop ...`, we automatically assume
211
+ // `'a` is meaningful and thus represents a bound
212
+ // through which we could reach borrowed data.
213
+ //
214
+ // FIXME (pnkfelix): In the future it would be good to
215
+ // extend the language to allow the user to express,
216
+ // in the impl signature, that a lifetime is not
217
+ // actually used (something like `where 'a: ?Live`).
218
+ let has_region_param_of_interest =
219
+ dtor_generics. has_region_params ( subst:: TypeSpace ) ;
220
+
221
+ has_dtor_of_interest =
222
+ has_region_param_of_interest ||
223
+ has_type_param_of_interest ||
224
+ has_pred_of_interest;
225
+
226
+ if has_dtor_of_interest {
227
+ debug ! ( "typ: {} has interesting dtor, due to \
228
+ region params: {} type params: {} or pred: {}",
229
+ typ. repr( rcx. tcx( ) ) ,
230
+ has_region_param_of_interest,
231
+ has_type_param_of_interest,
232
+ has_pred_of_interest) ;
233
+ } else {
234
+ debug ! ( "typ: {} has dtor, but it is uninteresting" ,
235
+ typ. repr( rcx. tcx( ) ) ) ;
236
+ }
237
+
238
+ } else {
239
+ debug ! ( "typ: {} has no dtor, and thus is uninteresting" ,
240
+ typ. repr( rcx. tcx( ) ) ) ;
241
+ has_dtor_of_interest = false ;
242
+ }
66
243
67
- if has_dtor {
244
+ if has_dtor_of_interest {
68
245
// If `typ` has a destructor, then we must ensure that all
69
246
// borrowed data reachable via `typ` must outlive the
70
247
// parent of `scope`. (It does not suffice for it to
71
248
// outlive `scope` because that could imply that the
72
249
// borrowed data is torn down in between the end of
73
- // `scope` and when the destructor itself actually runs.
250
+ // `scope` and when the destructor itself actually runs.)
74
251
75
252
let parent_region =
76
253
match rcx. tcx ( ) . region_maps . opt_encl_scope ( scope) {
0 commit comments