1 use rustc
::hir
::def_id
::DefId
;
2 use rustc
::infer
::canonical
::{Canonical, QueryResponse}
;
3 use rustc
::traits
::query
::dropck_outlives
::{DropckOutlivesResult, DtorckConstraint}
;
4 use rustc
::traits
::query
::dropck_outlives
::trivial_dropck_outlives
;
5 use rustc
::traits
::query
::{CanonicalTyGoal, NoSolution}
;
6 use rustc
::traits
::{TraitEngine, Normalized, ObligationCause, TraitEngineExt}
;
7 use rustc
::ty
::query
::Providers
;
8 use rustc
::ty
::subst
::{Subst, InternalSubsts}
;
9 use rustc
::ty
::{self, ParamEnvAnd, Ty, TyCtxt}
;
10 use rustc
::util
::nodemap
::FxHashSet
;
11 use syntax
::source_map
::{Span, DUMMY_SP}
;
13 crate fn provide(p
: &mut Providers
<'_
>) {
16 adt_dtorck_constraint
,
21 fn dropck_outlives
<'tcx
>(
23 canonical_goal
: CanonicalTyGoal
<'tcx
>,
24 ) -> Result
<&'tcx Canonical
<'tcx
, QueryResponse
<'tcx
, DropckOutlivesResult
<'tcx
>>>, NoSolution
> {
25 debug
!("dropck_outlives(goal={:#?})", canonical_goal
);
27 tcx
.infer_ctxt().enter_with_canonical(
30 |ref infcx
, goal
, canonical_inference_vars
| {
37 let mut result
= DropckOutlivesResult
{
42 // A stack of types left to process. Each round, we pop
43 // something from the stack and invoke
44 // `dtorck_constraint_for_ty`. This may produce new types that
45 // have to be pushed on the stack. This continues until we have explored
46 // all the reachable types from the type `for_ty`.
48 // Example: Imagine that we have the following code:
63 // } // here, `a` is dropped
66 // at the point where `a` is dropped, we need to figure out
67 // which types inside of `a` contain region data that may be
68 // accessed by any destructors in `a`. We begin by pushing `A`
69 // onto the stack, as that is the type of `a`. We will then
70 // invoke `dtorck_constraint_for_ty` which will expand `A`
71 // into the types of its fields `(B, Vec<A>)`. These will get
72 // pushed onto the stack. Eventually, expanding `Vec<A>` will
73 // lead to us trying to push `A` a second time -- to prevent
74 // infinite recursion, we notice that `A` was already pushed
76 let mut ty_stack
= vec
![(for_ty
, 0)];
78 // Set used to detect infinite recursion.
79 let mut ty_set
= FxHashSet
::default();
81 let mut fulfill_cx
= TraitEngine
::new(infcx
.tcx
);
83 let cause
= ObligationCause
::dummy();
84 let mut constraints
= DtorckConstraint
::empty();
85 while let Some((ty
, depth
)) = ty_stack
.pop() {
86 info
!("{} kinds, {} overflows, {} ty_stack",
87 result
.kinds
.len(), result
.overflows
.len(), ty_stack
.len());
88 dtorck_constraint_for_ty(tcx
, DUMMY_SP
, for_ty
, depth
, ty
, &mut constraints
)?
;
90 // "outlives" represent types/regions that may be touched
92 result
.kinds
.extend(constraints
.outlives
.drain(..));
93 result
.overflows
.extend(constraints
.overflows
.drain(..));
95 // If we have even one overflow, we should stop trying to evaluate further --
96 // chances are, the subsequent overflows for this evaluation won't provide useful
97 // information and will just decrease the speed at which we can emit these errors
98 // (since we'll be printing for just that much longer for the often enormous types
100 if result
.overflows
.len() >= 1 {
104 // dtorck types are "types that will get dropped but which
105 // do not themselves define a destructor", more or less. We have
106 // to push them onto the stack to be expanded.
107 for ty
in constraints
.dtorck_types
.drain(..) {
108 match infcx
.at(&cause
, param_env
).normalize(&ty
) {
113 fulfill_cx
.register_predicate_obligations(infcx
, obligations
);
115 debug
!("dropck_outlives: ty from dtorck_types = {:?}", ty
);
118 // All parameters live for the duration of the
122 // A projection that we couldn't resolve - it
123 // might have a destructor.
124 ty
::Projection(..) | ty
::Opaque(..) => {
125 result
.kinds
.push(ty
.into());
129 if ty_set
.insert(ty
) {
130 ty_stack
.push((ty
, depth
+ 1));
136 // We don't actually expect to fail to normalize.
137 // That implies a WF error somewhere else.
139 return Err(NoSolution
);
145 debug
!("dropck_outlives: result = {:#?}", result
);
147 infcx
.make_canonicalized_query_response(
148 canonical_inference_vars
,
156 /// Returns a set of constraints that needs to be satisfied in
157 /// order for `ty` to be valid for destruction.
158 fn dtorck_constraint_for_ty
<'tcx
>(
164 constraints
: &mut DtorckConstraint
<'tcx
>,
165 ) -> Result
<(), NoSolution
> {
167 "dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})",
168 span
, for_ty
, depth
, ty
171 if depth
>= *tcx
.sess
.recursion_limit
.get() {
172 constraints
.overflows
.push(ty
);
176 if trivial_dropck_outlives(tcx
, ty
) {
193 | ty
::GeneratorWitness(..) => {
194 // these types never have a destructor
197 ty
::Array(ety
, _
) | ty
::Slice(ety
) => {
198 // single-element containers, behave like their element
199 dtorck_constraint_for_ty(tcx
, span
, for_ty
, depth
+ 1, ety
, constraints
)?
;
202 ty
::Tuple(tys
) => for ty
in tys
.iter() {
203 dtorck_constraint_for_ty(tcx
, span
, for_ty
, depth
+ 1, ty
.expect_ty(), constraints
)?
;
206 ty
::Closure(def_id
, substs
) => for ty
in substs
.as_closure().upvar_tys(def_id
, tcx
) {
207 dtorck_constraint_for_ty(tcx
, span
, for_ty
, depth
+ 1, ty
, constraints
)?
;
210 ty
::Generator(def_id
, substs
, _movability
) => {
211 // rust-lang/rust#49918: types can be constructed, stored
212 // in the interior, and sit idle when generator yields
213 // (and is subsequently dropped).
215 // It would be nice to descend into interior of a
216 // generator to determine what effects dropping it might
217 // have (by looking at any drop effects associated with
220 // However, the interior's representation uses things like
221 // GeneratorWitness that explicitly assume they are not
222 // traversed in such a manner. So instead, we will
223 // simplify things for now by treating all generators as
224 // if they were like trait objects, where its upvars must
225 // all be alive for the generator's (potential)
228 // In particular, skipping over `_interior` is safe
229 // because any side-effects from dropping `_interior` can
230 // only take place through references with lifetimes
231 // derived from lifetimes attached to the upvars, and we
232 // *do* incorporate the upvars here.
234 constraints
.outlives
.extend(substs
.as_generator().upvar_tys(def_id
, tcx
)
235 .map(|t
| -> ty
::subst
::GenericArg
<'tcx
> { t.into() }
));
238 ty
::Adt(def
, substs
) => {
239 let DtorckConstraint
{
243 } = tcx
.at(span
).adt_dtorck_constraint(def
.did
)?
;
244 // FIXME: we can try to recursively `dtorck_constraint_on_ty`
245 // there, but that needs some way to handle cycles.
246 constraints
.dtorck_types
.extend(dtorck_types
.subst(tcx
, substs
));
247 constraints
.outlives
.extend(outlives
.subst(tcx
, substs
));
248 constraints
.overflows
.extend(overflows
.subst(tcx
, substs
));
251 // Objects must be alive in order for their destructor
254 constraints
.outlives
.push(ty
.into());
257 // Types that can't be resolved. Pass them forward.
258 ty
::Projection(..) | ty
::Opaque(..) | ty
::Param(..) => {
259 constraints
.dtorck_types
.push(ty
);
262 ty
::UnnormalizedProjection(..) => bug
!("only used with chalk-engine"),
264 ty
::Placeholder(..) | ty
::Bound(..) | ty
::Infer(..) | ty
::Error
=> {
265 // By the time this code runs, all type variables ought to
266 // be fully resolved.
267 return Err(NoSolution
)
274 /// Calculates the dtorck constraint for a type.
275 crate fn adt_dtorck_constraint(
278 ) -> Result
<DtorckConstraint
<'_
>, NoSolution
> {
279 let def
= tcx
.adt_def(def_id
);
280 let span
= tcx
.def_span(def_id
);
281 debug
!("dtorck_constraint: {:?}", def
);
283 if def
.is_phantom_data() {
284 // The first generic parameter here is guaranteed to be a type because it's
286 let substs
= InternalSubsts
::identity_for_item(tcx
, def_id
);
287 assert_eq
!(substs
.len(), 1);
288 let result
= DtorckConstraint
{
290 dtorck_types
: vec
![substs
.type_at(0)],
293 debug
!("dtorck_constraint: {:?} => {:?}", def
, result
);
297 let mut result
= DtorckConstraint
::empty();
298 for field
in def
.all_fields() {
299 let fty
= tcx
.type_of(field
.did
);
300 dtorck_constraint_for_ty(tcx
, span
, fty
, 0, fty
, &mut result
)?
;
302 result
.outlives
.extend(tcx
.destructor_constraints(def
));
303 dedup_dtorck_constraint(&mut result
);
305 debug
!("dtorck_constraint: {:?} => {:?}", def
, result
);
310 fn dedup_dtorck_constraint(c
: &mut DtorckConstraint
<'_
>) {
311 let mut outlives
= FxHashSet
::default();
312 let mut dtorck_types
= FxHashSet
::default();
314 c
.outlives
.retain(|&val
| outlives
.replace(val
).is_none());
316 .retain(|&val
| dtorck_types
.replace(val
).is_none());