@@ -12,6 +12,7 @@ use check::regionck::{self, Rcx};
1212
1313use middle:: infer;
1414use middle:: region;
15+ use middle:: subst;
1516use middle:: ty:: { self , Ty } ;
1617use util:: ppaux:: { Repr } ;
1718
@@ -46,31 +47,207 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
4647{
4748 let origin = |& : | infer:: SubregionOrigin :: SafeDestructor ( span) ;
4849 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+
4954 while let Some ( typ) = walker. next ( ) {
5055 // Avoid recursing forever.
5156 if breadcrumbs. contains ( & typ) {
5257 continue ;
5358 }
5459 breadcrumbs. push ( typ) ;
5560
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 ,
6084 } ;
6185
86+ let opt_dtor =
87+ opt_type_did. and_then ( |did| destructor_for_type. get ( & did) ) ;
88+
6289 debug ! ( "iterate_over_potentially_unsafe_regions_in_type \
63- {}typ: {} scope: {:?} has_dtor : {}",
90+ {}typ: {} scope: {:?} opt_dtor : {:? }",
6491 ( 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+ }
66243
67- if has_dtor {
244+ if has_dtor_of_interest {
68245 // If `typ` has a destructor, then we must ensure that all
69246 // borrowed data reachable via `typ` must outlive the
70247 // parent of `scope`. (It does not suffice for it to
71248 // outlive `scope` because that could imply that the
72249 // 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.)
74251
75252 let parent_region =
76253 match rcx. tcx ( ) . region_maps . opt_encl_scope ( scope) {
0 commit comments