]>
Commit | Line | Data |
---|---|---|
49aad941 FG |
1 | use crate::traits::query::normalize::QueryNormalizeExt; |
2 | use crate::traits::query::NoSolution; | |
3 | use crate::traits::{Normalized, ObligationCause, ObligationCtxt}; | |
74b04a01 | 4 | |
49aad941 FG |
5 | use rustc_data_structures::fx::FxHashSet; |
6 | use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult}; | |
7 | use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt}; | |
ed00b5ec | 8 | use rustc_span::{Span, DUMMY_SP}; |
0531ce1d | 9 | |
0531ce1d XL |
10 | /// This returns true if the type `ty` is "trivial" for |
11 | /// dropck-outlives -- that is, if it doesn't require any types to | |
12 | /// outlive. This is similar but not *quite* the same as the | |
13 | /// `needs_drop` test in the compiler already -- that is, for every | |
14 | /// type T for which this function return true, needs-drop would | |
9fa01778 | 15 | /// return `false`. But the reverse does not hold: in particular, |
0531ce1d XL |
16 | /// `needs_drop` returns false for `PhantomData`, but it is not |
17 | /// trivial for dropck-outlives. | |
18 | /// | |
19 | /// Note also that `needs_drop` requires a "global" type (i.e., one | |
a1dfa0c6 | 20 | /// with erased regions), but this function does not. |
064997fb FG |
21 | /// |
22 | // FIXME(@lcnr): remove this module and move this function somewhere else. | |
dc9dc135 | 23 | pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { |
1b1a35ee | 24 | match ty.kind() { |
0531ce1d XL |
25 | // None of these types have a destructor and hence they do not |
26 | // require anything in particular to outlive the dtor's | |
27 | // execution. | |
b7449926 XL |
28 | ty::Infer(ty::FreshIntTy(_)) |
29 | | ty::Infer(ty::FreshFloatTy(_)) | |
30 | | ty::Bool | |
31 | | ty::Int(_) | |
32 | | ty::Uint(_) | |
33 | | ty::Float(_) | |
34 | | ty::Never | |
35 | | ty::FnDef(..) | |
36 | | ty::FnPtr(_) | |
37 | | ty::Char | |
ed00b5ec | 38 | | ty::CoroutineWitness(..) |
b7449926 XL |
39 | | ty::RawPtr(_) |
40 | | ty::Ref(..) | |
41 | | ty::Str | |
42 | | ty::Foreign(..) | |
f035d41b | 43 | | ty::Error(_) => true, |
0531ce1d XL |
44 | |
45 | // [T; N] and [T] have same properties as T. | |
5099ac24 | 46 | ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty), |
0531ce1d XL |
47 | |
48 | // (T1..Tn) and closures have same properties as T1..Tn -- | |
064997fb | 49 | // check if *all* of them are trivial. |
5e7ed085 | 50 | ty::Tuple(tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t)), |
add651ee FG |
51 | ty::Closure(_, ref args) => { |
52 | trivial_dropck_outlives(tcx, args.as_closure().tupled_upvars_ty()) | |
dfeec247 | 53 | } |
0531ce1d | 54 | |
b7449926 | 55 | ty::Adt(def, _) => { |
5e7ed085 | 56 | if Some(def.did()) == tcx.lang_items().manually_drop() { |
8faf50e0 | 57 | // `ManuallyDrop` never has a dtor. |
0531ce1d XL |
58 | true |
59 | } else { | |
60 | // Other types might. Moreover, PhantomData doesn't | |
61 | // have a dtor, but it is considered to own its | |
b7449926 XL |
62 | // content, so it is non-trivial. Unions can have `impl Drop`, |
63 | // and hence are non-trivial as well. | |
0531ce1d XL |
64 | false |
65 | } | |
66 | } | |
67 | ||
0bf4aa26 | 68 | // The following *might* require a destructor: needs deeper inspection. |
b7449926 | 69 | ty::Dynamic(..) |
9c376795 | 70 | | ty::Alias(..) |
b7449926 | 71 | | ty::Param(_) |
a1dfa0c6 | 72 | | ty::Placeholder(..) |
b7449926 | 73 | | ty::Infer(_) |
a1dfa0c6 | 74 | | ty::Bound(..) |
ed00b5ec | 75 | | ty::Coroutine(..) => false, |
0531ce1d XL |
76 | } |
77 | } | |
49aad941 FG |
78 | |
79 | pub fn compute_dropck_outlives_inner<'tcx>( | |
80 | ocx: &ObligationCtxt<'_, 'tcx>, | |
81 | goal: ParamEnvAnd<'tcx, Ty<'tcx>>, | |
82 | ) -> Result<DropckOutlivesResult<'tcx>, NoSolution> { | |
83 | let tcx = ocx.infcx.tcx; | |
84 | let ParamEnvAnd { param_env, value: for_ty } = goal; | |
85 | ||
86 | let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; | |
87 | ||
88 | // A stack of types left to process. Each round, we pop | |
89 | // something from the stack and invoke | |
90 | // `dtorck_constraint_for_ty_inner`. This may produce new types that | |
91 | // have to be pushed on the stack. This continues until we have explored | |
92 | // all the reachable types from the type `for_ty`. | |
93 | // | |
94 | // Example: Imagine that we have the following code: | |
95 | // | |
96 | // ```rust | |
97 | // struct A { | |
98 | // value: B, | |
99 | // children: Vec<A>, | |
100 | // } | |
101 | // | |
102 | // struct B { | |
103 | // value: u32 | |
104 | // } | |
105 | // | |
106 | // fn f() { | |
107 | // let a: A = ...; | |
108 | // .. | |
109 | // } // here, `a` is dropped | |
110 | // ``` | |
111 | // | |
112 | // at the point where `a` is dropped, we need to figure out | |
113 | // which types inside of `a` contain region data that may be | |
114 | // accessed by any destructors in `a`. We begin by pushing `A` | |
115 | // onto the stack, as that is the type of `a`. We will then | |
116 | // invoke `dtorck_constraint_for_ty_inner` which will expand `A` | |
117 | // into the types of its fields `(B, Vec<A>)`. These will get | |
118 | // pushed onto the stack. Eventually, expanding `Vec<A>` will | |
119 | // lead to us trying to push `A` a second time -- to prevent | |
120 | // infinite recursion, we notice that `A` was already pushed | |
121 | // once and stop. | |
122 | let mut ty_stack = vec![(for_ty, 0)]; | |
123 | ||
124 | // Set used to detect infinite recursion. | |
125 | let mut ty_set = FxHashSet::default(); | |
126 | ||
127 | let cause = ObligationCause::dummy(); | |
128 | let mut constraints = DropckConstraint::empty(); | |
129 | while let Some((ty, depth)) = ty_stack.pop() { | |
130 | debug!( | |
131 | "{} kinds, {} overflows, {} ty_stack", | |
132 | result.kinds.len(), | |
133 | result.overflows.len(), | |
134 | ty_stack.len() | |
135 | ); | |
781aab86 | 136 | dtorck_constraint_for_ty_inner(tcx, param_env, DUMMY_SP, depth, ty, &mut constraints)?; |
49aad941 FG |
137 | |
138 | // "outlives" represent types/regions that may be touched | |
139 | // by a destructor. | |
140 | result.kinds.append(&mut constraints.outlives); | |
141 | result.overflows.append(&mut constraints.overflows); | |
142 | ||
143 | // If we have even one overflow, we should stop trying to evaluate further -- | |
144 | // chances are, the subsequent overflows for this evaluation won't provide useful | |
145 | // information and will just decrease the speed at which we can emit these errors | |
146 | // (since we'll be printing for just that much longer for the often enormous types | |
147 | // that result here). | |
148 | if !result.overflows.is_empty() { | |
149 | break; | |
150 | } | |
151 | ||
152 | // dtorck types are "types that will get dropped but which | |
153 | // do not themselves define a destructor", more or less. We have | |
154 | // to push them onto the stack to be expanded. | |
155 | for ty in constraints.dtorck_types.drain(..) { | |
156 | let Normalized { value: ty, obligations } = | |
157 | ocx.infcx.at(&cause, param_env).query_normalize(ty)?; | |
158 | ocx.register_obligations(obligations); | |
159 | ||
160 | debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); | |
161 | ||
162 | match ty.kind() { | |
163 | // All parameters live for the duration of the | |
164 | // function. | |
165 | ty::Param(..) => {} | |
166 | ||
167 | // A projection that we couldn't resolve - it | |
168 | // might have a destructor. | |
169 | ty::Alias(..) => { | |
170 | result.kinds.push(ty.into()); | |
171 | } | |
172 | ||
173 | _ => { | |
174 | if ty_set.insert(ty) { | |
175 | ty_stack.push((ty, depth + 1)); | |
176 | } | |
177 | } | |
178 | } | |
179 | } | |
180 | } | |
181 | ||
182 | debug!("dropck_outlives: result = {:#?}", result); | |
183 | Ok(result) | |
184 | } | |
185 | ||
186 | /// Returns a set of constraints that needs to be satisfied in | |
187 | /// order for `ty` to be valid for destruction. | |
781aab86 | 188 | #[instrument(level = "debug", skip(tcx, param_env, span, constraints))] |
49aad941 FG |
189 | pub fn dtorck_constraint_for_ty_inner<'tcx>( |
190 | tcx: TyCtxt<'tcx>, | |
781aab86 | 191 | param_env: ty::ParamEnv<'tcx>, |
49aad941 | 192 | span: Span, |
49aad941 FG |
193 | depth: usize, |
194 | ty: Ty<'tcx>, | |
195 | constraints: &mut DropckConstraint<'tcx>, | |
196 | ) -> Result<(), NoSolution> { | |
49aad941 FG |
197 | if !tcx.recursion_limit().value_within_limit(depth) { |
198 | constraints.overflows.push(ty); | |
199 | return Ok(()); | |
200 | } | |
201 | ||
202 | if trivial_dropck_outlives(tcx, ty) { | |
203 | return Ok(()); | |
204 | } | |
205 | ||
206 | match ty.kind() { | |
207 | ty::Bool | |
208 | | ty::Char | |
209 | | ty::Int(_) | |
210 | | ty::Uint(_) | |
211 | | ty::Float(_) | |
212 | | ty::Str | |
213 | | ty::Never | |
214 | | ty::Foreign(..) | |
215 | | ty::RawPtr(..) | |
216 | | ty::Ref(..) | |
217 | | ty::FnDef(..) | |
218 | | ty::FnPtr(_) | |
ed00b5ec | 219 | | ty::CoroutineWitness(..) => { |
49aad941 FG |
220 | // these types never have a destructor |
221 | } | |
222 | ||
223 | ty::Array(ety, _) | ty::Slice(ety) => { | |
224 | // single-element containers, behave like their element | |
225 | rustc_data_structures::stack::ensure_sufficient_stack(|| { | |
781aab86 | 226 | dtorck_constraint_for_ty_inner(tcx, param_env, span, depth + 1, *ety, constraints) |
49aad941 FG |
227 | })?; |
228 | } | |
229 | ||
230 | ty::Tuple(tys) => rustc_data_structures::stack::ensure_sufficient_stack(|| { | |
231 | for ty in tys.iter() { | |
781aab86 | 232 | dtorck_constraint_for_ty_inner(tcx, param_env, span, depth + 1, ty, constraints)?; |
49aad941 FG |
233 | } |
234 | Ok::<_, NoSolution>(()) | |
235 | })?, | |
236 | ||
add651ee FG |
237 | ty::Closure(_, args) => { |
238 | if !args.as_closure().is_valid() { | |
49aad941 FG |
239 | // By the time this code runs, all type variables ought to |
240 | // be fully resolved. | |
241 | ||
242 | tcx.sess.delay_span_bug( | |
243 | span, | |
244 | format!("upvar_tys for closure not found. Expected capture information for closure {ty}",), | |
245 | ); | |
246 | return Err(NoSolution); | |
247 | } | |
248 | ||
249 | rustc_data_structures::stack::ensure_sufficient_stack(|| { | |
add651ee | 250 | for ty in args.as_closure().upvar_tys() { |
781aab86 FG |
251 | dtorck_constraint_for_ty_inner( |
252 | tcx, | |
253 | param_env, | |
254 | span, | |
255 | depth + 1, | |
256 | ty, | |
257 | constraints, | |
258 | )?; | |
49aad941 FG |
259 | } |
260 | Ok::<_, NoSolution>(()) | |
261 | })? | |
262 | } | |
263 | ||
ed00b5ec | 264 | ty::Coroutine(_, args, _movability) => { |
49aad941 | 265 | // rust-lang/rust#49918: types can be constructed, stored |
ed00b5ec | 266 | // in the interior, and sit idle when coroutine yields |
49aad941 FG |
267 | // (and is subsequently dropped). |
268 | // | |
269 | // It would be nice to descend into interior of a | |
ed00b5ec | 270 | // coroutine to determine what effects dropping it might |
49aad941 FG |
271 | // have (by looking at any drop effects associated with |
272 | // its interior). | |
273 | // | |
274 | // However, the interior's representation uses things like | |
ed00b5ec | 275 | // CoroutineWitness that explicitly assume they are not |
49aad941 | 276 | // traversed in such a manner. So instead, we will |
ed00b5ec | 277 | // simplify things for now by treating all coroutines as |
49aad941 | 278 | // if they were like trait objects, where its upvars must |
ed00b5ec | 279 | // all be alive for the coroutine's (potential) |
49aad941 FG |
280 | // destructor. |
281 | // | |
282 | // In particular, skipping over `_interior` is safe | |
283 | // because any side-effects from dropping `_interior` can | |
284 | // only take place through references with lifetimes | |
285 | // derived from lifetimes attached to the upvars and resume | |
286 | // argument, and we *do* incorporate those here. | |
ed00b5ec | 287 | let args = args.as_coroutine(); |
781aab86 | 288 | if !args.is_valid() { |
49aad941 FG |
289 | // By the time this code runs, all type variables ought to |
290 | // be fully resolved. | |
291 | tcx.sess.delay_span_bug( | |
292 | span, | |
ed00b5ec | 293 | format!("upvar_tys for coroutine not found. Expected capture information for coroutine {ty}",), |
49aad941 FG |
294 | ); |
295 | return Err(NoSolution); | |
296 | } | |
297 | ||
781aab86 FG |
298 | // While we conservatively assume that all coroutines require drop |
299 | // to avoid query cycles during MIR building, we can check the actual | |
300 | // witness during borrowck to avoid unnecessary liveness constraints. | |
301 | if args.witness().needs_drop(tcx, tcx.erase_regions(param_env)) { | |
302 | constraints.outlives.extend(args.upvar_tys().iter().map(ty::GenericArg::from)); | |
303 | constraints.outlives.push(args.resume_ty().into()); | |
304 | } | |
49aad941 FG |
305 | } |
306 | ||
add651ee | 307 | ty::Adt(def, args) => { |
49aad941 FG |
308 | let DropckConstraint { dtorck_types, outlives, overflows } = |
309 | tcx.at(span).adt_dtorck_constraint(def.did())?; | |
310 | // FIXME: we can try to recursively `dtorck_constraint_on_ty` | |
311 | // there, but that needs some way to handle cycles. | |
312 | constraints | |
313 | .dtorck_types | |
add651ee | 314 | .extend(dtorck_types.iter().map(|t| EarlyBinder::bind(*t).instantiate(tcx, args))); |
49aad941 FG |
315 | constraints |
316 | .outlives | |
add651ee | 317 | .extend(outlives.iter().map(|t| EarlyBinder::bind(*t).instantiate(tcx, args))); |
49aad941 FG |
318 | constraints |
319 | .overflows | |
add651ee | 320 | .extend(overflows.iter().map(|t| EarlyBinder::bind(*t).instantiate(tcx, args))); |
49aad941 FG |
321 | } |
322 | ||
323 | // Objects must be alive in order for their destructor | |
324 | // to be called. | |
325 | ty::Dynamic(..) => { | |
326 | constraints.outlives.push(ty.into()); | |
327 | } | |
328 | ||
329 | // Types that can't be resolved. Pass them forward. | |
330 | ty::Alias(..) | ty::Param(..) => { | |
331 | constraints.dtorck_types.push(ty); | |
332 | } | |
333 | ||
334 | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => { | |
335 | // By the time this code runs, all type variables ought to | |
336 | // be fully resolved. | |
337 | return Err(NoSolution); | |
338 | } | |
339 | } | |
340 | ||
341 | Ok(()) | |
342 | } |