Skip to main content

rustc_trait_selection/traits/query/
dropck_outlives.rs

1use rustc_data_structures::fx::FxHashSet;
2use rustc_infer::traits::TraitErrors;
3use rustc_infer::traits::query::type_op::DropckOutlives;
4use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult};
5use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt, Unnormalized};
6use rustc_span::Span;
7use thin_vec::ThinVec;
8use tracing::{debug, instrument};
9
10use crate::solve::NextSolverError;
11use crate::traits::query::NoSolution;
12use crate::traits::query::normalize::QueryNormalizeExt;
13use crate::traits::{FromSolverError, Normalized, ObligationCause, ObligationCtxt, OldSolverError};
14
15/// This returns true if the type `ty` is "trivial" for
16/// dropck-outlives -- that is, if it doesn't require any types to
17/// outlive. This is similar but not *quite* the same as the
18/// `needs_drop` test in the compiler already -- that is, for every
19/// type T for which this function return true, needs-drop would
20/// return `false`. But the reverse does not hold: in particular,
21/// `needs_drop` returns false for `PhantomData`, but it is not
22/// trivial for dropck-outlives.
23///
24/// Note also that `needs_drop` requires a "global" type (i.e., one
25/// with erased regions), but this function does not.
26///
27// FIXME(@lcnr): remove this module and move this function somewhere else.
28pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
29    match ty.kind() {
30        // None of these types have a destructor and hence they do not
31        // require anything in particular to outlive the dtor's
32        // execution.
33        ty::Infer(ty::FreshIntTy(_))
34        | ty::Infer(ty::FreshFloatTy(_))
35        | ty::Bool
36        | ty::Int(_)
37        | ty::Uint(_)
38        | ty::Float(_)
39        | ty::Never
40        | ty::FnDef(..)
41        | ty::FnPtr(..)
42        | ty::Char
43        | ty::CoroutineWitness(..)
44        | ty::RawPtr(_, _)
45        | ty::Ref(..)
46        | ty::Str
47        | ty::Foreign(..)
48        | ty::Error(_) => true,
49
50        // `T is PAT` and `[T]` have same properties as T.
51        ty::Pat(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty),
52        ty::Array(ty, size) => {
53            // Empty array never has a dtor. See issue #110288.
54            match size.try_to_target_usize(tcx) {
55                Some(0) => true,
56                _ => trivial_dropck_outlives(tcx, *ty),
57            }
58        }
59
60        // (T1..Tn) and closures have same properties as T1..Tn --
61        // check if *all* of them are trivial.
62        ty::Tuple(tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t)),
63
64        ty::Closure(_, args) => trivial_dropck_outlives(tcx, args.as_closure().tupled_upvars_ty()),
65        ty::CoroutineClosure(_, args) => {
66            trivial_dropck_outlives(tcx, args.as_coroutine_closure().tupled_upvars_ty())
67        }
68
69        ty::Adt(def, _) => {
70            if def.is_manually_drop() {
71                // `ManuallyDrop` never has a dtor.
72                true
73            } else {
74                // Other types might. Moreover, PhantomData doesn't
75                // have a dtor, but it is considered to own its
76                // content, so it is non-trivial. Unions can have `impl Drop`,
77                // and hence are non-trivial as well.
78                false
79            }
80        }
81
82        // The following *might* require a destructor: needs deeper inspection.
83        ty::Dynamic(..)
84        | ty::Alias(..)
85        | ty::Param(_)
86        | ty::Placeholder(..)
87        | ty::Infer(_)
88        | ty::Bound(..)
89        | ty::Coroutine(..)
90        | ty::UnsafeBinder(_) => false,
91    }
92}
93
94pub fn compute_dropck_outlives_inner<'tcx>(
95    ocx: &ObligationCtxt<'_, 'tcx>,
96    goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>,
97    span: Span,
98) -> Result<DropckOutlivesResult<'tcx>, NoSolution> {
99    match compute_dropck_outlives_with_errors(ocx, goal, span) {
100        Ok(r) => Ok(r),
101        Err(_) => Err(NoSolution),
102    }
103}
104
105pub fn compute_dropck_outlives_with_errors<'tcx, E>(
106    ocx: &ObligationCtxt<'_, 'tcx, E>,
107    goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>,
108    span: Span,
109) -> Result<DropckOutlivesResult<'tcx>, ThinVec<E>>
110where
111    E: FromSolverError<'tcx, NextSolverError<'tcx>> + FromSolverError<'tcx, OldSolverError<'tcx>>,
112{
113    let tcx = ocx.infcx.tcx;
114    let ParamEnvAnd { param_env, value: DropckOutlives { dropped_ty } } = goal;
115
116    let mut result = DropckOutlivesResult { kinds: ::alloc::vec::Vec::new()vec![], overflows: ::alloc::vec::Vec::new()vec![] };
117
118    // A stack of types left to process. Each round, we pop
119    // something from the stack and invoke
120    // `dtorck_constraint_for_ty_inner`. This may produce new types that
121    // have to be pushed on the stack. This continues until we have explored
122    // all the reachable types from the type `dropped_ty`.
123    //
124    // Example: Imagine that we have the following code:
125    //
126    // ```rust
127    // struct A {
128    //     value: B,
129    //     children: Vec<A>,
130    // }
131    //
132    // struct B {
133    //     value: u32
134    // }
135    //
136    // fn f() {
137    //   let a: A = ...;
138    //   ..
139    // } // here, `a` is dropped
140    // ```
141    //
142    // at the point where `a` is dropped, we need to figure out
143    // which types inside of `a` contain region data that may be
144    // accessed by any destructors in `a`. We begin by pushing `A`
145    // onto the stack, as that is the type of `a`. We will then
146    // invoke `dtorck_constraint_for_ty_inner` which will expand `A`
147    // into the types of its fields `(B, Vec<A>)`. These will get
148    // pushed onto the stack. Eventually, expanding `Vec<A>` will
149    // lead to us trying to push `A` a second time -- to prevent
150    // infinite recursion, we notice that `A` was already pushed
151    // once and stop.
152    let mut ty_stack = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [(dropped_ty, 0)]))vec![(dropped_ty, 0)];
153
154    // Set used to detect infinite recursion.
155    let mut ty_set = FxHashSet::default();
156
157    let cause = ObligationCause::dummy_with_span(span);
158    let mut constraints = DropckConstraint::empty();
159    while let Some((ty, depth)) = ty_stack.pop() {
160        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs:160",
                        "rustc_trait_selection::traits::query::dropck_outlives",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs"),
                        ::tracing_core::__macro_support::Option::Some(160u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_trait_selection::traits::query::dropck_outlives"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("{0} kinds, {1} overflows, {2} ty_stack",
                                                    result.kinds.len(), result.overflows.len(), ty_stack.len())
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(
161            "{} kinds, {} overflows, {} ty_stack",
162            result.kinds.len(),
163            result.overflows.len(),
164            ty_stack.len()
165        );
166        dtorck_constraint_for_ty_inner(
167            tcx,
168            ocx.infcx.typing_env(param_env),
169            span,
170            depth,
171            ty,
172            &mut constraints,
173        );
174
175        // "outlives" represent types/regions that may be touched
176        // by a destructor.
177        result.kinds.append(&mut constraints.outlives);
178        result.overflows.append(&mut constraints.overflows);
179
180        // If we have even one overflow, we should stop trying to evaluate further --
181        // chances are, the subsequent overflows for this evaluation won't provide useful
182        // information and will just decrease the speed at which we can emit these errors
183        // (since we'll be printing for just that much longer for the often enormous types
184        // that result here).
185        if !result.overflows.is_empty() {
186            break;
187        }
188
189        // dtorck types are "types that will get dropped but which
190        // do not themselves define a destructor", more or less. We have
191        // to push them onto the stack to be expanded.
192        for ty in constraints.dtorck_types.drain(..) {
193            let ty = if let Ok(Normalized { value: ty, obligations }) =
194                ocx.infcx.at(&cause, param_env).query_normalize(ty)
195            {
196                ocx.register_obligations(obligations);
197
198                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs:198",
                        "rustc_trait_selection::traits::query::dropck_outlives",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs"),
                        ::tracing_core::__macro_support::Option::Some(198u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_trait_selection::traits::query::dropck_outlives"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("dropck_outlives: ty from dtorck_types = {0:?}",
                                                    ty) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
199                ty
200            } else {
201                // Flush errors b/c `deeply_normalize` doesn't expect pending
202                // obligations, and we may have pending obligations from the
203                // branch above (from other types).
204                let errors = ocx.evaluate_obligations_error_on_ambiguity();
205                if let TraitErrors::HasErrors(errors) = errors {
206                    return Err(errors);
207                }
208
209                // When query normalization fails, we don't get back an interesting
210                // reason that we could use to report an error in borrowck. In order to turn
211                // this into a reportable error, we deeply normalize again. We don't expect
212                // this to succeed, so delay a bug if it does.
213                match ocx.deeply_normalize(&cause, param_env, Unnormalized::new_wip(ty)) {
214                    Ok(_) => {
215                        tcx.dcx().span_delayed_bug(
216                            span,
217                            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("query normalize succeeded of {0}, but deep normalize failed",
                ty))
    })format!(
218                                "query normalize succeeded of {ty}, \
219                                but deep normalize failed",
220                            ),
221                        );
222                        ty
223                    }
224                    Err(errors) => return Err(errors),
225                }
226            };
227
228            match ty.kind() {
229                // All parameters live for the duration of the
230                // function.
231                ty::Param(..) => {}
232
233                // A projection that we couldn't resolve - it
234                // might have a destructor.
235                ty::Alias(..) => {
236                    result.kinds.push(ty.into());
237                }
238
239                _ => {
240                    if ty_set.insert(ty) {
241                        ty_stack.push((ty, depth + 1));
242                    }
243                }
244            }
245        }
246    }
247
248    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs:248",
                        "rustc_trait_selection::traits::query::dropck_outlives",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs"),
                        ::tracing_core::__macro_support::Option::Some(248u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_trait_selection::traits::query::dropck_outlives"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("dropck_outlives: result = {0:#?}",
                                                    result) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("dropck_outlives: result = {:#?}", result);
249    Ok(result)
250}
251
252/// Returns a set of constraints that needs to be satisfied in
253/// order for `ty` to be valid for destruction.
254#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("dtorck_constraint_for_ty_inner",
                                    "rustc_trait_selection::traits::query::dropck_outlives",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs"),
                                    ::tracing_core::__macro_support::Option::Some(254u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_trait_selection::traits::query::dropck_outlives"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("depth")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("depth");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("ty")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("ty");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&depth
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&ty)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            if !tcx.recursion_limit().value_within_limit(depth) {
                constraints.overflows.push(ty);
                return;
            }
            if trivial_dropck_outlives(tcx, ty) { return; }
            match *ty.kind() {
                ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_)
                    | ty::Str | ty::Never | ty::Foreign(..) | ty::RawPtr(..) |
                    ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(..) |
                    ty::CoroutineWitness(..) => {}
                ty::Pat(ety, _) | ty::Array(ety, _) | ty::Slice(ety) => {
                    dtorck_constraint_for_ty_inner(tcx, typing_env, span,
                        depth + 1, ety, constraints);
                }
                ty::Tuple(tys) => {
                    for ty in tys.iter() {
                        dtorck_constraint_for_ty_inner(tcx, typing_env, span,
                            depth + 1, ty, constraints);
                    }
                }
                ty::Closure(_, args) => {
                    for ty in args.as_closure().upvar_tys() {
                        dtorck_constraint_for_ty_inner(tcx, typing_env, span,
                            depth + 1, ty, constraints);
                    }
                }
                ty::CoroutineClosure(_, args) => {
                    for ty in args.as_coroutine_closure().upvar_tys() {
                        dtorck_constraint_for_ty_inner(tcx, typing_env, span,
                            depth + 1, ty, constraints);
                    }
                }
                ty::Coroutine(def_id, args) => {
                    let args = args.as_coroutine();
                    let typing_env =
                        tcx.erase_and_anonymize_regions(typing_env);
                    let needs_drop =
                        tcx.mir_coroutine_witnesses(def_id).is_some_and(|witness|
                                {
                                    witness.field_tys.iter().any(|field|
                                            field.ty.needs_drop(tcx, typing_env))
                                });
                    if needs_drop {
                        constraints.outlives.extend(args.upvar_tys().iter().map(ty::GenericArg::from));
                        constraints.outlives.push(args.resume_ty().into());
                    } else {
                        for ty in args.upvar_tys() {
                            dtorck_constraint_for_ty_inner(tcx, typing_env, span,
                                depth + 1, ty, constraints);
                        }
                    }
                }
                ty::Adt(def, args) => {
                    let DropckConstraint { dtorck_types, outlives, overflows } =
                        tcx.at(span).adt_dtorck_constraint(def.did());
                    constraints.dtorck_types.extend(dtorck_types.iter().map(|t|
                                EarlyBinder::bind(tcx,
                                            *t).instantiate(tcx, args).skip_norm_wip()));
                    constraints.outlives.extend(outlives.iter().map(|t|
                                EarlyBinder::bind(tcx,
                                            *t).instantiate(tcx, args).skip_norm_wip()));
                    constraints.overflows.extend(overflows.iter().map(|t|
                                EarlyBinder::bind(tcx,
                                            *t).instantiate(tcx, args).skip_norm_wip()));
                }
                ty::Dynamic(..) => { constraints.outlives.push(ty.into()); }
                ty::Alias(..) | ty::Param(..) => {
                    constraints.dtorck_types.push(ty);
                }
                ty::UnsafeBinder(_) => { constraints.dtorck_types.push(ty); }
                ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) |
                    ty::Error(_) => {
                    tcx.dcx().span_delayed_bug(span,
                        ::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("Unresolved type in dropck: {0:?}.",
                                        ty))
                            }));
                }
            }
        }
    }
}#[instrument(level = "debug", skip(tcx, typing_env, span, constraints))]
255pub fn dtorck_constraint_for_ty_inner<'tcx>(
256    tcx: TyCtxt<'tcx>,
257    typing_env: ty::TypingEnv<'tcx>,
258    span: Span,
259    depth: usize,
260    ty: Ty<'tcx>,
261    constraints: &mut DropckConstraint<'tcx>,
262) {
263    if !tcx.recursion_limit().value_within_limit(depth) {
264        constraints.overflows.push(ty);
265        return;
266    }
267
268    if trivial_dropck_outlives(tcx, ty) {
269        return;
270    }
271
272    match *ty.kind() {
273        ty::Bool
274        | ty::Char
275        | ty::Int(_)
276        | ty::Uint(_)
277        | ty::Float(_)
278        | ty::Str
279        | ty::Never
280        | ty::Foreign(..)
281        | ty::RawPtr(..)
282        | ty::Ref(..)
283        | ty::FnDef(..)
284        | ty::FnPtr(..)
285        | ty::CoroutineWitness(..) => {
286            // these types never have a destructor
287        }
288
289        ty::Pat(ety, _) | ty::Array(ety, _) | ty::Slice(ety) => {
290            // single-element containers, behave like their element
291            dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ety, constraints);
292        }
293
294        ty::Tuple(tys) => {
295            for ty in tys.iter() {
296                dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ty, constraints);
297            }
298        }
299
300        ty::Closure(_, args) => {
301            for ty in args.as_closure().upvar_tys() {
302                dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ty, constraints);
303            }
304        }
305
306        ty::CoroutineClosure(_, args) => {
307            for ty in args.as_coroutine_closure().upvar_tys() {
308                dtorck_constraint_for_ty_inner(tcx, typing_env, span, depth + 1, ty, constraints);
309            }
310        }
311
312        ty::Coroutine(def_id, args) => {
313            // rust-lang/rust#49918: Locals can be stored across await points in the coroutine,
314            // called interior/witness types. Since we do not compute these witnesses until after
315            // building MIR, we consider all coroutines to unconditionally require a drop during
316            // MIR building. However, considering the coroutine to unconditionally require a drop
317            // here may unnecessarily require its upvars' regions to be live when they don't need
318            // to be, leading to borrowck errors: <https://github.com/rust-lang/rust/issues/116242>.
319            //
320            // Here, we implement a more precise approximation for the coroutine's dtorck constraint
321            // by considering whether any of the interior types needs drop. Note that this is still
322            // an approximation because the coroutine interior has its regions erased, so we must add
323            // *all* of the upvars to live types set if we find that *any* interior type needs drop.
324            // This is because any of the regions captured in the upvars may be stored in the interior,
325            // which then has its regions replaced by a binder (conceptually erasing the regions),
326            // so there's no way to enforce that the precise region in the interior type is live
327            // since we've lost that information by this point.
328            //
329            // Note also that this check requires that the coroutine's upvars are use-live, since
330            // a region from a type that does not have a destructor that was captured in an upvar
331            // may flow into an interior type with a destructor. This is stronger than requiring
332            // the upvars are drop-live.
333            //
334            // For example, if we capture two upvar references `&'1 (), &'2 ()` and have some type
335            // in the interior, `for<'r> { NeedsDrop<'r> }`, we have no way to tell whether the
336            // region `'r` came from the `'1` or `'2` region, so we require both are live. This
337            // could even be unnecessary if `'r` was actually a `'static` region or some region
338            // local to the coroutine! That's why it's an approximation.
339            let args = args.as_coroutine();
340
341            // Note that we don't care about whether the resume type has any drops since this is
342            // redundant; there is no storage for the resume type, so if it is actually stored
343            // in the interior, we'll already detect the need for a drop by checking the interior.
344            //
345            // FIXME(@lcnr): Why do we erase regions in the env here? Seems odd
346            let typing_env = tcx.erase_and_anonymize_regions(typing_env);
347            let needs_drop = tcx.mir_coroutine_witnesses(def_id).is_some_and(|witness| {
348                witness.field_tys.iter().any(|field| field.ty.needs_drop(tcx, typing_env))
349            });
350            if needs_drop {
351                // Pushing types directly to `constraints.outlives` is equivalent
352                // to requiring them to be use-live, since if we were instead to
353                // recurse on them like we do below, we only end up collecting the
354                // types that are relevant for drop-liveness.
355                constraints.outlives.extend(args.upvar_tys().iter().map(ty::GenericArg::from));
356                constraints.outlives.push(args.resume_ty().into());
357            } else {
358                // Even if a witness type doesn't need a drop, we still require that
359                // the upvars are drop-live. This is only needed if we aren't already
360                // counting *all* of the upvars as use-live above, since use-liveness
361                // is a *stronger requirement* than drop-liveness. Recursing here
362                // unconditionally would just be collecting duplicated types for no
363                // reason.
364                for ty in args.upvar_tys() {
365                    dtorck_constraint_for_ty_inner(
366                        tcx,
367                        typing_env,
368                        span,
369                        depth + 1,
370                        ty,
371                        constraints,
372                    );
373                }
374            }
375        }
376
377        ty::Adt(def, args) => {
378            let DropckConstraint { dtorck_types, outlives, overflows } =
379                tcx.at(span).adt_dtorck_constraint(def.did());
380            // FIXME: we can try to recursively `dtorck_constraint_on_ty`
381            // there, but that needs some way to handle cycles.
382            constraints.dtorck_types.extend(
383                dtorck_types
384                    .iter()
385                    .map(|t| EarlyBinder::bind(tcx, *t).instantiate(tcx, args).skip_norm_wip()),
386            );
387            constraints.outlives.extend(
388                outlives
389                    .iter()
390                    .map(|t| EarlyBinder::bind(tcx, *t).instantiate(tcx, args).skip_norm_wip()),
391            );
392            constraints.overflows.extend(
393                overflows
394                    .iter()
395                    .map(|t| EarlyBinder::bind(tcx, *t).instantiate(tcx, args).skip_norm_wip()),
396            );
397        }
398
399        // Objects must be alive in order for their destructor
400        // to be called.
401        ty::Dynamic(..) => {
402            constraints.outlives.push(ty.into());
403        }
404
405        // Types that can't be resolved. Pass them forward.
406        ty::Alias(..) | ty::Param(..) => {
407            constraints.dtorck_types.push(ty);
408        }
409
410        // Can't instantiate binder here.
411        ty::UnsafeBinder(_) => {
412            constraints.dtorck_types.push(ty);
413        }
414
415        ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => {
416            // By the time this code runs, all type variables ought to
417            // be fully resolved.
418            tcx.dcx().span_delayed_bug(span, format!("Unresolved type in dropck: {:?}.", ty));
419        }
420    }
421}