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