Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Binder<Vec<Ty>> instead of Vec<Binder<Ty>> in both solvers for sized/auto traits/etc. #137689

Merged
merged 2 commits into from
Mar 1, 2025
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
6 changes: 4 additions & 2 deletions compiler/rustc_infer/src/infer/relate/higher_ranked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//! the end of the file for details.

use rustc_middle::ty::fold::FnMutDelegate;
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
use tracing::{debug, instrument};

Expand All @@ -26,8 +27,9 @@ impl<'tcx> InferCtxt<'tcx> {
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
if let Some(inner) = binder.clone().no_bound_vars() {
return inner;
// Inlined `no_bound_vars`.
if !binder.as_ref().skip_binder().has_escaping_bound_vars() {
return binder.skip_binder();
Comment on lines +31 to +32
Copy link
Contributor

Choose a reason for hiding this comment

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

to avoid clone?

do we do this more often, I've encountered this myself and have considered try_no_bound_vars() -> Result<T, Binder<T>> or maybe just changing no_bound_vars to that

Copy link
Member Author

Choose a reason for hiding this comment

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

well now in this pr we're entering whole binders of vecs rather than iterating over a vec of binder of ty which we then enter, so we do this more often i think

Copy link
Contributor

Choose a reason for hiding this comment

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

"do we do this more often"

as in: the pattern of no_bound_vars() and doing something with the binder if it returns None.

Copy link
Member Author

Choose a reason for hiding this comment

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

ohhh, as far as i can tell, no this seems like the only place where we were previously repeatedly fighting the Copy bound bc no_bound_vars consumes its valur

}

let next_universe = self.create_next_universe();
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,11 +324,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.features()
}

fn bound_coroutine_hidden_types(
fn coroutine_hidden_types(
self,
def_id: DefId,
) -> impl IntoIterator<Item = ty::EarlyBinder<'tcx, ty::Binder<'tcx, Ty<'tcx>>>> {
self.bound_coroutine_hidden_types(def_id)
) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, &'tcx ty::List<Ty<'tcx>>>> {
self.coroutine_hidden_types(def_id)
}

fn fn_sig(self, def_id: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> {
Expand Down
66 changes: 26 additions & 40 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,52 +725,38 @@ impl<'tcx> TyCtxt<'tcx> {
}
}

/// Return the set of types that should be taken into account when checking
/// trait bounds on a coroutine's internal state.
// FIXME(compiler-errors): We should remove this when the old solver goes away;
// and all other usages of this function should go through `bound_coroutine_hidden_types`
// instead.
pub fn coroutine_hidden_types(
self,
def_id: DefId,
) -> impl Iterator<Item = ty::EarlyBinder<'tcx, Ty<'tcx>>> {
let coroutine_layout = self.mir_coroutine_witnesses(def_id);
coroutine_layout
.as_ref()
.map_or_else(|| [].iter(), |l| l.field_tys.iter())
.filter(|decl| !decl.ignore_for_traits)
.map(|decl| ty::EarlyBinder::bind(decl.ty))
}

/// Return the set of types that should be taken into account when checking
/// trait bounds on a coroutine's internal state. This properly replaces
/// `ReErased` with new existential bound lifetimes.
pub fn bound_coroutine_hidden_types(
pub fn coroutine_hidden_types(
self,
def_id: DefId,
) -> impl Iterator<Item = ty::EarlyBinder<'tcx, ty::Binder<'tcx, Ty<'tcx>>>> {
) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, &'tcx ty::List<Ty<'tcx>>>> {
let coroutine_layout = self.mir_coroutine_witnesses(def_id);
coroutine_layout
.as_ref()
.map_or_else(|| [].iter(), |l| l.field_tys.iter())
.filter(|decl| !decl.ignore_for_traits)
.map(move |decl| {
let mut vars = vec![];
let ty = fold_regions(self, decl.ty, |re, debruijn| {
assert_eq!(re, self.lifetimes.re_erased);
let var = ty::BoundVar::from_usize(vars.len());
vars.push(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon));
ty::Region::new_bound(
self,
debruijn,
ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
)
});
ty::EarlyBinder::bind(ty::Binder::bind_with_vars(
ty,
self.mk_bound_variable_kinds(&vars),
))
})
let mut vars = vec![];
let bound_tys = self.mk_type_list_from_iter(
coroutine_layout
.as_ref()
.map_or_else(|| [].iter(), |l| l.field_tys.iter())
.filter(|decl| !decl.ignore_for_traits)
.map(|decl| {
let ty = fold_regions(self, decl.ty, |re, debruijn| {
assert_eq!(re, self.lifetimes.re_erased);
let var = ty::BoundVar::from_usize(vars.len());
vars.push(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon));
ty::Region::new_bound(
self,
debruijn,
ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
)
});
ty
}),
);
ty::EarlyBinder::bind(ty::Binder::bind_with_vars(
bound_tys,
self.mk_bound_variable_kinds(&vars),
))
}

