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
62 changes: 42 additions & 20 deletions compiler/rustc_type_ir/src/region_constraint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ 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,
TypeFolder, TypingMode, UniverseIndex, Variance, elaborate, max_universe,
AliasTy, Binder, BoundRegion, BoundRegionKind, BoundVar, BoundVariableKind, ClauseKind,
DebruijnIndex, InferCtxtLike, Interner, IsRigid, OutlivesClause, Region, RegionKind, TyKind,
TypeFoldable, TypeFolder, TypingMode, UniverseIndex, Variance, elaborate, max_universe,
set_aliases_to_non_rigid,
};

Expand Down Expand Up @@ -964,16 +964,16 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl
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(),
bound_vars: IndexMap::default(),
let mut replacer = MakeHigherRankederer {
cx: infcx,
preexisting_var_count: bound_outlives.bound_vars().len(),
regions: IndexMap::default(),
universe: u,
current_index: DebruijnIndex::ZERO,
};
let escaping_outlives = bound_outlives.skip_binder().fold_with(&mut replacer);
let bound_vars = bound_outlives.bound_vars().iter().chain(
core::mem::take(&mut replacer.bound_vars)
core::mem::take(&mut replacer.regions)
.into_iter()
.map(|(_, bound_region)| BoundVariableKind::Region(bound_region.kind)),
);
Expand All @@ -982,7 +982,7 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl
I::BoundVarKinds::from_vars(infcx.cx(), bound_vars),
);

// `PlaceholderReplacer` only folds regions. A non-lifetime binder can leave
// `MakeHigherRankederer` 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 {
Expand Down Expand Up @@ -1078,30 +1078,52 @@ pub fn regions_outlived_by_placeholder<I: Interner>(
})
}

pub struct PlaceholderReplacer<I: Interner> {
cx: I,
existing_var_count: usize,
bound_vars: IndexMap<BoundVar, BoundRegion<I>>,
pub struct MakeHigherRankederer<'a, I: Interner, Infcx: InferCtxtLike<Interner = I>> {
cx: &'a Infcx,
preexisting_var_count: usize,
regions: IndexMap<Region<I>, BoundRegion<I>>,
universe: UniverseIndex,
current_index: DebruijnIndex,
}

impl<I: Interner> TypeFolder<I> for PlaceholderReplacer<I> {
impl<'a, I: Interner, Infcx: InferCtxtLike<Interner = I>> MakeHigherRankederer<'a, I, Infcx> {
fn existing_var_count(&self) -> usize {
self.preexisting_var_count + self.regions.len()
}
}

impl<'a, I: Interner, Infcx: InferCtxtLike<Interner = I>> TypeFolder<I>
for MakeHigherRankederer<'a, I, Infcx>
{
fn cx(&self) -> I {
self.cx
self.cx.cx()
}

fn fold_region(&mut self, r: Region<I>) -> Region<I> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not in this PR, but, should we be folding types as well? for #![feature(non_lifetime_binders)]. tbh I don't know the state of non_lifetime_binders, if something like T::Alias<?x>: 'a -> for<NLB> T::Alias<NLB>: 'a is valid (in the sense of if it gets ICEy or not)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't get ICEy but we do just downgrade to ambiguity because we don't handle it. I have no idea what the right thing to do for non lifetime binders is lol that feature is not being actively developed rn tho

let cx = self.cx();

match r.kind() {
RegionKind::RePlaceholder(p) if p.universe == self.universe => {
let bound_vars_len = self.bound_vars.len();
let mapped_var = self.bound_vars.entry(p.bound.var).or_insert(BoundRegion {
var: BoundVar::from_usize(self.existing_var_count + bound_vars_len),
let existing_var_count = self.existing_var_count();
let mapped_var = self.regions.entry(r).or_insert(BoundRegion {
var: BoundVar::from_usize(existing_var_count),
kind: p.bound.kind,
});
Region::new_bound(self.cx, self.current_index, *mapped_var)
Region::new_bound(cx, self.current_index, *mapped_var)
}
RegionKind::ReVar(inf) => {
let u = self.cx.universe_of_region(inf).unwrap();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly a note for myself, I'm unfamiliar with phasing and when things are resolved/run, so I'm unsure if this is possible to ever be resolved and so universe_of_region returns None. Probably obvious once one knows how things work so a comment explaining why is probably not super worth it. Saw another comment that says it's only valid if eager_resolve_vars is run beforehand, but I don't see that here, but maybe we're working on the assumption that registered constraints have vars eagerly resolved?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we just crash here sometimes yeah :3 Should actually be proper after #164103

if u == self.universe {
let existing_var_count = self.existing_var_count();
let mapped_var = self.regions.entry(r).or_insert(BoundRegion {
var: BoundVar::from_usize(existing_var_count),
kind: BoundRegionKind::Anon,
});
Region::new_bound(cx, self.current_index, *mapped_var)
} else {
r
}
}
// FIXME(-Zassumptions-on-binders): We should be handling region variables here somehow
_ => r,
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally
//@ check-pass

// test that when rewriting alias outlives constraints we will replace
// region variables in the universe being handled, with (new) bound vars
// on the alias outlives constraint's binder.
//
// we previously did not do this and would get ambiguity which was overly
// conservative

#![feature(test_binder_constraints)]

trait Trait<'a> {
type Assoc;
}

core::test_binder_constraints! {
impl<T: for<'a> Trait<'a>> {
forall<'b>
where
for<'a> <T as Trait<'a>>::Assoc: 'b
{
forall<> {
exists<'a> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you know I'm realizing that I accidentally never actually tested exists, this is the first use of it in the test suite. whoops. glad it works!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✨

for<> <T as Trait<'a>>::Assoc: 'b
}
} expect {
for<'a> <T as Trait<'a>>::Assoc: 'b
}
} expect {
or { and {} }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is technically useless/not needed, right? just expect {} is fine. but being explicit is nice, shrug

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah

}
}
}

fn main() {}
Loading