Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Uplift BoundVarReplacer
  • Loading branch information
compiler-errors committed Jun 13, 2025
commit 6fa6d0e0977b1412724c74d962a1eba7c13c5c5a
2 changes: 1 addition & 1 deletion Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4618,7 +4618,7 @@ dependencies = [
"derive-where",
"ena",
"indexmap",
"rustc-hash 1.1.0",
"rustc-hash 2.1.1",
"rustc_ast_ir",
"rustc_data_structures",
"rustc_index",
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
Const::new_bound(tcx, debruijn, var)
}

fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderConst) -> Self {
Const::new_placeholder(tcx, placeholder)
}

fn new_unevaluated(interner: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Self {
Const::new_unevaluated(interner, uv)
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ impl<'tcx> rustc_type_ir::inherent::Region<TyCtxt<'tcx>> for Region<'tcx> {
Region::new_bound(tcx, debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon })
}

fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderRegion) -> Self {
Region::new_placeholder(tcx, placeholder)
}

fn new_static(tcx: TyCtxt<'tcx>) -> Self {
tcx.lifetimes.re_static
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_next_trait_solver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
pub mod canonicalizer;
pub mod coherence;
pub mod delegate;
pub mod placeholder;
pub mod resolve;
pub mod solve;
158 changes: 158 additions & 0 deletions compiler/rustc_next_trait_solver/src/placeholder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use core::panic;

use rustc_type_ir::data_structures::IndexMap;
use rustc_type_ir::inherent::*;
use rustc_type_ir::{
self as ty, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable,
TypeVisitableExt,
};

pub struct BoundVarReplacer<'a, Infcx, I = <Infcx as InferCtxtLike>::Interner>
where
Infcx: InferCtxtLike<Interner = I>,
I: Interner,
{
infcx: &'a Infcx,
// These three maps track the bound variable that were replaced by placeholders. It might be
// nice to remove these since we already have the `kind` in the placeholder; we really just need
// the `var` (but we *could* bring that into scope if we were to track them as we pass them).
mapped_regions: IndexMap<I::PlaceholderRegion, I::BoundRegion>,
mapped_types: IndexMap<I::PlaceholderTy, I::BoundTy>,
mapped_consts: IndexMap<I::PlaceholderConst, I::BoundConst>,
// The current depth relative to *this* folding, *not* the entire normalization. In other words,
// the depth of binders we've passed here.
current_index: ty::DebruijnIndex,
// The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy:
// we don't actually create a universe until we see a bound var we have to replace.
universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
}

impl<'a, Infcx, I> BoundVarReplacer<'a, Infcx, I>
where
Infcx: InferCtxtLike<Interner = I>,
I: Interner,
{
/// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
/// use a binding level above `universe_indices.len()`, we fail.
pub fn replace_bound_vars<T: TypeFoldable<I>>(
infcx: &'a Infcx,
universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
value: T,
) -> (
T,
IndexMap<I::PlaceholderRegion, I::BoundRegion>,
IndexMap<I::PlaceholderTy, I::BoundTy>,
IndexMap<I::PlaceholderConst, I::BoundConst>,
) {
let mut replacer = BoundVarReplacer {
infcx,
mapped_regions: Default::default(),
mapped_types: Default::default(),
mapped_consts: Default::default(),
current_index: ty::INNERMOST,
universe_indices,
};

let value = value.fold_with(&mut replacer);

(value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts)
}

fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex {
let infcx = self.infcx;
let index =
self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1;
let universe = self.universe_indices[index].unwrap_or_else(|| {
for i in self.universe_indices.iter_mut().take(index + 1) {
*i = i.or_else(|| Some(infcx.create_next_universe()))
}
self.universe_indices[index].unwrap()
});
universe
}
}

impl<Infcx, I> TypeFolder<I> for BoundVarReplacer<'_, Infcx, I>
where
Infcx: InferCtxtLike<Interner = I>,
I: Interner,
{
fn cx(&self) -> I {
self.infcx.cx()
}

fn fold_binder<T: TypeFoldable<I>>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T> {
self.current_index.shift_in(1);
let t = t.super_fold_with(self);
self.current_index.shift_out(1);
t
}

fn fold_region(&mut self, r: I::Region) -> I::Region {
match r.kind() {
ty::ReBound(debruijn, _)
if debruijn.as_usize()
>= self.current_index.as_usize() + self.universe_indices.len() =>
{
panic!(
"Bound vars {r:#?} outside of `self.universe_indices`: {:#?}",
self.universe_indices
);
}
ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
let universe = self.universe_for(debruijn);
let p = PlaceholderLike::new(universe, br);
self.mapped_regions.insert(p, br);
Region::new_placeholder(self.cx(), p)
}
_ => r,
}
}

fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
match t.kind() {
ty::Bound(debruijn, _)
if debruijn.as_usize() + 1
> self.current_index.as_usize() + self.universe_indices.len() =>
{
panic!(
"Bound vars {t:#?} outside of `self.universe_indices`: {:#?}",
self.universe_indices
);
}
ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
let universe = self.universe_for(debruijn);
let p = PlaceholderLike::new(universe, bound_ty);
self.mapped_types.insert(p, bound_ty);
Ty::new_placeholder(self.cx(), p)
}
_ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
_ => t,
}
}

fn fold_const(&mut self, ct: I::Const) -> I::Const {
match ct.kind() {
ty::ConstKind::Bound(debruijn, _)
if debruijn.as_usize() + 1
> self.current_index.as_usize() + self.universe_indices.len() =>
{
panic!(
"Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}",
self.universe_indices
);
}
ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => {
let universe = self.universe_for(debruijn);
let p = PlaceholderLike::new(universe, bound_const);
self.mapped_consts.insert(p, bound_const);
Const::new_placeholder(self.cx(), p)
}
_ => ct.super_fold_with(self),
}
}

fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
}
}
147 changes: 1 addition & 146 deletions compiler/rustc_trait_selection/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use rustc_middle::bug;
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
};
pub use rustc_next_trait_solver::placeholder::BoundVarReplacer;
use rustc_span::Span;
use smallvec::{SmallVec, smallvec};
use tracing::debug;
Expand Down Expand Up @@ -212,152 +213,6 @@ pub fn with_replaced_escaping_bound_vars<
}
}

pub struct BoundVarReplacer<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
// These three maps track the bound variable that were replaced by placeholders. It might be
// nice to remove these since we already have the `kind` in the placeholder; we really just need
// the `var` (but we *could* bring that into scope if we were to track them as we pass them).
mapped_regions: FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion>,
mapped_types: FxIndexMap<ty::PlaceholderType, ty::BoundTy>,
mapped_consts: FxIndexMap<ty::PlaceholderConst, ty::BoundVar>,
// The current depth relative to *this* folding, *not* the entire normalization. In other words,
// the depth of binders we've passed here.
current_index: ty::DebruijnIndex,
// The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy:
// we don't actually create a universe until we see a bound var we have to replace.
universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
}

impl<'a, 'tcx> BoundVarReplacer<'a, 'tcx> {
/// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
/// use a binding level above `universe_indices.len()`, we fail.
pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>(
infcx: &'a InferCtxt<'tcx>,
universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
value: T,
) -> (
T,
FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion>,
FxIndexMap<ty::PlaceholderType, ty::BoundTy>,
FxIndexMap<ty::PlaceholderConst, ty::BoundVar>,
) {
let mapped_regions: FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion> =
FxIndexMap::default();
let mapped_types: FxIndexMap<ty::PlaceholderType, ty::BoundTy> = FxIndexMap::default();
let mapped_consts: FxIndexMap<ty::PlaceholderConst, ty::BoundVar> = FxIndexMap::default();

let mut replacer = BoundVarReplacer {
infcx,
mapped_regions,
mapped_types,
mapped_consts,
current_index: ty::INNERMOST,
universe_indices,
};

let value = value.fold_with(&mut replacer);

(value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts)
}

fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex {
let infcx = self.infcx;
let index =
self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1;
let universe = self.universe_indices[index].unwrap_or_else(|| {
for i in self.universe_indices.iter_mut().take(index + 1) {
*i = i.or_else(|| Some(infcx.create_next_universe()))
}
self.universe_indices[index].unwrap()
});
universe
}
}

impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}

fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
&mut self,
t: ty::Binder<'tcx, T>,
) -> ty::Binder<'tcx, T> {
self.current_index.shift_in(1);
let t = t.super_fold_with(self);
self.current_index.shift_out(1);
t
}

fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
match r.kind() {
ty::ReBound(debruijn, _)
if debruijn.as_usize()
>= self.current_index.as_usize() + self.universe_indices.len() =>
{
bug!(
"Bound vars {r:#?} outside of `self.universe_indices`: {:#?}",
self.universe_indices
);
}
ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
let universe = self.universe_for(debruijn);
let p = ty::PlaceholderRegion { universe, bound: br };
self.mapped_regions.insert(p, br);
ty::Region::new_placeholder(self.infcx.tcx, p)
}
_ => r,
}
}

fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
match *t.kind() {
ty::Bound(debruijn, _)
if debruijn.as_usize() + 1
> self.current_index.as_usize() + self.universe_indices.len() =>
{
bug!(
"Bound vars {t:#?} outside of `self.universe_indices`: {:#?}",
self.universe_indices
);
}
ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
let universe = self.universe_for(debruijn);
let p = ty::PlaceholderType { universe, bound: bound_ty };
self.mapped_types.insert(p, bound_ty);
Ty::new_placeholder(self.infcx.tcx, p)
}
_ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
_ => t,
}
}

fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
match ct.kind() {
ty::ConstKind::Bound(debruijn, _)
if debruijn.as_usize() + 1
> self.current_index.as_usize() + self.universe_indices.len() =>
{
bug!(
"Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}",
self.universe_indices
);
}
ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => {
let universe = self.universe_for(debruijn);
let p = ty::PlaceholderConst { universe, bound: bound_const };
self.mapped_consts.insert(p, bound_const);
ty::Const::new_placeholder(self.infcx.tcx, p)
}
_ => ct.super_fold_with(self),
}
}

fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
}
}

/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
pub struct PlaceholderReplacer<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_type_ir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ bitflags = "2.4.1"
derive-where = "1.2.7"
ena = "0.14.3"
indexmap = "2.0.0"
rustc-hash = "1.1.0"
rustc-hash = "2.0.0"
rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false }
rustc_data_structures = { path = "../rustc_data_structures", optional = true }
rustc_index = { path = "../rustc_index", default-features = false }
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_type_ir/src/inherent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ pub trait Region<I: Interner<Region = Self>>:

fn new_static(interner: I) -> Self;

fn new_placeholder(interner: I, var: I::PlaceholderRegion) -> Self;

fn is_bound(self) -> bool {
matches!(self.kind(), ty::ReBound(..))
}
Expand All @@ -254,6 +256,8 @@ pub trait Const<I: Interner<Const = Self>>:

fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;

fn new_placeholder(interner: I, param: I::PlaceholderConst) -> Self;

fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst<I>) -> Self;

fn new_expr(interner: I, expr: I::ExprConst) -> Self;
Expand Down