/// Expands the given impl trait type, stopping if the type is recursive.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::solve::{AdtDestructorKind, EvalCtxt, Goal, NoSolution};
pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<D, I>(
ecx: &EvalCtxt<'_, D>,
ty: I::Ty,
) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>
where
D: SolverDelegate<Interner = I>,
I: Interner,
Expand All @@ -33,10 +33,10 @@ where
| ty::FnPtr(..)
| ty::Error(_)
| ty::Never
| ty::Char => Ok(vec![]),
| ty::Char => Ok(ty::Binder::dummy(vec![])),

// Treat `str` like it's defined as `struct str([u8]);`
ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(cx, Ty::new_u8(cx)))]),
ty::Str => Ok(ty::Binder::dummy(vec![Ty::new_slice(cx, Ty::new_u8(cx))])),

ty::Dynamic(..)
| ty::Param(..)
Expand All @@ -49,53 +49,49 @@ where
}

ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => {
Ok(vec![ty::Binder::dummy(element_ty)])
Ok(ty::Binder::dummy(vec![element_ty]))
}

ty::Pat(element_ty, _) | ty::Array(element_ty, _) | ty::Slice(element_ty) => {
Ok(vec![ty::Binder::dummy(element_ty)])
Ok(ty::Binder::dummy(vec![element_ty]))
}

ty::Tuple(tys) => {
// (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
Ok(tys.iter().map(ty::Binder::dummy).collect())
Ok(ty::Binder::dummy(tys.to_vec()))
}

ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
ty::Closure(_, args) => Ok(ty::Binder::dummy(vec![args.as_closure().tupled_upvars_ty()])),

ty::CoroutineClosure(_, args) => {
Ok(vec![ty::Binder::dummy(args.as_coroutine_closure().tupled_upvars_ty())])
Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()]))
}

ty::Coroutine(_, args) => {
let coroutine_args = args.as_coroutine();
Ok(vec![
ty::Binder::dummy(coroutine_args.tupled_upvars_ty()),
ty::Binder::dummy(coroutine_args.witness()),
])
Ok(ty::Binder::dummy(vec![coroutine_args.tupled_upvars_ty(), coroutine_args.witness()]))
}

ty::CoroutineWitness(def_id, args) => Ok(ecx
.cx()
.bound_coroutine_hidden_types(def_id)
.into_iter()
.map(|bty| bty.instantiate(cx, args))
.collect()),
.coroutine_hidden_types(def_id)
.instantiate(cx, args)
.map_bound(|tys| tys.to_vec())),

ty::UnsafeBinder(bound_ty) => Ok(vec![bound_ty.into()]),
ty::UnsafeBinder(bound_ty) => Ok(bound_ty.map_bound(|ty| vec![ty])),

// For `PhantomData<T>`, we pass `T`.
ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![ty::Binder::dummy(args.type_at(0))]),
ty::Adt(def, args) if def.is_phantom_data() => Ok(ty::Binder::dummy(vec![args.type_at(0)])),

ty::Adt(def, args) => {
Ok(def.all_field_tys(cx).iter_instantiated(cx, args).map(ty::Binder::dummy).collect())
Ok(ty::Binder::dummy(def.all_field_tys(cx).iter_instantiated(cx, args).collect()))
}

ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
// We can resolve the `impl Trait` to its concrete type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
Ok(vec![ty::Binder::dummy(cx.type_of(def_id).instantiate(cx, args))])
Ok(ty::Binder::dummy(vec![cx.type_of(def_id).instantiate(cx, args)]))
}
}
}
Expand All @@ -104,7 +100,7 @@ where
pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<D, I>(
ecx: &EvalCtxt<'_, D>,
ty: I::Ty,
) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>
where
D: SolverDelegate<Interner = I>,
I: Interner,
Expand All @@ -130,7 +126,7 @@ where
| ty::CoroutineClosure(..)
| ty::Never
| ty::Dynamic(_, _, ty::DynStar)
| ty::Error(_) => Ok(vec![]),
| ty::Error(_) => Ok(ty::Binder::dummy(vec![])),

ty::Str
| ty::Slice(_)
Expand All @@ -145,11 +141,11 @@ where
panic!("unexpected type `{ty:?}`")
}

ty::UnsafeBinder(bound_ty) => Ok(vec![bound_ty.into()]),
ty::UnsafeBinder(bound_ty) => Ok(bound_ty.map_bound(|ty| vec![ty])),

