Skip to content

Commit d7a2fdd

Browse files
Uplift complex type ops back into typeck so we can call them locally
1 parent a25aee1 commit d7a2fdd

File tree

9 files changed

+574
-558
lines changed

9 files changed

+574
-558
lines changed

compiler/rustc_borrowck/src/type_check/liveness/trace.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use rustc_index::bit_set::HybridBitSet;
33
use rustc_index::interval::IntervalSet;
44
use rustc_infer::infer::canonical::QueryRegionConstraints;
55
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
6+
use rustc_middle::traits::query::DropckOutlivesResult;
67
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
78
use rustc_span::DUMMY_SP;
8-
use rustc_trait_selection::traits::query::dropck_outlives::DropckOutlivesResult;
99
use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives;
1010
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
1111
use std::rc::Rc;

compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs

+267-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
use rustc_middle::ty::{self, Ty, TyCtxt};
1+
use crate::traits::query::normalize::QueryNormalizeExt;
2+
use crate::traits::query::NoSolution;
3+
use crate::traits::{Normalized, ObligationCause, ObligationCtxt};
24

3-
pub use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult};
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};
8+
use rustc_span::source_map::{Span, DUMMY_SP};
49

510
/// This returns true if the type `ty` is "trivial" for
611
/// dropck-outlives -- that is, if it doesn't require any types to
@@ -71,3 +76,263 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
7176
| ty::Generator(..) => false,
7277
}
7378
}
79+
80+
pub fn compute_dropck_outlives_inner<'tcx>(
81+
ocx: &ObligationCtxt<'_, 'tcx>,
82+
goal: ParamEnvAnd<'tcx, Ty<'tcx>>,
83+
) -> Result<DropckOutlivesResult<'tcx>, NoSolution> {
84+
let tcx = ocx.infcx.tcx;
85+
let ParamEnvAnd { param_env, value: for_ty } = goal;
86+
87+
let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] };
88+
89+
// A stack of types left to process. Each round, we pop
90+
// something from the stack and invoke
91+
// `dtorck_constraint_for_ty_inner`. This may produce new types that
92+
// have to be pushed on the stack. This continues until we have explored
93+
// all the reachable types from the type `for_ty`.
94+
//
95+
// Example: Imagine that we have the following code:
96+
//
97+
// ```rust
98+
// struct A {
99+
// value: B,
100+
// children: Vec<A>,
101+
// }
102+
//
103+
// struct B {
104+
// value: u32
105+
// }
106+
//
107+
// fn f() {
108+
// let a: A = ...;
109+
// ..
110+
// } // here, `a` is dropped
111+
// ```
112+
//
113+
// at the point where `a` is dropped, we need to figure out
114+
// which types inside of `a` contain region data that may be
115+
// accessed by any destructors in `a`. We begin by pushing `A`
116+
// onto the stack, as that is the type of `a`. We will then
117+
// invoke `dtorck_constraint_for_ty_inner` which will expand `A`
118+
// into the types of its fields `(B, Vec<A>)`. These will get
119+
// pushed onto the stack. Eventually, expanding `Vec<A>` will
120+
// lead to us trying to push `A` a second time -- to prevent
121+
// infinite recursion, we notice that `A` was already pushed
122+
// once and stop.
123+
let mut ty_stack = vec![(for_ty, 0)];
124+
125+
// Set used to detect infinite recursion.
126+
let mut ty_set = FxHashSet::default();
127+
128+
let cause = ObligationCause::dummy();
129+
let mut constraints = DropckConstraint::empty();
130+
while let Some((ty, depth)) = ty_stack.pop() {
131+
debug!(
132+
"{} kinds, {} overflows, {} ty_stack",
133+
result.kinds.len(),
134+
result.overflows.len(),
135+
ty_stack.len()
136+
);
137+
dtorck_constraint_for_ty_inner(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?;
138+
139+
// "outlives" represent types/regions that may be touched
140+
// by a destructor.
141+
result.kinds.append(&mut constraints.outlives);
142+
result.overflows.append(&mut constraints.overflows);
143+
144+
// If we have even one overflow, we should stop trying to evaluate further --
145+
// chances are, the subsequent overflows for this evaluation won't provide useful
146+
// information and will just decrease the speed at which we can emit these errors
147+
// (since we'll be printing for just that much longer for the often enormous types
148+
// that result here).
149+
if !result.overflows.is_empty() {
150+
break;
151+
}
152+
153+
// dtorck types are "types that will get dropped but which
154+
// do not themselves define a destructor", more or less. We have
155+
// to push them onto the stack to be expanded.
156+
for ty in constraints.dtorck_types.drain(..) {
157+
let Normalized { value: ty, obligations } =
158+
ocx.infcx.at(&cause, param_env).query_normalize(ty)?;
159+
ocx.register_obligations(obligations);
160+
161+
debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
162+
163+
match ty.kind() {
164+
// All parameters live for the duration of the
165+
// function.
166+
ty::Param(..) => {}
167+
168+
// A projection that we couldn't resolve - it
169+
// might have a destructor.
170+
ty::Alias(..) => {
171+
result.kinds.push(ty.into());
172+
}
173+
174+
_ => {
175+
if ty_set.insert(ty) {
176+
ty_stack.push((ty, depth + 1));
177+
}
178+
}
179+
}
180+
}
181+
}
182+
183+
debug!("dropck_outlives: result = {:#?}", result);
184+
Ok(result)
185+
}
186+
187+
/// Returns a set of constraints that needs to be satisfied in
188+
/// order for `ty` to be valid for destruction.
189+
pub fn dtorck_constraint_for_ty_inner<'tcx>(
190+
tcx: TyCtxt<'tcx>,
191+
span: Span,
192+
for_ty: Ty<'tcx>,
193+
depth: usize,
194+
ty: Ty<'tcx>,
195+
constraints: &mut DropckConstraint<'tcx>,
196+
) -> Result<(), NoSolution> {
197+
debug!("dtorck_constraint_for_ty_inner({:?}, {:?}, {:?}, {:?})", span, for_ty, depth, ty);
198+
199+
if !tcx.recursion_limit().value_within_limit(depth) {
200+
constraints.overflows.push(ty);
201+
return Ok(());
202+
}
203+
204+
if trivial_dropck_outlives(tcx, ty) {
205+
return Ok(());
206+
}
207+
208+
match ty.kind() {
209+
ty::Bool
210+
| ty::Char
211+
| ty::Int(_)
212+
| ty::Uint(_)
213+
| ty::Float(_)
214+
| ty::Str
215+
| ty::Never
216+
| ty::Foreign(..)
217+
| ty::RawPtr(..)
218+
| ty::Ref(..)
219+
| ty::FnDef(..)
220+
| ty::FnPtr(_)
221+
| ty::GeneratorWitness(..)
222+
| ty::GeneratorWitnessMIR(..) => {
223+
// these types never have a destructor
224+
}
225+
226+
ty::Array(ety, _) | ty::Slice(ety) => {
227+
// single-element containers, behave like their element
228+
rustc_data_structures::stack::ensure_sufficient_stack(|| {
229+
dtorck_constraint_for_ty_inner(tcx, span, for_ty, depth + 1, *ety, constraints)
230+
})?;
231+
}
232+
233+
ty::Tuple(tys) => rustc_data_structures::stack::ensure_sufficient_stack(|| {
234+
for ty in tys.iter() {
235+
dtorck_constraint_for_ty_inner(tcx, span, for_ty, depth + 1, ty, constraints)?;
236+
}
237+
Ok::<_, NoSolution>(())
238+
})?,
239+
240+
ty::Closure(_, substs) => {
241+
if !substs.as_closure().is_valid() {
242+
// By the time this code runs, all type variables ought to
243+
// be fully resolved.
244+
245+
tcx.sess.delay_span_bug(
246+
span,
247+
format!("upvar_tys for closure not found. Expected capture information for closure {ty}",),
248+
);
249+
return Err(NoSolution);
250+
}
251+
252+
rustc_data_structures::stack::ensure_sufficient_stack(|| {
253+
for ty in substs.as_closure().upvar_tys() {
254+
dtorck_constraint_for_ty_inner(tcx, span, for_ty, depth + 1, ty, constraints)?;
255+
}
256+
Ok::<_, NoSolution>(())
257+
})?
258+
}
259+
260+
ty::Generator(_, substs, _movability) => {
261+
// rust-lang/rust#49918: types can be constructed, stored
262+
// in the interior, and sit idle when generator yields
263+
// (and is subsequently dropped).
264+
//
265+
// It would be nice to descend into interior of a
266+
// generator to determine what effects dropping it might
267+
// have (by looking at any drop effects associated with
268+
// its interior).
269+
//
270+
// However, the interior's representation uses things like
271+
// GeneratorWitness that explicitly assume they are not
272+
// traversed in such a manner. So instead, we will
273+
// simplify things for now by treating all generators as
274+
// if they were like trait objects, where its upvars must
275+
// all be alive for the generator's (potential)
276+
// destructor.
277+
//
278+
// In particular, skipping over `_interior` is safe
279+
// because any side-effects from dropping `_interior` can
280+
// only take place through references with lifetimes
281+
// derived from lifetimes attached to the upvars and resume
282+
// argument, and we *do* incorporate those here.
283+
284+
if !substs.as_generator().is_valid() {
285+
// By the time this code runs, all type variables ought to
286+
// be fully resolved.
287+
tcx.sess.delay_span_bug(
288+
span,
289+
format!("upvar_tys for generator not found. Expected capture information for generator {ty}",),
290+
);
291+
return Err(NoSolution);
292+
}
293+
294+
constraints.outlives.extend(
295+
substs
296+
.as_generator()
297+
.upvar_tys()
298+
.map(|t| -> ty::subst::GenericArg<'tcx> { t.into() }),
299+
);
300+
constraints.outlives.push(substs.as_generator().resume_ty().into());
301+
}
302+
303+
ty::Adt(def, substs) => {
304+
let DropckConstraint { dtorck_types, outlives, overflows } =
305+
tcx.at(span).adt_dtorck_constraint(def.did())?;
306+
// FIXME: we can try to recursively `dtorck_constraint_on_ty`
307+
// there, but that needs some way to handle cycles.
308+
constraints
309+
.dtorck_types
310+
.extend(dtorck_types.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
311+
constraints
312+
.outlives
313+
.extend(outlives.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
314+
constraints
315+
.overflows
316+
.extend(overflows.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
317+
}
318+
319+
// Objects must be alive in order for their destructor
320+
// to be called.
321+
ty::Dynamic(..) => {
322+
constraints.outlives.push(ty.into());
323+
}
324+
325+
// Types that can't be resolved. Pass them forward.
326+
ty::Alias(..) | ty::Param(..) => {
327+
constraints.dtorck_types.push(ty);
328+
}
329+
330+
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => {
331+
// By the time this code runs, all type variables ought to
332+
// be fully resolved.
333+
return Err(NoSolution);
334+
}
335+
}
336+
337+
Ok(())
338+
}

0 commit comments

Comments
 (0)