rustc_trait_selection/traits/query/
dropck_outlives.rs

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