// impl Sized for ()
// impl Sized for (T1, T2, .., Tn) where Tn: Sized if n >= 1
ty::Tuple(tys) => Ok(tys.last().map_or_else(Vec::new, |ty| vec![ty::Binder::dummy(ty)])),
ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.last().map_or_else(Vec::new, |ty| vec![ty]))),

// impl Sized for Adt<Args...> where sized_constraint(Adt)<Args...>: Sized
// `sized_constraint(Adt)` is the deepest struct trail that can be determined
Expand All @@ -162,9 +158,9 @@ where
// if the ADT is sized for all possible args.
ty::Adt(def, args) => {
if let Some(sized_crit) = def.sized_constraint(ecx.cx()) {
Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.cx(), args))])
Ok(ty::Binder::dummy(vec![sized_crit.instantiate(ecx.cx(), args)]))
} else {
Ok(vec![])
Ok(ty::Binder::dummy(vec![]))
}
}
}
Expand All @@ -174,14 +170,14 @@ where
pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<D, I>(
ecx: &EvalCtxt<'_, D>,
ty: I::Ty,
) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
match ty.kind() {
// impl Copy/Clone for FnDef, FnPtr
ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(vec![]),
ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])),

// Implementations are provided in core
ty::Uint(_)
Expand All @@ -197,7 +193,7 @@ where

// Cannot implement in core, as we can't be generic over patterns yet,
// so we'd have to list all patterns and type combinations.
ty::Pat(ty, ..) => Ok(vec![ty::Binder::dummy(ty)]),
ty::Pat(ty, ..) => Ok(ty::Binder::dummy(vec![ty])),

ty::Dynamic(..)
| ty::Str
Expand All @@ -215,14 +211,14 @@ where
}

// impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone
ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.to_vec())),

// impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone
ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
ty::Closure(_, args) => Ok(ty::Binder::dummy(vec![args.as_closure().tupled_upvars_ty()])),

// impl Copy/Clone for CoroutineClosure where Self::TupledUpvars: Copy/Clone
ty::CoroutineClosure(_, args) => {
Ok(vec![ty::Binder::dummy(args.as_coroutine_closure().tupled_upvars_ty())])
Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()]))
}

// only when `coroutine_clone` is enabled and the coroutine is movable
Expand All @@ -232,10 +228,7 @@ where
Movability::Movable => {
if ecx.cx().features().coroutine_clone() {
let coroutine = args.as_coroutine();
Ok(vec![
ty::Binder::dummy(coroutine.tupled_upvars_ty()),
ty::Binder::dummy(coroutine.witness()),
])
Ok(ty::Binder::dummy(vec![coroutine.tupled_upvars_ty(), coroutine.witness()]))
} else {
Err(NoSolution)
}
Expand All @@ -247,10 +240,9 @@ where
// impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types
ty::CoroutineWitness(def_id, args) => Ok(ecx
.cx()
.bound_coroutine_hidden_types(def_id)
.into_iter()
.map(|bty| bty.instantiate(ecx.cx(), args))
.collect()),
.coroutine_hidden_types(def_id)
.instantiate(ecx.cx(), args)
.map_bound(|tys| tys.to_vec())),
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ where

/// `enter_forall`, but takes `&mut self` and passes it back through the
/// callback since it can't be aliased during the call.
pub(super) fn enter_forall<T: TypeFoldable<I> + Copy, U>(
pub(super) fn enter_forall<T: TypeFoldable<I>, U>(
&mut self,
value: ty::Binder<I, T>,
f: impl FnOnce(&mut Self, T) -> U,
Expand Down
16 changes: 7 additions & 9 deletions compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1239,17 +1239,15 @@ where
constituent_tys: impl Fn(
&EvalCtxt<'_, D>,
I::Ty,
) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>,
) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>,
) -> Result<Candidate<I>, NoSolution> {
self.probe_trait_candidate(source).enter(|ecx| {
let goals = constituent_tys(ecx, goal.predicate.self_ty())?
.into_iter()
.map(|ty| {
ecx.enter_forall(ty, |ecx, ty| {
goal.with(ecx.cx(), goal.predicate.with_self_ty(ecx.cx(), ty))
})
})
.collect::<Vec<_>>();
let goals =
ecx.enter_forall(constituent_tys(ecx, goal.predicate.self_ty())?, |ecx, tys| {
tys.into_iter()
.map(|ty| goal.with(ecx.cx(), goal.predicate.with_self_ty(ecx.cx(), ty)))
.collect::<Vec<_>>()
});
ecx.add_goals(GoalSource::ImplWhereBound, goals);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
Expand Down
Loading
Loading