Implement and use BikeshedGuaranteedNoDrop for union/unsafe field validity

This commit is contained in:
Michael Goulet 2025-02-06 20:08:29 +00:00
parent c72d443cdd
commit 516afd557c
21 changed files with 365 additions and 66 deletions

View file

@ -102,6 +102,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.assemble_candidate_for_tuple(obligation, &mut candidates);
} else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) {
self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates);
} else if tcx.is_lang_item(def_id, LangItem::BikeshedGuaranteedNoDrop) {
self.assemble_candidates_for_bikeshed_guaranteed_no_drop_trait(
obligation,
&mut candidates,
);
} else {
if tcx.is_lang_item(def_id, LangItem::Clone) {
// Same builtin conditions as `Copy`, i.e., every type which has builtin support
@ -1184,4 +1189,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
}
fn assemble_candidates_for_bikeshed_guaranteed_no_drop_trait(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
match obligation.predicate.self_ty().skip_binder().kind() {
ty::Ref(..)
| ty::Adt(..)
| ty::Tuple(_)
| ty::Array(..)
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::Error(_)
| ty::Uint(_)
| ty::Int(_)
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Bool
| ty::Float(_)
| ty::Char
| ty::RawPtr(..)
| ty::Never
| ty::Pat(..)
| ty::Dynamic(..)
| ty::Str
| ty::Slice(_)
| ty::Foreign(..)
| ty::Alias(..)
| ty::Param(_)
| ty::Placeholder(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::UnsafeBinder(_)
| ty::CoroutineWitness(..)
| ty::Bound(..) => {
candidates.vec.push(BikeshedGuaranteedNoDropCandidate);
}
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
candidates.ambiguous = true;
}
}
}
}

View file

@ -20,6 +20,7 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, Upcast};
use rustc_middle::{bug, span_bug};
use rustc_span::def_id::DefId;
use rustc_type_ir::elaborate;
use thin_vec::thin_vec;
use tracing::{debug, instrument};
use super::SelectionCandidate::{self, *};
@ -130,6 +131,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
TraitUpcastingUnsizeCandidate(idx) => {
self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?
}
BikeshedGuaranteedNoDropCandidate => {
self.confirm_bikeshed_guaranteed_no_drop_candidate(obligation)
}
};
// The obligations returned by confirmation are recursively evaluated
@ -1346,6 +1351,93 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
_ => bug!("source: {source}, target: {target}"),
})
}
fn confirm_bikeshed_guaranteed_no_drop_candidate(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
) -> ImplSource<'tcx, PredicateObligation<'tcx>> {
let mut obligations = thin_vec![];
let tcx = self.tcx();
let self_ty = obligation.predicate.self_ty();
match *self_ty.skip_binder().kind() {
// `&mut T` and `&T` always implement `BikeshedGuaranteedNoDrop`.
ty::Ref(..) => {}
// `ManuallyDrop<T>` always implements `BikeshedGuaranteedNoDrop`.
ty::Adt(def, _) if def.is_manually_drop() => {}
// Arrays and tuples implement `BikeshedGuaranteedNoDrop` only if
// their constituent types implement `BikeshedGuaranteedNoDrop`.
ty::Tuple(tys) => {
obligations.extend(tys.iter().map(|elem_ty| {
obligation.with(
tcx,
self_ty.rebind(ty::TraitRef::new(
tcx,
obligation.predicate.def_id(),
[elem_ty],
)),
)
}));
}
ty::Array(elem_ty, _) => {
obligations.push(obligation.with(
tcx,
self_ty.rebind(ty::TraitRef::new(
tcx,
obligation.predicate.def_id(),
[elem_ty],
)),
));
}
// All other types implement `BikeshedGuaranteedNoDrop` only if
// they implement `Copy`. We could be smart here and short-circuit
// some trivially `Copy`/`!Copy` types, but there's no benefit.
ty::FnDef(..)
| ty::FnPtr(..)
| ty::Error(_)
| ty::Uint(_)
| ty::Int(_)
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Bool
| ty::Float(_)
| ty::Char
| ty::RawPtr(..)
| ty::Never
| ty::Pat(..)
| ty::Dynamic(..)
| ty::Str
| ty::Slice(_)
| ty::Foreign(..)
| ty::Adt(..)
| ty::Alias(..)
| ty::Param(_)
| ty::Placeholder(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::UnsafeBinder(_)
| ty::CoroutineWitness(..)
| ty::Bound(..) => {
obligations.push(obligation.with(
tcx,
self_ty.map_bound(|ty| {
ty::TraitRef::new(
tcx,
tcx.require_lang_item(LangItem::Copy, Some(obligation.cause.span)),
[ty],
)
}),
));
}
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
panic!("unexpected type `{self_ty:?}`")
}
}
ImplSource::Builtin(BuiltinImplSource::Misc, obligations)
}
}
/// Compute a goal that some RPITIT (right now, only RPITITs corresponding to Futures)

View file

@ -1949,7 +1949,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| TraitAliasCandidate
| TraitUpcastingUnsizeCandidate(_)
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate => false,
| BuiltinUnsizeCandidate
| BikeshedGuaranteedNoDropCandidate => false,
// Non-global param candidates have already been handled, global
// where-bounds get ignored.
ParamCandidate(_) | ImplCandidate(_) => true,