Instantiate binder for Copy/Clone/Sized eagerly

This commit is contained in:
Michael Goulet
2025-07-07 22:21:08 +00:00
parent 8d2d4eb89a
commit bbb409cc68
3 changed files with 222 additions and 196 deletions

View File

@@ -19,7 +19,7 @@ use rustc_middle::{bug, span_bug};
use tracing::{debug, instrument, trace};
use super::SelectionCandidate::*;
use super::{BuiltinImplConditions, SelectionCandidateSet, SelectionContext, TraitObligationStack};
use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack};
use crate::traits::util;
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
@@ -75,8 +75,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.assemble_candidates_from_impls(obligation, &mut candidates);
// For other types, we'll use the builtin rules.
let copy_conditions = self.copy_clone_conditions(obligation);
self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates);
self.assemble_builtin_copy_clone_candidate(
obligation.predicate.self_ty().skip_binder(),
&mut candidates,
);
}
Some(LangItem::DiscriminantKind) => {
// `DiscriminantKind` is automatically implemented for every type.
@@ -88,14 +90,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
Some(LangItem::Sized) => {
self.assemble_builtin_sized_candidate(
obligation,
obligation.predicate.self_ty().skip_binder(),
&mut candidates,
SizedTraitKind::Sized,
);
}
Some(LangItem::MetaSized) => {
self.assemble_builtin_sized_candidate(
obligation,
obligation.predicate.self_ty().skip_binder(),
&mut candidates,
SizedTraitKind::MetaSized,
);
@@ -357,14 +359,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let self_ty = obligation.self_ty().skip_binder();
// gen constructs get lowered to a special kind of coroutine that
// should directly `impl FusedIterator`.
if let ty::Coroutine(did, ..) = self_ty.kind()
&& self.tcx().coroutine_is_gen(*did)
{
debug!(?self_ty, ?obligation, "assemble_fused_iterator_candidates",);
if self.coroutine_is_gen(obligation.self_ty().skip_binder()) {
candidates.vec.push(BuiltinCandidate);
}
}
@@ -1113,41 +1108,164 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
/// Assembles the `Sized` and `MetaSized` traits which are built-in to the language itself.
/// Assembles `Copy` and `Clone` candidates for built-in types with no libcore-defined
/// `Copy` or `Clone` impls.
#[instrument(level = "debug", skip(self, candidates))]
fn assemble_builtin_sized_candidate(
fn assemble_builtin_copy_clone_candidate(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
self_ty: Ty<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
sizedness: SizedTraitKind,
) {
match self.sizedness_conditions(obligation, sizedness) {
BuiltinImplConditions::Where(_nested) => {
candidates.vec.push(SizedCandidate);
match *self_ty.kind() {
// These impls are built-in because we cannot express sufficiently
// generic impls in libcore.
ty::FnDef(..)
| ty::FnPtr(..)
| ty::Error(_)
| ty::Tuple(..)
| ty::CoroutineWitness(..)
| ty::Pat(..) => {
candidates.vec.push(BuiltinCandidate);
}
BuiltinImplConditions::None => {}
BuiltinImplConditions::Ambiguous => {
// Implementations provided in libcore.
ty::Uint(_)
| ty::Int(_)
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Bool
| ty::Float(_)
| ty::Char
| ty::RawPtr(..)
| ty::Never
| ty::Ref(_, _, hir::Mutability::Not)
| ty::Array(..) => {}
// FIXME(unsafe_binder): Should we conditionally
// (i.e. universally) implement copy/clone?
ty::UnsafeBinder(_) => {}
// Not `Sized`, which is a supertrait of `Copy`/`Clone`.
ty::Dynamic(..) | ty::Str | ty::Slice(..) | ty::Foreign(..) => {}
// Not `Copy` or `Clone` by design.
ty::Ref(_, _, hir::Mutability::Mut) => {}
ty::Coroutine(coroutine_def_id, args) => {
match self.tcx().coroutine_movability(coroutine_def_id) {
hir::Movability::Static => {}
hir::Movability::Movable => {
if self.tcx().features().coroutine_clone() {
let resolved_upvars =
self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty());
let resolved_witness =
self.infcx.shallow_resolve(args.as_coroutine().witness());
if resolved_upvars.is_ty_var() || resolved_witness.is_ty_var() {
// Not yet resolved.
candidates.ambiguous = true;
} else {
candidates.vec.push(BuiltinCandidate);
}
}
}
}
}
ty::Closure(_, args) => {
let resolved_upvars =
self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty());
if resolved_upvars.is_ty_var() {
// Not yet resolved.
candidates.ambiguous = true;
} else {
candidates.vec.push(BuiltinCandidate);
}
}
ty::CoroutineClosure(_, args) => {
let resolved_upvars =
self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty());
if resolved_upvars.is_ty_var() {
// Not yet resolved.
candidates.ambiguous = true;
} else {
candidates.vec.push(BuiltinCandidate);
}
}
// Fallback to whatever user-defined impls or param-env clauses exist in this case.
ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {}
ty::Infer(ty::TyVar(_)) => {
candidates.ambiguous = true;
}
// Only appears when assembling higher-ranked `for<T> T: Clone`.
ty::Bound(..) => {}
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty);
}
}
}
/// Assembles the trait which are built-in to the language itself:
/// e.g. `Copy` and `Clone`.
/// Assembles the `Sized` and `MetaSized` traits which are built-in to the language itself.
#[instrument(level = "debug", skip(self, candidates))]
fn assemble_builtin_bound_candidates(
fn assemble_builtin_sized_candidate(
&mut self,
conditions: BuiltinImplConditions<'tcx>,
self_ty: Ty<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
sizedness: SizedTraitKind,
) {
match conditions {
BuiltinImplConditions::Where(_) => {
candidates.vec.push(BuiltinCandidate);
match *self_ty.kind() {
// Always sized.
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Uint(_)
| ty::Int(_)
| ty::Bool
| ty::Float(_)
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::RawPtr(..)
| ty::Char
| ty::Ref(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Array(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Never
| ty::Error(_) => {
candidates.vec.push(SizedCandidate);
}
BuiltinImplConditions::None => {}
BuiltinImplConditions::Ambiguous => {
// Conditionally `Sized`.
ty::Tuple(..) | ty::Pat(..) | ty::Adt(..) | ty::UnsafeBinder(_) => {
candidates.vec.push(SizedCandidate);
}
// `MetaSized` but not `Sized`.
ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness {
SizedTraitKind::Sized => {}
SizedTraitKind::MetaSized => {
candidates.vec.push(SizedCandidate);
}
},
// Not `MetaSized` or `Sized`.
ty::Foreign(..) => {}
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => {}
ty::Infer(ty::TyVar(_)) => {
candidates.ambiguous = true;
}
// Only appears when assembling higher-ranked `for<T> T: Sized`.
ty::Bound(..) => {}
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty);
}
}
}

View File

@@ -21,7 +21,7 @@ use thin_vec::thin_vec;
use tracing::{debug, instrument};
use super::SelectionCandidate::{self, *};
use super::{BuiltinImplConditions, PredicateObligations, SelectionContext};
use super::{PredicateObligations, SelectionContext};
use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
use crate::traits::util::{self, closure_trait_ref_and_return_type};
use crate::traits::{
@@ -257,16 +257,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?obligation, "confirm_builtin_candidate");
let tcx = self.tcx();
let trait_def = obligation.predicate.def_id();
let conditions = match tcx.as_lang_item(trait_def) {
Some(LangItem::Sized) => self.sizedness_conditions(obligation, SizedTraitKind::Sized),
let self_ty = self.infcx.shallow_resolve(
self.infcx.enter_forall_and_leak_universe(obligation.predicate.self_ty()),
);
let types = match tcx.as_lang_item(trait_def) {
Some(LangItem::Sized) => self.sizedness_conditions(self_ty, SizedTraitKind::Sized),
Some(LangItem::MetaSized) => {
self.sizedness_conditions(obligation, SizedTraitKind::MetaSized)
self.sizedness_conditions(self_ty, SizedTraitKind::MetaSized)
}
Some(LangItem::PointeeSized) => {
bug!("`PointeeSized` is removing during lowering");
}
Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(obligation),
Some(LangItem::FusedIterator) => self.fused_iterator_conditions(obligation),
Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(self_ty),
Some(LangItem::FusedIterator) => {
if self.coroutine_is_gen(self_ty) {
ty::Binder::dummy(vec![])
} else {
unreachable!("tried to assemble `FusedIterator` for non-gen coroutine");
}
}
Some(
LangItem::Destruct
| LangItem::DiscriminantKind
@@ -274,12 +283,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
| LangItem::PointeeTrait
| LangItem::Tuple
| LangItem::Unpin,
) => BuiltinImplConditions::Where(ty::Binder::dummy(vec![])),
) => ty::Binder::dummy(vec![]),
other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"),
};
let BuiltinImplConditions::Where(types) = conditions else {
bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation);
};
let types = self.infcx.enter_forall_and_leak_universe(types);
let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);

View File

@@ -188,18 +188,6 @@ struct EvaluatedCandidate<'tcx> {
evaluation: EvaluationResult,
}
/// When does the builtin impl for `T: Trait` apply?
#[derive(Debug)]
enum BuiltinImplConditions<'tcx> {
/// The impl is conditional on `T1, T2, ...: Trait`.
Where(ty::Binder<'tcx, Vec<Ty<'tcx>>>),
/// There is no built-in impl. There may be some other
/// candidate (a where-clause or user-defined impl).
None,
/// It is unknown whether there is an impl.
Ambiguous,
}
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> {
SelectionContext {
@@ -2104,14 +2092,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
impl<'tcx> SelectionContext<'_, 'tcx> {
fn sizedness_conditions(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
self_ty: Ty<'tcx>,
sizedness: SizedTraitKind,
) -> BuiltinImplConditions<'tcx> {
use self::BuiltinImplConditions::{Ambiguous, None, Where};
// NOTE: binder moved to (*)
let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty());
) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
match self_ty.kind() {
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Uint(_)
@@ -2129,59 +2112,44 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Never
| ty::Error(_) => {
// safe for everything
Where(ty::Binder::dummy(Vec::new()))
}
| ty::Error(_) => ty::Binder::dummy(vec![]),
ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness {
SizedTraitKind::Sized => None,
SizedTraitKind::MetaSized => Where(ty::Binder::dummy(Vec::new())),
SizedTraitKind::Sized => unreachable!("tried to assemble `Sized` for unsized type"),
SizedTraitKind::MetaSized => ty::Binder::dummy(vec![]),
},
ty::Foreign(..) => None,
ty::Foreign(..) => unreachable!("tried to assemble `Sized` for unsized type"),
ty::Tuple(tys) => Where(
obligation.predicate.rebind(tys.last().map_or_else(Vec::new, |&last| vec![last])),
),
ty::Tuple(tys) => {
ty::Binder::dummy(tys.last().map_or_else(Vec::new, |&last| vec![last]))
}
ty::Pat(ty, _) => Where(obligation.predicate.rebind(vec![*ty])),
ty::Pat(ty, _) => ty::Binder::dummy(vec![*ty]),
ty::Adt(def, args) => {
if let Some(crit) = def.sizedness_constraint(self.tcx(), sizedness) {
// (*) binder moved here
Where(obligation.predicate.rebind(vec![crit.instantiate(self.tcx(), args)]))
ty::Binder::dummy(vec![crit.instantiate(self.tcx(), args)])
} else {
Where(ty::Binder::dummy(Vec::new()))
ty::Binder::dummy(vec![])
}
}
// FIXME(unsafe_binders): This binder needs to be squashed
ty::UnsafeBinder(binder_ty) => Where(binder_ty.map_bound(|ty| vec![ty])),
ty::UnsafeBinder(binder_ty) => binder_ty.map_bound(|ty| vec![ty]),
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None,
ty::Infer(ty::TyVar(_)) => Ambiguous,
// We can make this an ICE if/once we actually instantiate the trait obligation eagerly.
ty::Bound(..) => None,
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty);
ty::Alias(..)
| ty::Param(_)
| ty::Placeholder(..)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
| ty::Bound(..) => {
bug!("asked to assemble `Sized` of unexpected type: {:?}", self_ty);
}
}
}
fn copy_clone_conditions(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
) -> BuiltinImplConditions<'tcx> {
// NOTE: binder moved to (*)
let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty());
use self::BuiltinImplConditions::{Ambiguous, None, Where};
fn copy_clone_conditions(&mut self, self_ty: Ty<'tcx>) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
match *self_ty.kind() {
ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())),
ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => ty::Binder::dummy(vec![]),
ty::Uint(_)
| ty::Int(_)
@@ -2193,127 +2161,78 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| ty::Never
| ty::Ref(_, _, hir::Mutability::Not)
| ty::Array(..) => {
// Implementations provided in libcore
None
unreachable!("tried to assemble `Sized` for type with libcore-provided impl")
}
// FIXME(unsafe_binder): Should we conditionally
// (i.e. universally) implement copy/clone?
ty::UnsafeBinder(_) => None,
ty::Dynamic(..)
| ty::Str
| ty::Slice(..)
| ty::Foreign(..)
| ty::Ref(_, _, hir::Mutability::Mut) => None,
ty::UnsafeBinder(_) => unreachable!("tried to assemble `Sized` for unsafe binder"),
ty::Tuple(tys) => {
// (*) binder moved here
Where(obligation.predicate.rebind(tys.iter().collect()))
ty::Binder::dummy(tys.iter().collect())
}
ty::Pat(ty, _) => {
// (*) binder moved here
Where(obligation.predicate.rebind(vec![ty]))
ty::Binder::dummy(vec![ty])
}
ty::Coroutine(coroutine_def_id, args) => {
match self.tcx().coroutine_movability(coroutine_def_id) {
hir::Movability::Static => None,
hir::Movability::Static => {
unreachable!("tried to assemble `Sized` for static coroutine")
}
hir::Movability::Movable => {
if self.tcx().features().coroutine_clone() {
let resolved_upvars =
self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty());
let resolved_witness =
self.infcx.shallow_resolve(args.as_coroutine().witness());
if resolved_upvars.is_ty_var() || resolved_witness.is_ty_var() {
// Not yet resolved.
Ambiguous
} else {
let all = args
.as_coroutine()
ty::Binder::dummy(
args.as_coroutine()
.upvar_tys()
.iter()
.chain([args.as_coroutine().witness()])
.collect::<Vec<_>>();
Where(obligation.predicate.rebind(all))
}
.collect::<Vec<_>>(),
)
} else {
None
unreachable!(
"tried to assemble `Sized` for coroutine without enabled feature"
)
}
}
}
}
ty::CoroutineWitness(def_id, args) => {
let hidden_types = rebind_coroutine_witness_types(
self.infcx.tcx,
def_id,
args,
obligation.predicate.bound_vars(),
);
Where(hidden_types)
}
ty::CoroutineWitness(def_id, args) => self
.infcx
.tcx
.coroutine_hidden_types(def_id)
.instantiate(self.infcx.tcx, args)
.map_bound(|witness| witness.types.to_vec()),
ty::Closure(_, args) => {
// (*) binder moved here
let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty());
if let ty::Infer(ty::TyVar(_)) = ty.kind() {
// Not yet resolved.
Ambiguous
} else {
Where(obligation.predicate.rebind(args.as_closure().upvar_tys().to_vec()))
}
}
ty::Closure(_, args) => ty::Binder::dummy(args.as_closure().upvar_tys().to_vec()),
ty::CoroutineClosure(_, args) => {
// (*) binder moved here
let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty());
if let ty::Infer(ty::TyVar(_)) = ty.kind() {
// Not yet resolved.
Ambiguous
} else {
Where(
obligation
.predicate
.rebind(args.as_coroutine_closure().upvar_tys().to_vec()),
)
}
ty::Binder::dummy(args.as_coroutine_closure().upvar_tys().to_vec())
}
ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {
// Fallback to whatever user-defined impls exist in this case.
None
}
ty::Infer(ty::TyVar(_)) => {
// Unbound type variable. Might or might not have
// applicable impls and so forth, depending on what
// those type variables wind up being bound to.
Ambiguous
}
// We can make this an ICE if/once we actually instantiate the trait obligation eagerly.
ty::Bound(..) => None,
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
ty::Foreign(..)
| ty::Str
| ty::Slice(_)
| ty::Dynamic(..)
| ty::Adt(..)
| ty::Alias(..)
| ty::Param(..)
| ty::Placeholder(..)
| ty::Bound(..)
| ty::Ref(_, _, ty::Mutability::Mut)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty);
}
}
}
fn fused_iterator_conditions(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
) -> BuiltinImplConditions<'tcx> {
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
if let ty::Coroutine(did, ..) = *self_ty.kind()
&& self.tcx().coroutine_is_gen(did)
{
BuiltinImplConditions::Where(ty::Binder::dummy(Vec::new()))
} else {
BuiltinImplConditions::None
}
fn coroutine_is_gen(&mut self, self_ty: Ty<'tcx>) -> bool {
matches!(*self_ty.kind(), ty::Coroutine(did, ..)
if self.tcx().coroutine_is_gen(did))
}
/// For default impls, we need to break apart a type into its
@@ -2886,23 +2805,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}
}
fn rebind_coroutine_witness_types<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
args: ty::GenericArgsRef<'tcx>,
bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
let bound_coroutine_types = tcx.coroutine_hidden_types(def_id).skip_binder();
let shifted_coroutine_types =
tcx.shift_bound_var_indices(bound_vars.len(), bound_coroutine_types.skip_binder());
ty::Binder::bind_with_vars(
ty::EarlyBinder::bind(shifted_coroutine_types.types.to_vec()).instantiate(tcx, args),
tcx.mk_bound_variable_kinds_from_iter(
bound_vars.iter().chain(bound_coroutine_types.bound_vars()),
),
)
}
impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> {
fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> {
TraitObligationStackList::with(self)