]>
Commit | Line | Data |
---|---|---|
74b04a01 XL |
1 | //! Check whether a type has (potentially) non-trivial drop glue. |
2 | ||
74b04a01 XL |
3 | use rustc_data_structures::fx::FxHashSet; |
4 | use rustc_hir::def_id::DefId; | |
ba9703b0 | 5 | use rustc_middle::ty::subst::Subst; |
94222f64 | 6 | use rustc_middle::ty::subst::SubstsRef; |
ba9703b0 XL |
7 | use rustc_middle::ty::util::{needs_drop_components, AlwaysRequiresDrop}; |
8 | use rustc_middle::ty::{self, Ty, TyCtxt}; | |
f9f354fc | 9 | use rustc_session::Limit; |
17df50a5 | 10 | use rustc_span::{sym, DUMMY_SP}; |
74b04a01 XL |
11 | |
12 | type NeedsDropResult<T> = Result<T, AlwaysRequiresDrop>; | |
13 | ||
14 | fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { | |
74b04a01 XL |
15 | // If we don't know a type doesn't need drop, for example if it's a type |
16 | // parameter without a `Copy` bound, then we conservatively return that it | |
17 | // needs drop. | |
c295e0f8 | 18 | let adt_has_dtor = |
ee023bcb | 19 | |adt_def: ty::AdtDef<'tcx>| adt_def.destructor(tcx).map(|_| DtorType::Significant); |
3c0e092e XL |
20 | let res = |
21 | drop_tys_helper(tcx, query.value, query.param_env, adt_has_dtor, false).next().is_some(); | |
c295e0f8 | 22 | |
74b04a01 XL |
23 | debug!("needs_drop_raw({:?}) = {:?}", query, res); |
24 | res | |
25 | } | |
26 | ||
17df50a5 XL |
27 | fn has_significant_drop_raw<'tcx>( |
28 | tcx: TyCtxt<'tcx>, | |
29 | query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, | |
30 | ) -> bool { | |
3c0e092e XL |
31 | let res = drop_tys_helper( |
32 | tcx, | |
33 | query.value, | |
34 | query.param_env, | |
35 | adt_consider_insignificant_dtor(tcx), | |
36 | true, | |
37 | ) | |
38 | .next() | |
39 | .is_some(); | |
17df50a5 XL |
40 | debug!("has_significant_drop_raw({:?}) = {:?}", query, res); |
41 | res | |
42 | } | |
43 | ||
74b04a01 XL |
44 | struct NeedsDropTypes<'tcx, F> { |
45 | tcx: TyCtxt<'tcx>, | |
46 | param_env: ty::ParamEnv<'tcx>, | |
47 | query_ty: Ty<'tcx>, | |
48 | seen_tys: FxHashSet<Ty<'tcx>>, | |
49 | /// A stack of types left to process, and the recursion depth when we | |
50 | /// pushed that type. Each round, we pop something from the stack and check | |
51 | /// if it needs drop. If the result depends on whether some other types | |
52 | /// need drop we push them onto the stack. | |
53 | unchecked_tys: Vec<(Ty<'tcx>, usize)>, | |
f9f354fc | 54 | recursion_limit: Limit, |
74b04a01 XL |
55 | adt_components: F, |
56 | } | |
57 | ||
58 | impl<'tcx, F> NeedsDropTypes<'tcx, F> { | |
59 | fn new( | |
60 | tcx: TyCtxt<'tcx>, | |
61 | param_env: ty::ParamEnv<'tcx>, | |
62 | ty: Ty<'tcx>, | |
63 | adt_components: F, | |
64 | ) -> Self { | |
65 | let mut seen_tys = FxHashSet::default(); | |
66 | seen_tys.insert(ty); | |
74b04a01 XL |
67 | Self { |
68 | tcx, | |
69 | param_env, | |
70 | seen_tys, | |
71 | query_ty: ty, | |
72 | unchecked_tys: vec![(ty, 0)], | |
136023e0 | 73 | recursion_limit: tcx.recursion_limit(), |
74b04a01 XL |
74 | adt_components, |
75 | } | |
76 | } | |
77 | } | |
78 | ||
79 | impl<'tcx, F, I> Iterator for NeedsDropTypes<'tcx, F> | |
80 | where | |
ee023bcb | 81 | F: Fn(ty::AdtDef<'tcx>, SubstsRef<'tcx>) -> NeedsDropResult<I>, |
74b04a01 XL |
82 | I: Iterator<Item = Ty<'tcx>>, |
83 | { | |
84 | type Item = NeedsDropResult<Ty<'tcx>>; | |
85 | ||
86 | fn next(&mut self) -> Option<NeedsDropResult<Ty<'tcx>>> { | |
87 | let tcx = self.tcx; | |
88 | ||
89 | while let Some((ty, level)) = self.unchecked_tys.pop() { | |
f9f354fc | 90 | if !self.recursion_limit.value_within_limit(level) { |
74b04a01 XL |
91 | // Not having a `Span` isn't great. But there's hopefully some other |
92 | // recursion limit error as well. | |
93 | tcx.sess.span_err( | |
94 | DUMMY_SP, | |
95 | &format!("overflow while checking whether `{}` requires drop", self.query_ty), | |
96 | ); | |
97 | return Some(Err(AlwaysRequiresDrop)); | |
98 | } | |
99 | ||
100 | let components = match needs_drop_components(ty, &tcx.data_layout) { | |
101 | Err(e) => return Some(Err(e)), | |
102 | Ok(components) => components, | |
103 | }; | |
104 | debug!("needs_drop_components({:?}) = {:?}", ty, components); | |
105 | ||
106 | let queue_type = move |this: &mut Self, component: Ty<'tcx>| { | |
107 | if this.seen_tys.insert(component) { | |
108 | this.unchecked_tys.push((component, level + 1)); | |
109 | } | |
110 | }; | |
111 | ||
112 | for component in components { | |
1b1a35ee | 113 | match *component.kind() { |
f035d41b | 114 | _ if component.is_copy_modulo_regions(tcx.at(DUMMY_SP), self.param_env) => (), |
74b04a01 | 115 | |
ba9703b0 | 116 | ty::Closure(_, substs) => { |
fc512014 | 117 | queue_type(self, substs.as_closure().tupled_upvars_ty()); |
74b04a01 XL |
118 | } |
119 | ||
f035d41b | 120 | ty::Generator(def_id, substs, _) => { |
ba9703b0 | 121 | let substs = substs.as_generator(); |
fc512014 | 122 | queue_type(self, substs.tupled_upvars_ty()); |
ba9703b0 XL |
123 | |
124 | let witness = substs.witness(); | |
1b1a35ee | 125 | let interior_tys = match witness.kind() { |
fc512014 | 126 | &ty::GeneratorWitness(tys) => tcx.erase_late_bound_regions(tys), |
f035d41b XL |
127 | _ => { |
128 | tcx.sess.delay_span_bug( | |
129 | tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP), | |
130 | &format!("unexpected generator witness type {:?}", witness), | |
131 | ); | |
132 | return Some(Err(AlwaysRequiresDrop)); | |
133 | } | |
ba9703b0 XL |
134 | }; |
135 | ||
136 | for interior_ty in interior_tys { | |
137 | queue_type(self, interior_ty); | |
138 | } | |
139 | } | |
140 | ||
74b04a01 XL |
141 | // Check for a `Drop` impl and whether this is a union or |
142 | // `ManuallyDrop`. If it's a struct or enum without a `Drop` | |
143 | // impl then check whether the field types need `Drop`. | |
144 | ty::Adt(adt_def, substs) => { | |
94222f64 | 145 | let tys = match (self.adt_components)(adt_def, substs) { |
74b04a01 XL |
146 | Err(e) => return Some(Err(e)), |
147 | Ok(tys) => tys, | |
148 | }; | |
149 | for required_ty in tys { | |
a2a8927a XL |
150 | let required = tcx |
151 | .try_normalize_erasing_regions(self.param_env, required_ty) | |
152 | .unwrap_or(required_ty); | |
153 | ||
3c0e092e | 154 | queue_type(self, required); |
74b04a01 XL |
155 | } |
156 | } | |
157 | ty::Array(..) | ty::Opaque(..) | ty::Projection(..) | ty::Param(_) => { | |
158 | if ty == component { | |
159 | // Return the type to the caller: they may be able | |
160 | // to normalize further than we can. | |
161 | return Some(Ok(component)); | |
162 | } else { | |
163 | // Store the type for later. We can't return here | |
164 | // because we would then lose any other components | |
165 | // of the type. | |
166 | queue_type(self, component); | |
167 | } | |
168 | } | |
169 | _ => return Some(Err(AlwaysRequiresDrop)), | |
170 | } | |
171 | } | |
172 | } | |
173 | ||
ba9703b0 | 174 | None |
74b04a01 XL |
175 | } |
176 | } | |
177 | ||
94222f64 XL |
178 | enum DtorType { |
179 | /// Type has a `Drop` but it is considered insignificant. | |
180 | /// Check the query `adt_significant_drop_tys` for understanding | |
181 | /// "significant" / "insignificant". | |
182 | Insignificant, | |
183 | ||
ee023bcb | 184 | /// Type has a `Drop` implantation. |
94222f64 XL |
185 | Significant, |
186 | } | |
187 | ||
17df50a5 | 188 | // This is a helper function for `adt_drop_tys` and `adt_significant_drop_tys`. |
ee023bcb | 189 | // Depending on the implantation of `adt_has_dtor`, it is used to check if the |
17df50a5 XL |
190 | // ADT has a destructor or if the ADT only has a significant destructor. For |
191 | // understanding significant destructor look at `adt_significant_drop_tys`. | |
c295e0f8 | 192 | fn drop_tys_helper<'tcx>( |
94222f64 | 193 | tcx: TyCtxt<'tcx>, |
c295e0f8 XL |
194 | ty: Ty<'tcx>, |
195 | param_env: rustc_middle::ty::ParamEnv<'tcx>, | |
ee023bcb | 196 | adt_has_dtor: impl Fn(ty::AdtDef<'tcx>) -> Option<DtorType>, |
3c0e092e | 197 | only_significant: bool, |
c295e0f8 | 198 | ) -> impl Iterator<Item = NeedsDropResult<Ty<'tcx>>> { |
3c0e092e XL |
199 | fn with_query_cache<'tcx>( |
200 | tcx: TyCtxt<'tcx>, | |
201 | iter: impl IntoIterator<Item = Ty<'tcx>>, | |
3c0e092e XL |
202 | ) -> NeedsDropResult<Vec<Ty<'tcx>>> { |
203 | iter.into_iter().try_fold(Vec::new(), |mut vec, subty| { | |
204 | match subty.kind() { | |
205 | ty::Adt(adt_id, subst) => { | |
ee023bcb | 206 | for subty in tcx.adt_drop_tys(adt_id.did())? { |
3c0e092e XL |
207 | vec.push(subty.subst(tcx, subst)); |
208 | } | |
209 | } | |
210 | _ => vec.push(subty), | |
211 | }; | |
212 | Ok(vec) | |
213 | }) | |
214 | } | |
215 | ||
ee023bcb | 216 | let adt_components = move |adt_def: ty::AdtDef<'tcx>, substs: SubstsRef<'tcx>| { |
74b04a01 | 217 | if adt_def.is_manually_drop() { |
c295e0f8 | 218 | debug!("drop_tys_helper: `{:?}` is manually drop", adt_def); |
3c0e092e | 219 | Ok(Vec::new()) |
94222f64 XL |
220 | } else if let Some(dtor_info) = adt_has_dtor(adt_def) { |
221 | match dtor_info { | |
222 | DtorType::Significant => { | |
c295e0f8 | 223 | debug!("drop_tys_helper: `{:?}` implements `Drop`", adt_def); |
3c0e092e | 224 | Err(AlwaysRequiresDrop) |
94222f64 XL |
225 | } |
226 | DtorType::Insignificant => { | |
c295e0f8 | 227 | debug!("drop_tys_helper: `{:?}` drop is insignificant", adt_def); |
94222f64 XL |
228 | |
229 | // Since the destructor is insignificant, we just want to make sure all of | |
230 | // the passed in type parameters are also insignificant. | |
231 | // Eg: Vec<T> dtor is insignificant when T=i32 but significant when T=Mutex. | |
ee023bcb | 232 | Ok(substs.types().collect()) |
94222f64 XL |
233 | } |
234 | } | |
74b04a01 | 235 | } else if adt_def.is_union() { |
c295e0f8 | 236 | debug!("drop_tys_helper: `{:?}` is a union", adt_def); |
3c0e092e XL |
237 | Ok(Vec::new()) |
238 | } else { | |
ee023bcb FG |
239 | let field_tys = adt_def.all_fields().map(|field| { |
240 | let r = tcx.type_of(field.did).subst(tcx, substs); | |
241 | debug!("drop_tys_helper: Subst into {:?} with {:?} gettng {:?}", field, substs, r); | |
242 | r | |
243 | }); | |
244 | if only_significant { | |
245 | // We can't recurse through the query system here because we might induce a cycle | |
246 | Ok(field_tys.collect()) | |
247 | } else { | |
248 | // We can use the query system if we consider all drops significant. In that case, | |
249 | // ADTs are `needs_drop` exactly if they `impl Drop` or if any of their "transitive" | |
250 | // fields do. There can be no cycles here, because ADTs cannot contain themselves as | |
251 | // fields. | |
252 | with_query_cache(tcx, field_tys) | |
253 | } | |
74b04a01 | 254 | } |
3c0e092e | 255 | .map(|v| v.into_iter()) |
74b04a01 XL |
256 | }; |
257 | ||
c295e0f8 | 258 | NeedsDropTypes::new(tcx, param_env, ty, adt_components) |
17df50a5 XL |
259 | } |
260 | ||
c295e0f8 XL |
261 | fn adt_consider_insignificant_dtor<'tcx>( |
262 | tcx: TyCtxt<'tcx>, | |
ee023bcb FG |
263 | ) -> impl Fn(ty::AdtDef<'tcx>) -> Option<DtorType> + 'tcx { |
264 | move |adt_def: ty::AdtDef<'tcx>| { | |
265 | let is_marked_insig = tcx.has_attr(adt_def.did(), sym::rustc_insignificant_dtor); | |
94222f64 XL |
266 | if is_marked_insig { |
267 | // In some cases like `std::collections::HashMap` where the struct is a wrapper around | |
268 | // a type that is a Drop type, and the wrapped type (eg: `hashbrown::HashMap`) lies | |
269 | // outside stdlib, we might choose to still annotate the the wrapper (std HashMap) with | |
270 | // `rustc_insignificant_dtor`, even if the type itself doesn't have a `Drop` impl. | |
271 | Some(DtorType::Insignificant) | |
272 | } else if adt_def.destructor(tcx).is_some() { | |
273 | // There is a Drop impl and the type isn't marked insignificant, therefore Drop must be | |
274 | // significant. | |
275 | Some(DtorType::Significant) | |
276 | } else { | |
277 | // No destructor found nor the type is annotated with `rustc_insignificant_dtor`, we | |
278 | // treat this as the simple case of Drop impl for type. | |
279 | None | |
280 | } | |
c295e0f8 XL |
281 | } |
282 | } | |
283 | ||
ee023bcb FG |
284 | fn adt_drop_tys<'tcx>( |
285 | tcx: TyCtxt<'tcx>, | |
286 | def_id: DefId, | |
287 | ) -> Result<&ty::List<Ty<'tcx>>, AlwaysRequiresDrop> { | |
c295e0f8 XL |
288 | // This is for the "adt_drop_tys" query, that considers all `Drop` impls, therefore all dtors are |
289 | // significant. | |
290 | let adt_has_dtor = | |
ee023bcb | 291 | |adt_def: ty::AdtDef<'tcx>| adt_def.destructor(tcx).map(|_| DtorType::Significant); |
3c0e092e XL |
292 | // `tcx.type_of(def_id)` identical to `tcx.make_adt(def, identity_substs)` |
293 | drop_tys_helper(tcx, tcx.type_of(def_id), tcx.param_env(def_id), adt_has_dtor, false) | |
c295e0f8 XL |
294 | .collect::<Result<Vec<_>, _>>() |
295 | .map(|components| tcx.intern_type_list(&components)) | |
296 | } | |
3c0e092e | 297 | // If `def_id` refers to a generic ADT, the queries above and below act as if they had been handed |
ee023bcb | 298 | // a `tcx.make_ty(def, identity_substs)` and as such it is legal to substitute the generic parameters |
3c0e092e | 299 | // of the ADT into the outputted `ty`s. |
c295e0f8 XL |
300 | fn adt_significant_drop_tys( |
301 | tcx: TyCtxt<'_>, | |
302 | def_id: DefId, | |
303 | ) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> { | |
304 | drop_tys_helper( | |
305 | tcx, | |
3c0e092e | 306 | tcx.type_of(def_id), // identical to `tcx.make_adt(def, identity_substs)` |
c295e0f8 XL |
307 | tcx.param_env(def_id), |
308 | adt_consider_insignificant_dtor(tcx), | |
3c0e092e | 309 | true, |
c295e0f8 XL |
310 | ) |
311 | .collect::<Result<Vec<_>, _>>() | |
312 | .map(|components| tcx.intern_type_list(&components)) | |
17df50a5 XL |
313 | } |
314 | ||
f035d41b | 315 | pub(crate) fn provide(providers: &mut ty::query::Providers) { |
17df50a5 XL |
316 | *providers = ty::query::Providers { |
317 | needs_drop_raw, | |
318 | has_significant_drop_raw, | |
319 | adt_drop_tys, | |
320 | adt_significant_drop_tys, | |
321 | ..*providers | |
322 | }; | |
74b04a01 | 323 | } |