1//! The bulk of the logic for implementing `-Zassumptions-on-binders`
23use derive_where::derive_where;
4use indexmap::IndexSet;
5#[cfg(feature = "nightly")]
6use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher};
7#[cfg(feature = "nightly")]
8use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder};
9use tracing::{debug, instrument};
1011// Workaround for TransitiveRelation being in rustc_data_structures which isn't accessible on stable
12#[cfg(not(feature = "nightly"))]
13#[derive(Default, Clone, Debug)]
14pub struct TransitiveRelation<T>(T);
15#[cfg(not(feature = "nightly"))]
16impl<T> TransitiveRelation<T> {
17pub fn reachable_from(&self, _data: T) -> Vec<T> {
18unreachable!("-Zassumptions-on-binders is not supported for r-a")
19 }
2021pub fn base_edges(&self) -> impl Iterator<Item = (T, T)> {
22unreachable!("-Zassumptions-on-binders is not supported for r-a");
2324#[allow(unreachable_code)]
25[].into_iter()
26 }
27}
28#[derive(Clone, Debug)]
29#[cfg(not(feature = "nightly"))]
30pub struct TransitiveRelationBuilder<T>(T);
31#[cfg(not(feature = "nightly"))]
32impl<T> TransitiveRelationBuilder<T> {
33pub fn freeze(self) -> TransitiveRelation<T> {
34unreachable!("-Zassumptions-on-binders is not supported for r-a")
35 }
3637pub fn add(&mut self, _: T, _: T) {
38unreachable!("-Zassumptions-on-binders is not supported for r-a")
39 }
40}
41#[cfg(not(feature = "nightly"))]
42impl<T> Default for TransitiveRelationBuilder<T> {
43fn default() -> Self {
44unreachable!("-Zassumptions-on-binders is not supported for r-a")
45 }
46}
4748use crate::data_structures::IndexMap;
49use crate::fold::TypeSuperFoldable;
50use crate::inherent::*;
51use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo};
52use crate::{
53AliasTy, Binder, BoundRegion, BoundVar, BoundVariableKind, DebruijnIndex, FallibleTypeFolder,
54InferCtxtLike, Interner, IsRigid, OutlivesPredicate, RegionKind, TyKind, TypeFoldable,
55TypeFolder, TypeVisitable, TypeVisitor, TypingMode, UniverseIndex, Variance, VisitorResult,
56max_universe, set_aliases_to_non_rigid,
57};
5859#[automatically_derived]
impl<I: Interner> ::core::fmt::Debug for Assumptions<I> where I: Interner {
fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>)
-> ::core::fmt::Result {
match self {
Assumptions {
type_outlives: ref __field_type_outlives,
region_outlives: ref __field_region_outlives,
inverse_region_outlives: ref __field_inverse_region_outlives }
=> {
let mut __builder =
::core::fmt::Formatter::debug_struct(__f, "Assumptions");
::core::fmt::DebugStruct::field(&mut __builder,
"type_outlives", __field_type_outlives);
::core::fmt::DebugStruct::field(&mut __builder,
"region_outlives", __field_region_outlives);
::core::fmt::DebugStruct::field(&mut __builder,
"inverse_region_outlives", __field_inverse_region_outlives);
::core::fmt::DebugStruct::finish(&mut __builder)
}
}
}
}#[derive_where(Clone, Debug; I: Interner)]60pub struct Assumptions<I: Interner> {
61pub type_outlives: Vec<Binder<I, OutlivesPredicate<I, I::Ty>>>,
62pub region_outlives: TransitiveRelation<I::Region>,
63pub inverse_region_outlives: TransitiveRelation<I::Region>,
64}
6566impl<I: Interner> Assumptions<I> {
67pub fn empty() -> Self {
68Self {
69 type_outlives: Vec::new(),
70 region_outlives: TransitiveRelationBuilder::default().freeze(),
71 inverse_region_outlives: TransitiveRelationBuilder::default().freeze(),
72 }
73 }
7475pub fn new(
76 type_outlives: Vec<Binder<I, OutlivesPredicate<I, I::Ty>>>,
77 region_outlives: TransitiveRelation<I::Region>,
78 ) -> Self {
79Self {
80 inverse_region_outlives: {
81let mut builder = TransitiveRelationBuilder::default();
82for (r1, r2) in region_outlives.base_edges() {
83 builder.add(r2, r1);
84 }
85builder.freeze()
86 },
87type_outlives,
88region_outlives,
89 }
90 }
91}
9293#[automatically_derived]
impl<I: Interner> ::core::fmt::Debug for RegionConstraint<I> where I: Interner
{
fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>)
-> ::core::fmt::Result {
match self {
RegionConstraint::Ambiguity =>
::core::fmt::Formatter::write_str(__f, "Ambiguity"),
RegionConstraint::RegionOutlives(ref __field_0, ref __field_1) =>
{
let mut __builder =
::core::fmt::Formatter::debug_tuple(__f, "RegionOutlives");
::core::fmt::DebugTuple::field(&mut __builder, __field_0);
::core::fmt::DebugTuple::field(&mut __builder, __field_1);
::core::fmt::DebugTuple::finish(&mut __builder)
}
RegionConstraint::AliasTyOutlivesViaEnv(ref __field_0) => {
let mut __builder =
::core::fmt::Formatter::debug_tuple(__f,
"AliasTyOutlivesViaEnv");
::core::fmt::DebugTuple::field(&mut __builder, __field_0);
::core::fmt::DebugTuple::finish(&mut __builder)
}
RegionConstraint::PlaceholderTyOutlives(ref __field_0,
ref __field_1) => {
let mut __builder =
::core::fmt::Formatter::debug_tuple(__f,
"PlaceholderTyOutlives");
::core::fmt::DebugTuple::field(&mut __builder, __field_0);
::core::fmt::DebugTuple::field(&mut __builder, __field_1);
::core::fmt::DebugTuple::finish(&mut __builder)
}
RegionConstraint::And(ref __field_0) => {
let mut __builder =
::core::fmt::Formatter::debug_tuple(__f, "And");
::core::fmt::DebugTuple::field(&mut __builder, __field_0);
::core::fmt::DebugTuple::finish(&mut __builder)
}
RegionConstraint::Or(ref __field_0) => {
let mut __builder =
::core::fmt::Formatter::debug_tuple(__f, "Or");
::core::fmt::DebugTuple::field(&mut __builder, __field_0);
::core::fmt::DebugTuple::finish(&mut __builder)
}
}
}
}#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner)]94pub enum RegionConstraint<I: Interner> {
95 Ambiguity,
96 RegionOutlives(I::Region, I::Region),
97/// Requirement that a (potentially higher ranked) alias outlives some (potentially higher ranked)
98 /// region due to an assumption in the environment. This cannot be satisfied via component outlives
99 /// or item bounds.
100 ///
101 /// We cannot eagerly look at assumptions as we are usually working with an incomplete set of assumptions
102 /// and there may wind up being assumptions we can use to prove this when we're in a smaller universe.
103 ///
104 /// We eagerly destructure alias outlives requirements into region outlives requirements corresponding to
105 /// component outlives & item bound outlives rules, leaving only param env candidates.
106AliasTyOutlivesViaEnv(Binder<I, (AliasTy<I>, I::Region)>),
107/// This is an `I::Ty` for two reasons:
108 /// 1. We need the type visitable impl to be able to `visit_ty` on this so canonicalization
109 /// knows about the placeholder
110 /// 2. When exiting the trait solver there may be placeholder outlives corresponding to params
111 /// from the root universe. These need to be changed from a `Placeholder` to the original
112 /// `Param`.
113 ///
114 /// We cannot eagerly look at assumptions as we are usually working with an incomplete set of assumptions
115 /// and there may wind up being assumptions we can use to prove this when we're in a smaller universe.
116PlaceholderTyOutlives(I::Ty, I::Region),
117118 And(Box<[RegionConstraint<I>]>),
119 Or(Box<[RegionConstraint<I>]>),
120}
121122// This is not a derived impl because a perfect derive leads to inductive
123// cycle causing the trait to never actually be implemented
124#[cfg(feature = "nightly")]
125impl<I: Interner> StableHashfor RegionConstraint<I>
126where
127I::Region: StableHash,
128 I::Ty: StableHash,
129 I::GenericArgs: StableHash,
130 I::TraitAssocTyId: StableHash,
131 I::InherentAssocTyId: StableHash,
132 I::OpaqueTyId: StableHash,
133 I::FreeTyAliasId: StableHash,
134 I::BoundVarKinds: StableHash,
135{
136#[inline]
137fn stable_hash<CTX: StableHashCtxt>(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
138use RegionConstraint::*;
139140 std::mem::discriminant(self).stable_hash(hcx, hasher);
141match self {
142Ambiguity => (),
143RegionOutlives(a, b) => {
144a.stable_hash(hcx, hasher);
145b.stable_hash(hcx, hasher);
146 }
147AliasTyOutlivesViaEnv(outlives) => {
148outlives.stable_hash(hcx, hasher);
149 }
150PlaceholderTyOutlives(a, b) => {
151a.stable_hash(hcx, hasher);
152b.stable_hash(hcx, hasher);
153 }
154And(and) => {
155for a in and.iter() {
156 a.stable_hash(hcx, hasher);
157 }
158 }
159Or(or) => {
160for a in or.iter() {
161 a.stable_hash(hcx, hasher);
162 }
163 }
164 }
165 }
166}
167168impl<I: Interner> TypeFoldable<I> for RegionConstraint<I> {
169fn try_fold_with<F: FallibleTypeFolder<I>>(self, f: &mut F) -> Result<Self, F::Error> {
170use RegionConstraint::*;
171Ok(match self {
172Ambiguity => self,
173RegionOutlives(a, b) => RegionOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?),
174AliasTyOutlivesViaEnv(outlives) => AliasTyOutlivesViaEnv(outlives.try_fold_with(f)?),
175PlaceholderTyOutlives(a, b) => {
176PlaceholderTyOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?)
177 }
178And(and) => {
179let mut new_and = Vec::new();
180for a in and {
181 new_and.push(a.try_fold_with(f)?);
182 }
183And(new_and.into_boxed_slice())
184 }
185Or(or) => {
186let mut new_or = Vec::new();
187for a in or {
188 new_or.push(a.try_fold_with(f)?);
189 }
190Or(new_or.into_boxed_slice())
191 }
192 })
193 }
194195fn fold_with<F: TypeFolder<I>>(self, f: &mut F) -> Self {
196use RegionConstraint::*;
197match self {
198Ambiguity => self,
199RegionOutlives(a, b) => RegionOutlives(a.fold_with(f), b.fold_with(f)),
200AliasTyOutlivesViaEnv(outlives) => AliasTyOutlivesViaEnv(outlives.fold_with(f)),
201PlaceholderTyOutlives(a, b) => PlaceholderTyOutlives(a.fold_with(f), b.fold_with(f)),
202And(and) => {
203let mut new_and = Vec::new();
204for a in and {
205 new_and.push(a.fold_with(f));
206 }
207And(new_and.into_boxed_slice())
208 }
209Or(or) => {
210let mut new_or = Vec::new();
211for a in or {
212 new_or.push(a.fold_with(f));
213 }
214Or(new_or.into_boxed_slice())
215 }
216 }
217 }
218}
219220impl<I: Interner> TypeVisitable<I> for RegionConstraint<I> {
221fn visit_with<F: TypeVisitor<I>>(&self, f: &mut F) -> F::Result {
222use core::ops::ControlFlow::*;
223224use RegionConstraint::*;
225226match self {
227Ambiguity => (),
228RegionOutlives(a, b) => {
229if let b @ Break(_) = a.visit_with(f).branch() {
230return F::Result::from_branch(b);
231 };
232if let b @ Break(_) = b.visit_with(f).branch() {
233return F::Result::from_branch(b);
234 };
235 }
236AliasTyOutlivesViaEnv(outlives) => {
237return outlives.visit_with(f);
238 }
239PlaceholderTyOutlives(a, b) => {
240if let b @ Break(_) = a.visit_with(f).branch() {
241return F::Result::from_branch(b);
242 };
243if let b @ Break(_) = b.visit_with(f).branch() {
244return F::Result::from_branch(b);
245 };
246 }
247And(and) => {
248for a in and {
249if let b @ Break(_) = a.visit_with(f).branch() {
250return F::Result::from_branch(b);
251 };
252 }
253 }
254Or(or) => {
255for a in or {
256if let b @ Break(_) = a.visit_with(f).branch() {
257return F::Result::from_branch(b);
258 };
259 }
260 }
261 };
262263 F::Result::output()
264 }
265}
266267impl<I: Interner> Defaultfor RegionConstraint<I> {
268fn default() -> Self {
269Self::new_true()
270 }
271}
272273impl<I: Interner> RegionConstraint<I> {
274pub fn new_true() -> Self {
275 RegionConstraint::And(Box::new([]))
276 }
277278pub fn is_true(&self) -> bool {
279match self {
280Self::And(and) => and.is_empty(),
281_ => false,
282 }
283 }
284285pub fn new_false() -> Self {
286 RegionConstraint::Or(Box::new([]))
287 }
288289pub fn is_false(&self) -> bool {
290match self {
291Self::Or(or) => or.is_empty(),
292_ => false,
293 }
294 }
295296pub fn is_or(&self) -> bool {
297#[allow(non_exhaustive_omitted_patterns)] match self {
Self::Or(_) => true,
_ => false,
}matches!(self, Self::Or(_))298 }
299300pub fn unwrap_or(self) -> Box<[RegionConstraint<I>]> {
301match self {
302Self::Or(ors) => ors,
303_ => {
::core::panicking::panic_fmt(format_args!("`unwrap_or` on non-Or: {0:?}",
self));
}panic!("`unwrap_or` on non-Or: {self:?}"),
304 }
305 }
306307pub fn unwrap_and(self) -> Box<[RegionConstraint<I>]> {
308match self {
309Self::And(ands) => ands,
310_ => {
::core::panicking::panic_fmt(format_args!("`unwrap_and` on non-And: {0:?}",
self));
}panic!("`unwrap_and` on non-And: {self:?}"),
311 }
312 }
313314pub fn is_and(&self) -> bool {
315#[allow(non_exhaustive_omitted_patterns)] match self {
Self::And(_) => true,
_ => false,
}matches!(self, Self::And(_))316 }
317318pub fn is_ambig(&self) -> bool {
319#[allow(non_exhaustive_omitted_patterns)] match self {
Self::Ambiguity => true,
_ => false,
}matches!(self, Self::Ambiguity)320 }
321322pub fn and(self, other: RegionConstraint<I>) -> RegionConstraint<I> {
323use RegionConstraint::*;
324325match (self, other) {
326 (And(a_ands), And(b_ands)) => And(a_ands327 .into_iter()
328 .chain(b_ands.into_iter())
329 .collect::<Vec<_>>()
330 .into_boxed_slice()),
331 (And(ands), other) | (other, And(ands)) => {
332And(ands.into_iter().chain([other]).collect::<Vec<_>>().into_boxed_slice())
333 }
334 (this, other) => And(Box::new([this, other])),
335 }
336 }
337338/// Converts the region constraint into an ORs of ANDs of "leaf" constraints. Where
339 /// a leaf constraint is a non-or/and constraint.
340x;#[instrument(level = "debug", ret)]341pub fn canonical_form(self) -> Self {
342use RegionConstraint::*;
343344fn permutations<I: Interner>(
345 ors: &[Vec<RegionConstraint<I>>],
346 ) -> Vec<Vec<RegionConstraint<I>>> {
347match ors {
348 [] => vec![vec![]],
349 [or1] => {
350let mut choices = vec![];
351for choice in or1 {
352 choices.push(vec![choice.clone()]);
353 }
354 choices
355 }
356 [or1, rest_ors @ ..] => {
357let mut choices = vec![];
358for choice in or1 {
359 choices.extend(permutations(rest_ors).into_iter().map(|mut and| {
360 and.push(choice.clone());
361 and
362 }));
363 }
364 choices
365 }
366 }
367 }
368369let canonical = match self {
370 And(ands) => {
371// AND of OR of AND of LEAFs
372 //
373 // We can turn `AND of OR of X` into `OR of AND of X` by enumerating every set of choices
374 // for the list of ORs. For example if we have `AND ( OR(A, B), OR(C, D) )` we can convert this into
375 // `OR ( AND (A, C), AND (A, D), AND (B, C), AND (B, D ))`
376 //
377 // if A/B/C/D are all in canonical forms then we wind up with an `OR of AND of AND of LEAFs` which
378 // is trivially canonicalizeable by flattening the multiple layers of AND into one.
379let ors = ands
380 .into_iter()
381 .map(|c| c.canonical_form().unwrap_or().to_vec())
382 .collect::<Vec<_>>();
383debug!(?ors);
384let or_permutations = permutations(&ors);
385debug!(?or_permutations);
386387 Or(or_permutations
388 .into_iter()
389 .map(|c| {
390 And(c
391 .into_iter()
392 .flat_map(|c2| c2.unwrap_and().into_iter())
393 .collect::<Vec<_>>()
394 .into_boxed_slice())
395 })
396 .collect::<Vec<_>>()
397 .into_boxed_slice())
398 }
399 Or(ors) => {
400// OR of OR of AND of LEAFs
401 //
402 // trivially canonicalizeable by concatenating all of the ORs into one big OR
403Or(ors
404 .into_iter()
405 .flat_map(|c| c.canonical_form().unwrap_or().into_iter())
406 .collect::<Vec<_>>()
407 .into_boxed_slice())
408 }
409_ => Or(Box::new([And(Box::new([self]))])),
410 };
411412assert!(
413 canonical.is_canonical_form(),
414"non canonical form region constraint: {:?}",
415 canonical
416 );
417 canonical
418 }
419420fn is_leaf_constraint(&self) -> bool {
421use RegionConstraint::*;
422match self {
423Ambiguity424 | RegionOutlives(..)
425 | AliasTyOutlivesViaEnv(..)
426 | PlaceholderTyOutlives(..) => true,
427And(..) | Or(..) => false,
428 }
429 }
430431fn is_canonical_and(&self) -> bool {
432if let Self::And(ands) = self { ands.iter().all(|c| c.is_leaf_constraint()) } else { false }
433 }
434435pub fn is_canonical_form(&self) -> bool {
436if let Self::Or(ors) = self { ors.iter().all(|c| c.is_canonical_and()) } else { false }
437 }
438}
439440/// Takes any constraints involving placeholders from the current universe and eagerly checks them.
441/// This can be done a few ways:
442/// - There's an assumption on the binder introducing the placeholder which means the constraint is satisfied (true)
443/// - There's assumptions on the binder introducing the placeholder which allow us to rewrite the constraint in
444/// terms of lower universe variables. For example given `for<'a> where('b: 'a) { prove(T: '!a_u1) }` we can
445/// convert this constraint to `T: 'b` which no longer references anything from `u1`.
446/// - There are no relevant assumptions so we can neither rewrite the constraint nor consider it satisfied (false)
447/// - We failed to compute the full set of assumptions when entering the binder corresponding to `u`. (ambiguity)
448///
449/// After handling all of the region constraints in `u` we then evaluate the entire constraint as much as possible,
450/// propagating true/false/ambiguity as close to the root of the constraint as we can. The returned constraint should
451/// be checked for whether it is true/false/ambiguous as that should affect the result of whatever operation required
452/// entering the binder corresponding to `u`.
453x;#[instrument(level = "debug", skip(infcx), ret)]454pub fn eagerly_handle_placeholders_in_universe<Infcx: InferCtxtLike<Interner = I>, I: Interner>(
455 infcx: &Infcx,
456 constraint: RegionConstraint<I>,
457 u: UniverseIndex,
458) -> RegionConstraint<I> {
459use RegionConstraint::*;
460461let assumptions = infcx.get_placeholder_assumptions(u);
462463// 1. rewrite type outlives constraints involving things from `u` into either region constraints
464 // involving things from `u` or type outlives constraints not involving things from `u`
465 //
466 // IOW, we only want to encounter things from `u` as part of region out lives constraints.
467let constraint = rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling(
468 infcx,
469 constraint,
470 u,
471&assumptions,
472 );
473474// 2. rewrite the constraint into a canonical ORs of ANDs form
475let constraint = constraint.canonical_form();
476477// 3. compute transitive region outlives and get a new set of region outlives constraints by
478 // looking for every region which either a placeholder_u flows into it, or it flows into
479 // the placeholder.
480 //
481 // do this for each element in the top level OR
482let constraint = Or(constraint
483 .unwrap_or()
484 .into_iter()
485 .map(|c| {
486let and =
487 And(compute_new_region_constraints(infcx, &c.unwrap_and(), u).into_boxed_slice());
488489// 4. rewrite region outlives constraints (potentially to false/true)
490pull_region_outlives_constraints_out_of_universe(infcx, and, u, &assumptions)
491 })
492 .collect::<Vec<_>>()
493 .into_boxed_slice());
494495// 5. actually evaluate the constraint to eagerly error on false
496evaluate_solver_constraint(&constraint)
497}
498499/// Filter our region constraints to not include constraints between region variables from `u` and
500/// other regions as those are always satisfied. This requires some care to handle correctly for example:
501/// `'!a_u1: '?x_u1: '!b_u1` should result in us requiring `'!a_u1: '!b_u1` rather than dropping the two
502/// constraints entirely.
503///
504/// The only constraints involving things from `u` should be region outlives constraints at this point. Type
505/// outlives constraints should have been handled already either by destructuring into region outlives or by
506/// being rewritten in terms of smaller universe variables.
507x;#[instrument(level = "debug", skip(infcx), ret)]508fn compute_new_region_constraints<Infcx: InferCtxtLike<Interner = I>, I: Interner>(
509 infcx: &Infcx,
510 constraints: &[RegionConstraint<I>],
511 u: UniverseIndex,
512) -> Vec<RegionConstraint<I>> {
513use RegionConstraint::*;
514515let mut new_constraints = vec![];
516517let mut region_flows_builder = TransitiveRelationBuilder::default();
518let mut regions = IndexSet::new();
519for c in constraints {
520match c {
521 And(..) | Or(..) => unreachable!(),
522 Ambiguity | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => {
523 new_constraints.push(c.clone())
524 }
525 RegionOutlives(r1, r2) => {
526 regions.insert(r1);
527 regions.insert(r2);
528 region_flows_builder.add(r2, r1);
529 }
530 }
531 }
532533let region_flow = region_flows_builder.freeze();
534for r in regions.into_iter() {
535for ub in region_flow.reachable_from(r) {
536// we want to retain any region constraints between two "placeholder-likes" where for our
537 // purposes a placeholder-like is either a placeholder or variable in a lower universe
538let is_placeholder_like = |r: I::Region| match r.kind() {
539 RegionKind::ReLateParam(..)
540 | RegionKind::ReEarlyParam(..)
541 | RegionKind::RePlaceholder(..)
542 | RegionKind::ReStatic => true,
543 RegionKind::ReVar(..) => max_universe(infcx, r) < u,
544 RegionKind::ReError(..) => false,
545 RegionKind::ReErased | RegionKind::ReBound(..) => unreachable!(),
546 };
547548if is_placeholder_like(*r) && is_placeholder_like(*ub) {
549 new_constraints.push(RegionOutlives(*ub, *r));
550 }
551 }
552 }
553554 new_constraints
555}
556557/// Evaluate ANDs and ORs to true/false/ambiguous based on whether their arguments are true/false/ambiguous
558x;#[instrument(level = "debug", ret)]559pub fn evaluate_solver_constraint<I: Interner>(
560 constraint: &RegionConstraint<I>,
561) -> RegionConstraint<I> {
562use RegionConstraint::*;
563match constraint {
564 Ambiguity | RegionOutlives(..) | AliasTyOutlivesViaEnv(..) | PlaceholderTyOutlives(..) => {
565 constraint.clone()
566 }
567 And(and) => {
568let mut and_constraints = Vec::new();
569let mut is_ambiguous_constraint = false;
570for c in and.iter() {
571let evaluated_constraint = evaluate_solver_constraint(c);
572if evaluated_constraint.is_true() {
573// - do nothing
574} else if evaluated_constraint.is_false() {
575return RegionConstraint::new_false();
576 } else if evaluated_constraint.is_ambig() {
577 is_ambiguous_constraint = true;
578 } else {
579 and_constraints.push(evaluated_constraint);
580 }
581 }
582583if is_ambiguous_constraint {
584 RegionConstraint::Ambiguity
585 } else {
586 RegionConstraint::And(and_constraints.into_boxed_slice())
587 }
588 }
589 Or(or) => {
590let mut or_constraints = Vec::new();
591let mut is_ambiguous_constraint = false;
592for c in or.iter() {
593let evaluated_constraint = evaluate_solver_constraint(c);
594if evaluated_constraint.is_false() {
595// do nothing
596} else if evaluated_constraint.is_true() {
597return RegionConstraint::new_true();
598 } else if evaluated_constraint.is_ambig() {
599 is_ambiguous_constraint = true;
600 } else {
601 or_constraints.push(evaluated_constraint);
602 }
603 }
604605if is_ambiguous_constraint {
606 RegionConstraint::Ambiguity
607 } else {
608 RegionConstraint::Or(or_constraints.into_boxed_slice())
609 }
610 }
611 }
612}
613614/// Handles converting region outlives constraints involving placeholders from `u` into OR constraints
615/// involving regions from smaller universes with known relationships to the placeholder. For example:
616/// ```ignore (not rust)
617/// for<'a, 'b> where(
618/// 'c: 'b, 'd: 'b,
619/// 'a: 'e, 'a: 'f,
620/// ) {
621/// 'a_u1: 'b_u1
622/// }
623/// ```
624/// will get converted to:
625/// ```ignore (not rust)
626/// OR(
627/// 'e: 'c,
628/// 'e: 'd,
629/// 'f: 'c,
630/// 'f: 'd,
631/// )
632/// ```
633/// if we are handling constraints in `u1`.
634x;#[instrument(level = "debug", skip(infcx), ret)]635fn pull_region_outlives_constraints_out_of_universe<
636 Infcx: InferCtxtLike<Interner = I>,
637 I: Interner,
638>(
639 infcx: &Infcx,
640 constraint: RegionConstraint<I>,
641 u: UniverseIndex,
642 assumptions: &Option<Assumptions<I>>,
643) -> RegionConstraint<I> {
644assert!(max_universe(infcx, constraint.clone()) <= u);
645646// FIXME(-Zassumptions-on-binders): we don't lower universes of region variables when exiting `u`
647 // this seems dubious/potentially wrong? we can't just blindly do this though as if we had something
648 // like `!T_u -> ?x_u -> !U_u` then lowering `?x` to `u-1` when exiting `u` would be wrong.
649 //
650 // I'm not even sure this would be necessary given we filter out region constraints involving regions#
651 // from the current universe and only retain those between placeholders.
652653use RegionConstraint::*;
654match constraint {
655 Ambiguity | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => {
656assert!(max_universe(infcx, constraint.clone()) < u);
657 constraint
658 }
659 RegionOutlives(region_1, region_2) => {
660let region_1_u = max_universe(infcx, region_1);
661let region_2_u = max_universe(infcx, region_2);
662663if region_1_u != u && region_2_u != u {
664return constraint;
665 }
666667let assumptions = match assumptions {
668Some(assumptions) => assumptions,
669None => return RegionConstraint::Ambiguity,
670 };
671672let mut candidates = vec![];
673for ub in
674regions_outlived_by(region_1, assumptions).filter(|r| max_universe(infcx, *r) < u)
675 {
676// FIXME(-Zassumptions-on-binders): if `region_2` is in a smaller universe there'll be both
677 // `'region_2` and `'static` as lower bounds which seems... unfortunate and may cause us to
678 // add a bunch of duplicate `'ub: 'static` candidates the more binders we leave.
679for lb in regions_outliving(region_2, assumptions, infcx.cx())
680 .filter(|r| max_universe(infcx, *r) < u)
681 {
682// As long as any region outlived by `region_1` outlives any region region which
683 // `region_2` outlives, we know that `region_1: region_2` holds. In other words,
684 // there exists some set of 4 regions for which `'r1: 'i1` `'i1: 'i2` `'i2: 'r2`
685candidates.push(RegionOutlives(ub, lb));
686 }
687 }
688689 RegionConstraint::Or(candidates.into_boxed_slice())
690 }
691 And(constraints) => And(constraints
692 .into_iter()
693 .map(|constraint| {
694 pull_region_outlives_constraints_out_of_universe(infcx, constraint, u, assumptions)
695 })
696 .collect()),
697 Or(_) => unreachable!(),
698 }
699}
700701/// Converts type outlives constraints into region outlives constraints. This assumes the *complete* set of
702/// assumptions are known. This should not be called until the end of type checking.
703///
704/// The returned region constraint will not have *any* PlaceholderTyOutlives or AliasTyOutlivesViaEnv constraints.
705pub fn destructure_type_outlives_constraints_in_root<
706 Infcx: InferCtxtLike<Interner = I>,
707 I: Interner,
708>(
709 infcx: &Infcx,
710 constraint: RegionConstraint<I>,
711 assumptions: &Assumptions<I>,
712) -> RegionConstraint<I> {
713use RegionConstraint::*;
714715match constraint {
716Ambiguity | RegionOutlives(..) => constraint,
717PlaceholderTyOutlives(ty, r) => {
718Or(regions_outlived_by_placeholder(ty, assumptions, infcx.cx())
719 .map(move |assumption_r| RegionOutlives(assumption_r, r))
720 .collect::<Vec<_>>()
721 .into_boxed_slice())
722 }
723AliasTyOutlivesViaEnv(bound_outlives) => {
724alias_outlives_candidates_from_assumptions(infcx, bound_outlives, assumptions)
725 }
726And(constraints) => And(constraints727 .into_iter()
728 .map(|constraint| {
729destructure_type_outlives_constraints_in_root(infcx, constraint, assumptions)
730 })
731 .collect()),
732Or(constraints) => Or(constraints733 .into_iter()
734 .map(|constraint| {
735destructure_type_outlives_constraints_in_root(infcx, constraint, assumptions)
736 })
737 .collect()),
738 }
739}
740741/// Converts type outlives constraints into either region outlives constraints, or type outlives
742/// constraints which do not contain anything from `u`.
743///
744/// This only works off assumptions associated with the binder corresponding to `u` both for
745/// perf reasons and because the full set of region assumptions is not known during type checking
746/// due to closure signature inference.
747///
748/// This only really causes problems for higher-ranked outlives assumptions, for example if we have
749/// `where for<'a> <T as Trait<'a>>::Assoc: 'b` then we can't use that to prove `<T as Trait<'!c>>::Assoc: 'b`
750/// until we are in the root context. See comments inside this function for more detail.
751x;#[instrument(level = "debug", skip(infcx), ret)]752fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling<
753 Infcx: InferCtxtLike<Interner = I>,
754 I: Interner,
755>(
756 infcx: &Infcx,
757 constraint: RegionConstraint<I>,
758 u: UniverseIndex,
759 assumptions: &Option<Assumptions<I>>,
760) -> RegionConstraint<I> {
761assert!(
762 max_universe(infcx, constraint.clone()) <= u,
763"constraint {:?} contains terms from a larger universe than {:?}",
764 constraint.clone(),
765 u
766 );
767768use RegionConstraint::*;
769match constraint {
770 Ambiguity | RegionOutlives(..) => constraint,
771 PlaceholderTyOutlives(ty, region) => {
772let ty_u = max_universe(infcx, ty);
773let region_u = max_universe(infcx, region);
774775if region_u != u && ty_u != u {
776return constraint;
777 }
778779let assumptions = match assumptions {
780Some(assumptions) => assumptions,
781None => return Ambiguity,
782 };
783784let mut candidates = vec![];
785786// There could be `!T: 'region` assumptions in the env even if `!T` is in a
787 // smaller universe
788candidates.extend(
789 regions_outlived_by_placeholder(ty, assumptions, infcx.cx())
790 .map(move |assumption_r| RegionOutlives(assumption_r, region)),
791 );
792793// We can express `!T: 'region` as `!T: 'r` where `'r: 'region`. This is only necessary
794 // if the placeholder type is in a smaller universe as otherwise we know all regions which
795 // the placeholder outlives and can just destructure into an OR of RegionOutlives.
796if region_u == u && ty_u < u {
797 candidates.extend(
798 regions_outliving::<I>(region, assumptions, infcx.cx())
799 .filter(|r| max_universe(infcx, *r) < u)
800 .map(|r| PlaceholderTyOutlives(ty, r)),
801 );
802 }
803804 Or(candidates.into_boxed_slice())
805 }
806 AliasTyOutlivesViaEnv(bound_outlives) => {
807let mut candidates = Vec::new();
808809// given there can be higher ranked assumptions, e.g. `for<'a> <T as Trait<'a>>::Assoc: 'c`, that
810 // means that it's actually *always* possible for an alias outlive to be satisfied in the root universe
811 // which means there should *always* be atleast two candidates when destructuring alias outlives. The
812 // two candidates being component outlives and then a higher ranked alias outlives.
813 //
814 // we dont care about this for region outlives as `for<'a> 'a: 'b` can't exist as we don't elaborate
815 // higher ranked type outlives assumptions into higher ranked region outlives assumptions. similarly,
816 // we don't care about `for<'a> Foo<'a>: 'b` as we always destructure adts into their components and if
817 // we dont equivalently elaborate the assumption into assumptions on the adt's components we just drop the
818 // assumptions
819 //
820 // so actually only `for<'a, 'b> Alias<'a>: 'b` and `for<'a> T: 'a` are assumptions we actually need to
821 // handle.
822 //
823 // we don't care about this when rewriting in the root universe as we know the complete set of assumptions
824if max_universe(infcx, bound_outlives) == u {
825let mut replacer = PlaceholderReplacer {
826 cx: infcx.cx(),
827 existing_var_count: bound_outlives.bound_vars().len(),
828 bound_vars: IndexMap::default(),
829 universe: u,
830 current_index: DebruijnIndex::ZERO,
831 };
832let escaping_outlives = bound_outlives.skip_binder().fold_with(&mut replacer);
833let bound_vars = bound_outlives.bound_vars().iter().chain(
834 core::mem::take(&mut replacer.bound_vars)
835 .into_iter()
836 .map(|(_, bound_region)| BoundVariableKind::Region(bound_region.kind)),
837 );
838let bound_outlives = Binder::bind_with_vars(
839 escaping_outlives,
840 I::BoundVarKinds::from_vars(infcx.cx(), bound_vars),
841 );
842let candidate = RegionConstraint::AliasTyOutlivesViaEnv(bound_outlives);
843if max_universe(infcx, candidate.clone()) < u {
844 candidates.push(candidate);
845 } else {
846// `PlaceholderReplacer` only folds regions. A non-lifetime binder can leave
847 // a placeholder type in `u`, so this type-outlives constraint cannot be
848 // handled by the region-outlives-only eager placeholder machinery.
849candidates.push(Ambiguity);
850 }
851 }
852853let assumptions = match assumptions {
854Some(assumptions) => assumptions,
855None => {
856 candidates.push(Ambiguity);
857return Or(candidates.into_boxed_slice());
858 }
859 };
860861// Actually look at the assumptions and matching our higher ranked alias outlives goal
862 // against potentially higher ranked type outlives assumptions.
863candidates.push(alias_outlives_candidates_from_assumptions(
864 infcx,
865 bound_outlives,
866 assumptions,
867 ));
868869// we can rewrite `Alias_u1: 'u2` into `Or(Alias_u1: 'u1)`
870 // given a list of regions which outlive `'u2`
871 //
872 // we don't care about this when rewriting in the root universe as we know the complete set of assumptions
873let (escaping_alias, escaping_r) = bound_outlives.skip_binder();
874if max_universe(infcx, escaping_r) == u {
875let mut replacer = PlaceholderReplacer {
876 cx: infcx.cx(),
877 existing_var_count: bound_outlives.bound_vars().len(),
878 bound_vars: IndexMap::default(),
879 universe: u,
880 current_index: DebruijnIndex::ZERO,
881 };
882let escaping_alias = escaping_alias.fold_with(&mut replacer);
883let bound_vars = bound_outlives.bound_vars().iter().chain(
884 core::mem::take(&mut replacer.bound_vars)
885 .into_iter()
886 .map(|(_, bound_region)| BoundVariableKind::Region(bound_region.kind)),
887 );
888let bound_alias = Binder::bind_with_vars(
889 escaping_alias,
890 I::BoundVarKinds::from_vars(infcx.cx(), bound_vars),
891 );
892893// while we did skip the binder, bound vars aren't in any universe so
894 // this can't be an escaping bound var
895for r2 in regions_outliving(escaping_r, assumptions, infcx.cx())
896 .filter(|r2| max_universe(infcx, *r2) < u)
897 {
898let candidate =
899 AliasTyOutlivesViaEnv(bound_alias.map_bound(|alias| (alias, r2)));
900if max_universe(infcx, candidate.clone()) < u {
901 candidates.push(candidate);
902 } else {
903 candidates.push(Ambiguity);
904 }
905 }
906 }
907908// I'm not convinced our handling here is *complete* so for now
909 // let's be conservative and not let alias outlives' cause NoSolution
910 // in coherence
911match infcx.typing_mode_raw() {
912 TypingMode::Coherence => candidates.push(RegionConstraint::Ambiguity),
913 TypingMode::Typeck { .. }
914 | TypingMode::ErasedNotCoherence { .. }
915 | TypingMode::PostTypeckUntilBorrowck { .. }
916 | TypingMode::PostBorrowck { .. }
917 | TypingMode::PostAnalysis
918 | TypingMode::Codegen => (),
919 };
920921 RegionConstraint::Or(candidates.into_boxed_slice())
922 }
923 And(constraints) => And(constraints
924 .into_iter()
925 .map(|constraint| {
926 rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling(
927 infcx,
928 constraint,
929 u,
930 assumptions,
931 )
932 })
933 .collect()),
934 Or(constraints) => Or(constraints
935 .into_iter()
936 .map(|constraint| {
937 rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling(
938 infcx,
939 constraint,
940 u,
941 assumptions,
942 )
943 })
944 .collect()),
945 }
946}
947948/// Returns all regions `r2` for which `r: r2` is known to hold in
949/// the universe associated with `assumptions`
950pub fn regions_outlived_by<I: Interner>(
951 r: I::Region,
952 assumptions: &Assumptions<I>,
953) -> impl Iterator<Item = I::Region> {
954// FIXME(-Zassumptions-on-binders): do we need to be adding the reflexive edge here?
955assumptions.region_outlives.reachable_from(r).into_iter().chain([r])
956}
957958/// Returns all regions `r2` for which `r2: r` is known to hold in
959/// the universe associated with `assumptions`
960pub fn regions_outliving<I: Interner>(
961 r: I::Region,
962 assumptions: &Assumptions<I>,
963 cx: I,
964) -> impl Iterator<Item = I::Region> {
965assumptions966 .inverse_region_outlives
967 .reachable_from(r)
968 .into_iter()
969// FIXME(-Zassumptions-on-binders): 'static may have been an input region canonicalized to something else is that important?
970 // FIXME(-Zassumptions-on-binders): do we need to adding the reflexive edge here?
971.chain([r, I::Region::new_static(cx)])
972}
973974/// Returns all regions `r` for which `!t: r` is known to hold in
975/// the universe associated with `assumptions`
976pub fn regions_outlived_by_placeholder<I: Interner>(
977 t: I::Ty,
978 assumptions: &Assumptions<I>,
979 cx: I,
980) -> impl Iterator<Item = I::Region> {
981match t.kind() {
982 TyKind::Placeholder(..) | TyKind::Param(..) => (),
983_ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("non-placeholder in `regions_outlived_by_placeholder`: {0:?}",
t)));
}unreachable!("non-placeholder in `regions_outlived_by_placeholder`: {t:?}"),
984 }
985986assumptions.type_outlives.iter().flat_map(move |binder| match binder.no_bound_vars() {
987Some(OutlivesPredicate(ty, r)) => (ty == t).then_some(r),
988None => Some(I::Region::new_static(cx)),
989 })
990}
991992pub struct PlaceholderReplacer<I: Interner> {
993 cx: I,
994 existing_var_count: usize,
995 bound_vars: IndexMap<BoundVar, BoundRegion<I>>,
996 universe: UniverseIndex,
997 current_index: DebruijnIndex,
998}
9991000impl<I: Interner> TypeFolder<I> for PlaceholderReplacer<I> {
1001fn cx(&self) -> I {
1002self.cx
1003 }
10041005fn fold_region(&mut self, r: I::Region) -> I::Region {
1006match r.kind() {
1007 RegionKind::RePlaceholder(p) if p.universe == self.universe => {
1008let bound_vars_len = self.bound_vars.len();
1009let mapped_var = self.bound_vars.entry(p.bound.var).or_insert(BoundRegion {
1010 var: BoundVar::from_usize(self.existing_var_count + bound_vars_len),
1011 kind: p.bound.kind,
1012 });
1013 I::Region::new_bound(self.cx, self.current_index, *mapped_var)
1014 }
1015// FIXME(-Zassumptions-on-binders): We should be handling region variables here somehow
1016_ => r,
1017 }
1018 }
10191020fn fold_binder<T: TypeFoldable<I>>(&mut self, b: Binder<I, T>) -> Binder<I, T> {
1021self.current_index.shift_in(1);
1022let b = b.super_fold_with(self);
1023self.current_index.shift_out(1);
1024b1025 }
1026}
10271028/// Converts an `AliasTyOutlivesViaEnv` constraint into an OR of region outlives constraints by
1029/// matching the alias against any `Alias: 'a` assumptions. This is somewhat tricky as we have a
1030/// potentially higher ranked alias being equated with a potentially higher ranked assumption and
1031/// we don't handle it correctly right now (though it is a somewhat reasonable halfway step).
1032x;#[instrument(level = "debug", skip(infcx), ret)]1033fn alias_outlives_candidates_from_assumptions<Infcx: InferCtxtLike<Interner = I>, I: Interner>(
1034 infcx: &Infcx,
1035 bound_outlives: Binder<I, (AliasTy<I>, I::Region)>,
1036 assumptions: &Assumptions<I>,
1037) -> RegionConstraint<I> {
1038let mut candidates = Vec::new();
10391040let prev_universe = infcx.universe();
10411042 infcx.enter_forall_with_empty_assumptions(bound_outlives, |(alias, r)| {
1043for bound_type_outlives in assumptions.type_outlives.iter() {
1044let OutlivesPredicate(alias2, r2) =
1045 infcx.instantiate_binder_with_infer(*bound_type_outlives);
10461047let mut relation = HigherRankedAliasMatcher {
1048 infcx,
1049 region_constraints: vec![RegionConstraint::RegionOutlives(r2, r)],
1050 };
10511052// FIXME(#155345): Both sides should be rigid in the future.
1053 // Currently we can't guarantee that.
1054if let Ok(_) = relation.relate(
1055 alias.to_ty(infcx.cx(), IsRigid::No),
1056 set_aliases_to_non_rigid(infcx.cx(), alias2).skip_norm_wip(),
1057 ) {
1058 candidates
1059 .push(RegionConstraint::And(relation.region_constraints.into_boxed_slice()));
1060 }
1061 }
1062 });
10631064let constraint = RegionConstraint::Or(candidates.into_boxed_slice());
10651066let largest_universe = infcx.universe();
1067debug!(?prev_universe, ?largest_universe);
10681069 ((prev_universe.index() + 1)..=largest_universe.index())
1070 .map(|u| UniverseIndex::from_usize(u))
1071 .rev()
1072 .fold(constraint, |constraint, u| {
1073 eagerly_handle_placeholders_in_universe(infcx, constraint, u)
1074 })
1075}
10761077struct HigherRankedAliasMatcher<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> {
1078 infcx: &'a Infcx,
1079 region_constraints: Vec<RegionConstraint<I>>,
1080}
10811082impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeRelation<I>
1083for HigherRankedAliasMatcher<'a, Infcx, I>
1084{
1085fn cx(&self) -> I {
1086self.infcx.cx()
1087 }
10881089fn relate_ty_args(
1090&mut self,
1091 a_ty: I::Ty,
1092 _b_ty: I::Ty,
1093 _ty_def_id: I::DefId,
1094 a_args: I::GenericArgs,
1095 b_args: I::GenericArgs,
1096 _mk: impl FnOnce(I::GenericArgs) -> I::Ty,
1097 ) -> RelateResult<I, I::Ty> {
1098 rustc_type_ir::relate::relate_args_invariantly(self, a_args, b_args)?;
1099Ok(a_ty)
1100 }
11011102fn relate_with_variance<T: Relate<I>>(
1103&mut self,
1104 _variance: Variance,
1105 _info: VarianceDiagInfo<I>,
1106 a: T,
1107 b: T,
1108 ) -> RelateResult<I, T> {
1109// FIXME(-Zassumptions-on-binders): bivariance is important for opaque type args so
1110 // we should actually handle variance in some way here.
1111self.relate(a, b)
1112 }
11131114fn tys(&mut self, a: I::Ty, b: I::Ty) -> RelateResult<I, I::Ty> {
1115 rustc_type_ir::relate::structurally_relate_tys(self, a, b)
1116 }
11171118fn regions(&mut self, a: I::Region, b: I::Region) -> RelateResult<I, I::Region> {
1119if a != b {
1120self.region_constraints.push(RegionConstraint::RegionOutlives(a, b));
1121self.region_constraints.push(RegionConstraint::RegionOutlives(b, a));
1122 }
1123Ok(a)
1124 }
11251126fn consts(&mut self, a: I::Const, b: I::Const) -> RelateResult<I, I::Const> {
1127 rustc_type_ir::relate::structurally_relate_consts(self, a, b)
1128 }
11291130fn binders<T>(&mut self, a: Binder<I, T>, b: Binder<I, T>) -> RelateResult<I, Binder<I, T>>
1131where
1132T: Relate<I>,
1133 {
1134self.infcx.enter_forall_with_empty_assumptions(a, |a| {
1135let u = self.infcx.universe();
1136self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty()));
1137let b = self.infcx.instantiate_binder_with_infer(b);
1138self.relate(a, b)
1139 })?;
11401141self.infcx.enter_forall_with_empty_assumptions(b, |b| {
1142let u = self.infcx.universe();
1143self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty()));
1144let a = self.infcx.instantiate_binder_with_infer(a);
1145self.relate(a, b)
1146 })?;
11471148Ok(a)
1149 }
1150}