Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 42 additions & 55 deletions compiler/rustc_type_ir/src/region_constraint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ use crate::data_structures::IndexMap;
use crate::fold::TypeSuperFoldable;
use crate::inherent::*;
use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo};
use crate::visit::TypeVisitableExt;
use crate::{
AliasTy, Binder, BoundRegion, BoundVar, BoundVariableKind, ClauseKind, DebruijnIndex,
InferCtxtLike, Interner, IsRigid, OutlivesClause, Region, RegionKind, TyKind, TypeFoldable,
Expand Down Expand Up @@ -861,6 +862,7 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling<
RegionConstraint::new_from_or(Or::build_and(and_constraint, or_constraint))
}

#[instrument(level = "debug", skip(infcx), ret)]
fn rewrite_placeholder_ty_outlives_constraints_in_universe_for_eager_placeholder_handling<
Infcx: InferCtxtLike<Interner = I>,
I: Interner,
Expand Down Expand Up @@ -903,6 +905,7 @@ fn rewrite_placeholder_ty_outlives_constraints_in_universe_for_eager_placeholder
Or::new(candidates.into_iter().map(|c| And::new([c])))
}

#[instrument(level = "debug", skip(infcx), ret)]
fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handling<
Infcx: InferCtxtLike<Interner = I>,
I: Interner,
Expand All @@ -914,12 +917,15 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl
) -> Or<I> {
use LeafRegionConstraint::*;

if max_universe(infcx, bound_outlives) != u {
return Or::new_leaf(AliasTyOutlivesViaEnv(bound_outlives, ()));
}

let mut candidates = Vec::new();

// given there can be higher ranked assumptions, e.g. `for<'a> <T as Trait<'a>>::Assoc: 'c`, that
// means that it's actually *always* possible for an alias outlive to be satisfied in the root universe
// which means there should *always* be atleast two candidates when destructuring alias outlives. The
// two candidates being component outlives and then a higher ranked alias outlives.
// which means there should *always* be atleast one candidate when destructuring alias outlives.
//
// we dont care about this for region outlives as `for<'a> 'a: 'b` can't exist as we don't elaborate
// higher ranked type outlives assumptions into higher ranked region outlives assumptions. similarly,
Expand All @@ -929,9 +935,9 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl
//
// so actually only `for<'a, 'b> Alias<'a>: 'b` and `for<'a> T: 'a` are assumptions we actually need to
// handle.
//
// we don't care about this when rewriting in the root universe as we know the complete set of assumptions
if max_universe(infcx, bound_outlives) == u {
let higher_ranked_candidate_for_outlived_region = |region| {
let bound_outlives = bound_outlives.map_bound(|(alias, _)| (alias, region));

let mut replacer = PlaceholderReplacer {
cx: infcx.cx(),
existing_var_count: bound_outlives.bound_vars().len(),
Expand All @@ -949,61 +955,42 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl
escaping_outlives,
I::BoundVarKinds::from_vars(infcx.cx(), bound_vars),
);
let candidate = Or::new_leaf(AliasTyOutlivesViaEnv(bound_outlives, ()));
if max_universe(infcx, candidate.clone()) < u {
candidates.push(candidate);
} else {
// `PlaceholderReplacer` only folds regions. A non-lifetime binder can leave
// a placeholder type in `u`, so this type-outlives constraint cannot be
// handled by the region-outlives-only eager placeholder machinery.
candidates.push(Or::new_ambig(()));

// `PlaceholderReplacer` only folds regions. A non-lifetime binder can leave
// a placeholder type in `u`, so this type-outlives constraint cannot be
// handled by the region-outlives-only eager placeholder machinery.
if max_universe(infcx, bound_outlives) == u {
return Or::new_ambig(());
}
}

// Actually look at the assumptions and matching our higher ranked alias outlives goal
// against potentially higher ranked type outlives assumptions.
candidates.push(alias_outlives_candidates_from_assumptions(infcx, bound_outlives, assumptions));
debug!("higher ranked candidate: {bound_outlives:?}");
Or::new_leaf(AliasTyOutlivesViaEnv(bound_outlives, ()))
};

// we can rewrite `Alias_u1: 'u2` into `Or(Alias_u1: 'u1)`
// given a list of regions which outlive `'u2`
// We elaborate something like `Alias<'a_u2>: 'b_u2` into alias outlives involving each
// region which is known to outlive `'b_u2`. So e.g. if `'c_u2: 'b_u2` is known to hold
// would wind up looking at `Alias<'a_u2>: 'c_u2` as well as `Alias<'a_u2>: 'b_u2`.
//
// we don't care about this when rewriting in the root universe as we know the complete set of assumptions
let (escaping_alias, escaping_r) = bound_outlives.skip_binder();
if max_universe(infcx, escaping_r) == u {
let mut replacer = PlaceholderReplacer {
cx: infcx.cx(),
existing_var_count: bound_outlives.bound_vars().len(),
bound_vars: IndexMap::default(),
universe: u,
current_index: DebruijnIndex::ZERO,
};
let escaping_alias = escaping_alias.fold_with(&mut replacer);
let bound_vars = bound_outlives.bound_vars().iter().chain(
core::mem::take(&mut replacer.bound_vars)
.into_iter()
.map(|(_, bound_region)| BoundVariableKind::Region(bound_region.kind)),
);
let bound_alias = Binder::bind_with_vars(
escaping_alias,
I::BoundVarKinds::from_vars(infcx.cx(), bound_vars),
);
// This is important in two cases. First, if the outliving region is in a lower universe then
// we've successfully lowered the universe of one of the terms in our alias outlives constraint.
//
// Secondly, if it's a region in the current universe *and* it's a region in the components of the
// alias then we get a less general constraint after replacing with bound vars compared to if we
// had just replaced the initial region. See `assumptions_on_binders/alias_outlives_with_repeated_placeholder.rs`
let escaping_r = bound_outlives.skip_binder().1;
if max_universe(infcx, escaping_r) < u || escaping_r.has_escaping_bound_vars() {
// we don't know anything about lower universe regions, and we also don't bother
// looking at the assumptions on the `AliasTyOutlivesViaEnv` binder.
candidates.push(higher_ranked_candidate_for_outlived_region(escaping_r))
} else {
for r in regions_outliving(escaping_r, assumptions, infcx.cx()) {
candidates.push(higher_ranked_candidate_for_outlived_region(r));
}
};

// while we did skip the binder, bound vars aren't in any universe so
// this can't be an escaping bound var
candidates.push(Or::new(
regions_outliving(escaping_r, assumptions, infcx.cx())
.filter(|r2| max_universe(infcx, *r2) < u)
.map(|r2| {
let candidate =
AliasTyOutlivesViaEnv(bound_alias.map_bound(|alias| (alias, r2)), ());
if max_universe(infcx, candidate.clone()) < u {
And::new([candidate])
} else {
And::new([Ambiguity(())])
}
}),
));
}
// Actually look at the assumptions and matching our higher ranked alias outlives goal
// against potentially higher ranked type outlives assumptions.
candidates.push(alias_outlives_candidates_from_assumptions(infcx, bound_outlives, assumptions));

// I'm not convinced our handling here is *complete* so for now
// let's be conservative and not let alias outlives' cause NoSolution
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//@ check-pass
//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally

#![feature(test_binder_constraints, non_lifetime_binders)]
#![expect(incomplete_features)]

trait Trait<'a> {
type Assoc;
}

core::test_binder_constraints! {
impl<T>
where
for<'a> T: Trait<'a>,
for<'a> <T as Trait<'a>>::Assoc: 'a
{
forall<'a, 'b>
where
'a: 'b,
{
for<> <T as Trait<'a>>::Assoc: 'b
} expect {
or {
// this first candidate is the important one!
for<'a> <T as Trait<'a>>::Assoc: 'a,
// these won't wind up actually being used to prove the OR
for<'a, 'b> <T as Trait<'a>>::Assoc: 'b,
for<'a> <T as Trait<'a>>::Assoc: 'static,
}
}
}
}

fn main() {}
26 changes: 26 additions & 0 deletions tests/ui/assumptions_on_binders/lower_universe_alias_outlives.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//@ check-pass

// test that we don't incorrectly try and eagerly handle alias outlives' from too
// low a universe

#![feature(test_binder_constraints, generic_const_items)]

trait AliasHaver {
type Assoc;
}

core::test_binder_constraints! {
impl<T: AliasHaver>
where
T::Assoc: 'static,
{
forall<'b> {
for<> T::Assoc: 'static
} expect {
// previously this would just be `or {}` i.e. false
for<> T::Assoc: 'static
}
}
}

fn main() {}
Loading