Auto merge of #108080 - oli-obk:FnPtr-trait, r=lcnr
Add a builtin `FnPtr` trait that is implemented for all function pointers r? `@ghost` Rebased version of https://github.com/rust-lang/rust/pull/99531 (plus adjustments mentioned in the PR). If perf is happy with this version, I would like to land it, even if the diagnostics fix in 9df8e1befb5031a5bf9d8dfe25170620642d3c59 only works for `FnPtr` specifically, and does not generally improve blanket impls.
This commit is contained in:
commit
bf57e8ada6
@ -382,6 +382,7 @@ pub(crate) fn eval_fn_call(
|
||||
| ty::InstanceDef::FnPtrShim(..)
|
||||
| ty::InstanceDef::DropGlue(..)
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
| ty::InstanceDef::FnPtrAddrShim(..)
|
||||
| ty::InstanceDef::Item(_) => {
|
||||
// We need MIR for this fn
|
||||
let Some((body, instance)) =
|
||||
|
@ -166,6 +166,9 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
|
||||
|
||||
Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
|
||||
FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
FnPtrAddr, sym::fn_ptr_addr, fn_ptr_addr, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
|
||||
Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None;
|
||||
Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
|
||||
|
||||
|
@ -381,7 +381,8 @@ fn item_sort_key<'tcx>(tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>) -> ItemSortKey<'
|
||||
| InstanceDef::Virtual(..)
|
||||
| InstanceDef::ClosureOnceShim { .. }
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::CloneShim(..) => None,
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..) => None,
|
||||
}
|
||||
}
|
||||
MonoItem::Static(def_id) => def_id.as_local().map(Idx::index),
|
||||
|
@ -340,7 +340,8 @@ fn super_source_scope_data(
|
||||
|
||||
ty::InstanceDef::FnPtrShim(_def_id, ty) |
|
||||
ty::InstanceDef::DropGlue(_def_id, Some(ty)) |
|
||||
ty::InstanceDef::CloneShim(_def_id, ty) => {
|
||||
ty::InstanceDef::CloneShim(_def_id, ty) |
|
||||
ty::InstanceDef::FnPtrAddrShim(_def_id, ty) => {
|
||||
// FIXME(eddyb) use a better `TyContext` here.
|
||||
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
|
||||
}
|
||||
|
@ -96,6 +96,13 @@ pub enum InstanceDef<'tcx> {
|
||||
///
|
||||
/// The `DefId` is for `Clone::clone`, the `Ty` is the type `T` with the builtin `Clone` impl.
|
||||
CloneShim(DefId, Ty<'tcx>),
|
||||
|
||||
/// Compiler-generated `<T as FnPtr>::addr` implementation.
|
||||
///
|
||||
/// Automatically generated for all potentially higher-ranked `fn(I) -> R` types.
|
||||
///
|
||||
/// The `DefId` is for `FnPtr::addr`, the `Ty` is the type `T`.
|
||||
FnPtrAddrShim(DefId, Ty<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> Instance<'tcx> {
|
||||
@ -151,7 +158,8 @@ pub fn def_id(self) -> DefId {
|
||||
| InstanceDef::Intrinsic(def_id)
|
||||
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
|
||||
| InstanceDef::DropGlue(def_id, _)
|
||||
| InstanceDef::CloneShim(def_id, _) => def_id,
|
||||
| InstanceDef::CloneShim(def_id, _)
|
||||
| InstanceDef::FnPtrAddrShim(def_id, _) => def_id,
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,7 +175,8 @@ pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option<DefId> {
|
||||
| InstanceDef::Intrinsic(..)
|
||||
| InstanceDef::ClosureOnceShim { .. }
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::CloneShim(..) => None,
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,7 +191,8 @@ pub fn with_opt_param(self) -> ty::WithOptConstParam<DefId> {
|
||||
| InstanceDef::Intrinsic(def_id)
|
||||
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
|
||||
| InstanceDef::DropGlue(def_id, _)
|
||||
| InstanceDef::CloneShim(def_id, _) => ty::WithOptConstParam::unknown(def_id),
|
||||
| InstanceDef::CloneShim(def_id, _)
|
||||
| InstanceDef::FnPtrAddrShim(def_id, _) => ty::WithOptConstParam::unknown(def_id),
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,6 +278,7 @@ pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool {
|
||||
pub fn has_polymorphic_mir_body(&self) -> bool {
|
||||
match *self {
|
||||
InstanceDef::CloneShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..)
|
||||
| InstanceDef::FnPtrShim(..)
|
||||
| InstanceDef::DropGlue(_, Some(_)) => false,
|
||||
InstanceDef::ClosureOnceShim { .. }
|
||||
@ -306,6 +317,7 @@ fn fmt_instance(
|
||||
InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"),
|
||||
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty),
|
||||
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty),
|
||||
InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({})", ty),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2279,17 +2279,18 @@ pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option<usiz
|
||||
|
||||
/// Returns `true` if the impls are the same polarity and the trait either
|
||||
/// has no items or is annotated `#[marker]` and prevents item overrides.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
pub fn impls_are_allowed_to_overlap(
|
||||
self,
|
||||
def_id1: DefId,
|
||||
def_id2: DefId,
|
||||
) -> Option<ImplOverlapKind> {
|
||||
let impl_trait_ref1 = self.impl_trait_ref(def_id1);
|
||||
let impl_trait_ref2 = self.impl_trait_ref(def_id2);
|
||||
// If either trait impl references an error, they're allowed to overlap,
|
||||
// as one of them essentially doesn't exist.
|
||||
if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.subst_identity().references_error())
|
||||
|| self
|
||||
.impl_trait_ref(def_id2)
|
||||
.map_or(false, |tr| tr.subst_identity().references_error())
|
||||
if impl_trait_ref1.map_or(false, |tr| tr.subst_identity().references_error())
|
||||
|| impl_trait_ref2.map_or(false, |tr| tr.subst_identity().references_error())
|
||||
{
|
||||
return Some(ImplOverlapKind::Permitted { marker: false });
|
||||
}
|
||||
@ -2297,19 +2298,11 @@ pub fn impls_are_allowed_to_overlap(
|
||||
match (self.impl_polarity(def_id1), self.impl_polarity(def_id2)) {
|
||||
(ImplPolarity::Reservation, _) | (_, ImplPolarity::Reservation) => {
|
||||
// `#[rustc_reservation_impl]` impls don't overlap with anything
|
||||
debug!(
|
||||
"impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (reservations)",
|
||||
def_id1, def_id2
|
||||
);
|
||||
return Some(ImplOverlapKind::Permitted { marker: false });
|
||||
}
|
||||
(ImplPolarity::Positive, ImplPolarity::Negative)
|
||||
| (ImplPolarity::Negative, ImplPolarity::Positive) => {
|
||||
// `impl AutoTrait for Type` + `impl !AutoTrait for Type`
|
||||
debug!(
|
||||
"impls_are_allowed_to_overlap({:?}, {:?}) - None (differing polarities)",
|
||||
def_id1, def_id2
|
||||
);
|
||||
return None;
|
||||
}
|
||||
(ImplPolarity::Positive, ImplPolarity::Positive)
|
||||
@ -2317,38 +2310,25 @@ pub fn impls_are_allowed_to_overlap(
|
||||
};
|
||||
|
||||
let is_marker_overlap = {
|
||||
let is_marker_impl = |def_id: DefId| -> bool {
|
||||
let trait_ref = self.impl_trait_ref(def_id);
|
||||
let is_marker_impl = |trait_ref: Option<EarlyBinder<TraitRef<'_>>>| -> bool {
|
||||
trait_ref.map_or(false, |tr| self.trait_def(tr.skip_binder().def_id).is_marker)
|
||||
};
|
||||
is_marker_impl(def_id1) && is_marker_impl(def_id2)
|
||||
is_marker_impl(impl_trait_ref1) && is_marker_impl(impl_trait_ref2)
|
||||
};
|
||||
|
||||
if is_marker_overlap {
|
||||
debug!(
|
||||
"impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (marker overlap)",
|
||||
def_id1, def_id2
|
||||
);
|
||||
Some(ImplOverlapKind::Permitted { marker: true })
|
||||
} else {
|
||||
if let Some(self_ty1) = self.issue33140_self_ty(def_id1) {
|
||||
if let Some(self_ty2) = self.issue33140_self_ty(def_id2) {
|
||||
if self_ty1 == self_ty2 {
|
||||
debug!(
|
||||
"impls_are_allowed_to_overlap({:?}, {:?}) - issue #33140 HACK",
|
||||
def_id1, def_id2
|
||||
);
|
||||
return Some(ImplOverlapKind::Issue33140);
|
||||
} else {
|
||||
debug!(
|
||||
"impls_are_allowed_to_overlap({:?}, {:?}) - found {:?} != {:?}",
|
||||
def_id1, def_id2, self_ty1, self_ty2
|
||||
);
|
||||
debug!("found {self_ty1:?} != {self_ty2:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("impls_are_allowed_to_overlap({:?}, {:?}) = None", def_id1, def_id2);
|
||||
None
|
||||
}
|
||||
}
|
||||
@ -2405,7 +2385,8 @@ pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> {
|
||||
| ty::InstanceDef::Virtual(..)
|
||||
| ty::InstanceDef::ClosureOnceShim { .. }
|
||||
| ty::InstanceDef::DropGlue(..)
|
||||
| ty::InstanceDef::CloneShim(..) => self.mir_shims(instance),
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
| ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,7 +270,8 @@ fn check_mir_is_available(
|
||||
| InstanceDef::FnPtrShim(..)
|
||||
| InstanceDef::ClosureOnceShim { .. }
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::CloneShim(..) => return Ok(()),
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..) => return Ok(()),
|
||||
}
|
||||
|
||||
if self.tcx.is_constructor(callee_def_id) {
|
||||
|
@ -84,6 +84,9 @@ fn process<'tcx>(
|
||||
| InstanceDef::FnPtrShim(..)
|
||||
| InstanceDef::ClosureOnceShim { .. }
|
||||
| InstanceDef::CloneShim(..) => {}
|
||||
|
||||
// This shim does not call any other functions, thus there can be no recursion.
|
||||
InstanceDef::FnPtrAddrShim(..) => continue,
|
||||
InstanceDef::DropGlue(..) => {
|
||||
// FIXME: A not fully substituted drop shim can cause ICEs if one attempts to
|
||||
// have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
|
||||
|
@ -77,6 +77,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
|
||||
build_drop_shim(tcx, def_id, ty)
|
||||
}
|
||||
ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty),
|
||||
ty::InstanceDef::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty),
|
||||
ty::InstanceDef::Virtual(..) => {
|
||||
bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance)
|
||||
}
|
||||
@ -861,3 +862,39 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> {
|
||||
|
||||
body
|
||||
}
|
||||
|
||||
/// ```ignore (pseudo-impl)
|
||||
/// impl FnPtr for fn(u32) {
|
||||
/// fn addr(self) -> usize {
|
||||
/// self as usize
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> {
|
||||
assert!(matches!(self_ty.kind(), ty::FnPtr(..)), "expected fn ptr, found {self_ty}");
|
||||
let span = tcx.def_span(def_id);
|
||||
let Some(sig) = tcx.fn_sig(def_id).subst(tcx, &[self_ty.into()]).no_bound_vars() else {
|
||||
span_bug!(span, "FnPtr::addr with bound vars for `{self_ty}`");
|
||||
};
|
||||
let locals = local_decls_for_sig(&sig, span);
|
||||
|
||||
let source_info = SourceInfo::outermost(span);
|
||||
// FIXME: use `expose_addr` once we figure out whether function pointers have meaningful provenance.
|
||||
let rvalue = Rvalue::Cast(
|
||||
CastKind::FnPtrToPtr,
|
||||
Operand::Move(Place::from(Local::new(1))),
|
||||
tcx.mk_imm_ptr(tcx.types.unit),
|
||||
);
|
||||
let stmt = Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((Place::return_place(), rvalue))),
|
||||
};
|
||||
let statements = vec![stmt];
|
||||
let start_block = BasicBlockData {
|
||||
statements,
|
||||
terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }),
|
||||
is_cleanup: false,
|
||||
};
|
||||
let source = MirSource::from_instance(ty::InstanceDef::FnPtrAddrShim(def_id, self_ty));
|
||||
new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span)
|
||||
}
|
||||
|
@ -974,7 +974,8 @@ fn visit_instance_use<'tcx>(
|
||||
| ty::InstanceDef::ClosureOnceShim { .. }
|
||||
| ty::InstanceDef::Item(..)
|
||||
| ty::InstanceDef::FnPtrShim(..)
|
||||
| ty::InstanceDef::CloneShim(..) => {
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
| ty::InstanceDef::FnPtrAddrShim(..) => {
|
||||
output.push(create_fn_mono_item(tcx, instance, source));
|
||||
}
|
||||
}
|
||||
|
@ -278,7 +278,8 @@ fn characteristic_def_id_of_mono_item<'tcx>(
|
||||
| ty::InstanceDef::Intrinsic(..)
|
||||
| ty::InstanceDef::DropGlue(..)
|
||||
| ty::InstanceDef::Virtual(..)
|
||||
| ty::InstanceDef::CloneShim(..) => return None,
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
| ty::InstanceDef::FnPtrAddrShim(..) => return None,
|
||||
};
|
||||
|
||||
// If this is a method, we want to put it into the same module as
|
||||
@ -432,7 +433,8 @@ fn mono_item_visibility<'tcx>(
|
||||
| InstanceDef::Intrinsic(..)
|
||||
| InstanceDef::ClosureOnceShim { .. }
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::CloneShim(..) => return Visibility::Hidden,
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..) => return Visibility::Hidden,
|
||||
};
|
||||
|
||||
// The `start_fn` lang item is actually a monomorphized instance of a
|
||||
|
@ -722,6 +722,8 @@
|
||||
fn_mut,
|
||||
fn_once,
|
||||
fn_once_output,
|
||||
fn_ptr_addr,
|
||||
fn_ptr_trait,
|
||||
forbid,
|
||||
forget,
|
||||
format,
|
||||
|
@ -153,6 +153,12 @@ fn consider_builtin_pointer_like_candidate(
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
// A type is a `FnPtr` if it is of `FnPtr` type.
|
||||
fn consider_builtin_fn_ptr_trait_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>`
|
||||
// family of traits where `A` is given by the signature of the type.
|
||||
fn consider_builtin_fn_trait_candidates(
|
||||
@ -331,6 +337,8 @@ fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>(
|
||||
G::consider_builtin_copy_clone_candidate(self, goal)
|
||||
} else if lang_items.pointer_like() == Some(trait_def_id) {
|
||||
G::consider_builtin_pointer_like_candidate(self, goal)
|
||||
} else if lang_items.fn_ptr_trait() == Some(trait_def_id) {
|
||||
G::consider_builtin_fn_ptr_trait_candidate(self, goal)
|
||||
} else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) {
|
||||
G::consider_builtin_fn_trait_candidates(self, goal, kind)
|
||||
} else if lang_items.tuple_trait() == Some(trait_def_id) {
|
||||
|
@ -261,6 +261,13 @@ fn consider_builtin_pointer_like_candidate(
|
||||
bug!("`PointerLike` does not have an associated type: {:?}", goal);
|
||||
}
|
||||
|
||||
fn consider_builtin_fn_ptr_trait_candidate(
|
||||
_ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
bug!("`FnPtr` does not have an associated type: {:?}", goal);
|
||||
}
|
||||
|
||||
fn consider_builtin_fn_trait_candidates(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
|
@ -222,9 +222,8 @@ fn consider_builtin_pointer_like_candidate(
|
||||
let self_ty = tcx.erase_regions(goal.predicate.self_ty());
|
||||
|
||||
if let Ok(layout) = tcx.layout_of(goal.param_env.and(self_ty))
|
||||
&& let usize_layout = tcx.layout_of(ty::ParamEnv::empty().and(tcx.types.usize)).unwrap().layout
|
||||
&& layout.layout.size() == usize_layout.size()
|
||||
&& layout.layout.align().abi == usize_layout.align().abi
|
||||
&& layout.layout.size() == tcx.data_layout.pointer_size
|
||||
&& layout.layout.align().abi == tcx.data_layout.pointer_align.abi
|
||||
{
|
||||
// FIXME: We could make this faster by making a no-constraints response
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
@ -233,6 +232,17 @@ fn consider_builtin_pointer_like_candidate(
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_builtin_fn_ptr_trait_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if let ty::FnPtr(..) = goal.predicate.self_ty().kind() {
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_builtin_fn_trait_candidates(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
|
@ -411,11 +411,24 @@ fn resolve_negative_obligation<'tcx>(
|
||||
infcx.resolve_regions(&outlives_env).is_empty()
|
||||
}
|
||||
|
||||
/// Returns whether all impls which would apply to the `trait_ref`
|
||||
/// e.g. `Ty: Trait<Arg>` are already known in the local crate.
|
||||
///
|
||||
/// This both checks whether any downstream or sibling crates could
|
||||
/// implement it and whether an upstream crate can add this impl
|
||||
/// without breaking backwards compatibility.
|
||||
#[instrument(level = "debug", skip(tcx), ret)]
|
||||
pub fn trait_ref_is_knowable<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_ref: ty::TraitRef<'tcx>,
|
||||
) -> Result<(), Conflict> {
|
||||
if Some(trait_ref.def_id) == tcx.lang_items().fn_ptr_trait() {
|
||||
// The only types implementing `FnPtr` are function pointers,
|
||||
// so if there's no impl of `FnPtr` in the current crate,
|
||||
// then such an impl will never be added in the future.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if orphan_check_trait_ref(trait_ref, InCrate::Remote).is_ok() {
|
||||
// A downstream or cousin crate is allowed to implement some
|
||||
// substitution of this trait-ref.
|
||||
|
@ -5,6 +5,8 @@
|
||||
//! candidates. See the [rustc dev guide] for more details.
|
||||
//!
|
||||
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
|
||||
|
||||
use hir::def_id::DefId;
|
||||
use hir::LangItem;
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
@ -96,6 +98,8 @@ pub(super) fn assemble_candidates<'o>(
|
||||
self.assemble_candidate_for_tuple(obligation, &mut candidates);
|
||||
} else if lang_items.pointer_like() == Some(def_id) {
|
||||
self.assemble_candidate_for_ptr_sized(obligation, &mut candidates);
|
||||
} else if lang_items.fn_ptr_trait() == Some(def_id) {
|
||||
self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates);
|
||||
} else {
|
||||
if lang_items.clone_trait() == Some(def_id) {
|
||||
// Same builtin conditions as `Copy`, i.e., every type which has builtin support
|
||||
@ -321,13 +325,12 @@ fn assemble_fn_pointer_candidates(
|
||||
}
|
||||
|
||||
/// Searches for impls that might apply to `obligation`.
|
||||
#[instrument(level = "debug", skip(self, candidates))]
|
||||
fn assemble_candidates_from_impls(
|
||||
&mut self,
|
||||
obligation: &TraitObligation<'tcx>,
|
||||
candidates: &mut SelectionCandidateSet<'tcx>,
|
||||
) {
|
||||
debug!(?obligation, "assemble_candidates_from_impls");
|
||||
|
||||
// Essentially any user-written impl will match with an error type,
|
||||
// so creating `ImplCandidates` isn't useful. However, we might
|
||||
// end up finding a candidate elsewhere (e.g. a `BuiltinCandidate` for `Sized`)
|
||||
@ -352,6 +355,13 @@ fn assemble_candidates_from_impls(
|
||||
if self.fast_reject_trait_refs(obligation, &impl_trait_ref.0) {
|
||||
return;
|
||||
}
|
||||
if self.reject_fn_ptr_impls(
|
||||
impl_def_id,
|
||||
obligation,
|
||||
impl_trait_ref.skip_binder().self_ty(),
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.infcx.probe(|_| {
|
||||
if let Ok(_substs) = self.match_impl(impl_def_id, impl_trait_ref, obligation) {
|
||||
@ -362,6 +372,99 @@ fn assemble_candidates_from_impls(
|
||||
);
|
||||
}
|
||||
|
||||
/// The various `impl<T: FnPtr> Trait for T` in libcore are more like builtin impls for all function items
|
||||
/// and function pointers and less like blanket impls. Rejecting them when they can't possibly apply (because
|
||||
/// the obligation's self-type does not implement `FnPtr`) avoids reporting that the self type does not implement
|
||||
/// `FnPtr`, when we wanted to report that it doesn't implement `Trait`.
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn reject_fn_ptr_impls(
|
||||
&self,
|
||||
impl_def_id: DefId,
|
||||
obligation: &TraitObligation<'tcx>,
|
||||
impl_self_ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
// Let `impl<T: FnPtr> Trait for Vec<T>` go through the normal rejection path.
|
||||
if !matches!(impl_self_ty.kind(), ty::Param(..)) {
|
||||
return false;
|
||||
}
|
||||
let Some(fn_ptr_trait) = self.tcx().lang_items().fn_ptr_trait() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
for &(predicate, _) in self.tcx().predicates_of(impl_def_id).predicates {
|
||||
let ty::PredicateKind::Clause(ty::Clause::Trait(pred))
|
||||
= predicate.kind().skip_binder() else { continue };
|
||||
if fn_ptr_trait != pred.trait_ref.def_id {
|
||||
continue;
|
||||
}
|
||||
trace!(?pred);
|
||||
// Not the bound we're looking for
|
||||
if pred.self_ty() != impl_self_ty {
|
||||
continue;
|
||||
}
|
||||
|
||||
match obligation.self_ty().skip_binder().kind() {
|
||||
// Fast path to avoid evaluating an obligation that trivally holds.
|
||||
// There may be more bounds, but these are checked by the regular path.
|
||||
ty::FnPtr(..) => return false,
|
||||
// These may potentially implement `FnPtr`
|
||||
ty::Placeholder(..)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Alias(_, _)
|
||||
| ty::Infer(_)
|
||||
| ty::Param(..) => {}
|
||||
|
||||
ty::Bound(_, _) => span_bug!(
|
||||
obligation.cause.span(),
|
||||
"cannot have escaping bound var in self type of {obligation:#?}"
|
||||
),
|
||||
// These can't possibly implement `FnPtr` as they are concrete types
|
||||
// and not `FnPtr`
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Adt(_, _)
|
||||
| ty::Foreign(_)
|
||||
| ty::Str
|
||||
| ty::Array(_, _)
|
||||
| ty::Slice(_)
|
||||
| ty::RawPtr(_)
|
||||
| ty::Ref(_, _, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::Generator(_, _, _)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(_, _)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Error(_) => return true,
|
||||
// FIXME: Function definitions could actually implement `FnPtr` by
|
||||
// casting the ZST function def to a function pointer.
|
||||
ty::FnDef(_, _) => return true,
|
||||
}
|
||||
|
||||
// Generic params can implement `FnPtr` if the predicate
|
||||
// holds within its own environment.
|
||||
let obligation = Obligation::new(
|
||||
self.tcx(),
|
||||
obligation.cause.clone(),
|
||||
obligation.param_env,
|
||||
self.tcx().mk_predicate(obligation.predicate.map_bound(|mut pred| {
|
||||
pred.trait_ref =
|
||||
self.tcx().mk_trait_ref(fn_ptr_trait, [pred.trait_ref.self_ty()]);
|
||||
ty::PredicateKind::Clause(ty::Clause::Trait(pred))
|
||||
})),
|
||||
);
|
||||
if let Ok(r) = self.infcx.evaluate_obligation(&obligation) {
|
||||
if !r.may_apply() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn assemble_candidates_from_auto_impls(
|
||||
&mut self,
|
||||
obligation: &TraitObligation<'tcx>,
|
||||
@ -853,13 +956,50 @@ fn assemble_candidate_for_ptr_sized(
|
||||
return;
|
||||
}
|
||||
|
||||
let usize_layout =
|
||||
self.tcx().layout_of(ty::ParamEnv::empty().and(self.tcx().types.usize)).unwrap().layout;
|
||||
if let Ok(layout) = self.tcx().layout_of(obligation.param_env.and(self_ty))
|
||||
&& layout.layout.size() == usize_layout.size()
|
||||
&& layout.layout.align().abi == usize_layout.align().abi
|
||||
&& layout.layout.size() == self.tcx().data_layout.pointer_size
|
||||
&& layout.layout.align().abi == self.tcx().data_layout.pointer_align.abi
|
||||
{
|
||||
candidates.vec.push(BuiltinCandidate { has_nested: false });
|
||||
}
|
||||
}
|
||||
|
||||
fn assemble_candidates_for_fn_ptr_trait(
|
||||
&mut self,
|
||||
obligation: &TraitObligation<'tcx>,
|
||||
candidates: &mut SelectionCandidateSet<'tcx>,
|
||||
) {
|
||||
let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
|
||||
match self_ty.skip_binder().kind() {
|
||||
ty::FnPtr(_) => candidates.vec.push(BuiltinCandidate { has_nested: false }),
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Adt(..)
|
||||
| ty::Foreign(..)
|
||||
| ty::Str
|
||||
| ty::Array(..)
|
||||
| ty::Slice(_)
|
||||
| ty::RawPtr(_)
|
||||
| ty::Ref(..)
|
||||
| ty::FnDef(..)
|
||||
| ty::Placeholder(..)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(..)
|
||||
| ty::Alias(..)
|
||||
| ty::Param(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Error(_) => {}
|
||||
ty::Infer(_) => {
|
||||
candidates.ambiguous = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ pub struct FutureCompatOverlapError<'tcx> {
|
||||
}
|
||||
|
||||
/// The result of attempting to insert an impl into a group of children.
|
||||
#[derive(Debug)]
|
||||
enum Inserted<'tcx> {
|
||||
/// The impl was inserted as a new child in this group of children.
|
||||
BecameNewSibling(Option<FutureCompatOverlapError<'tcx>>),
|
||||
@ -82,6 +83,7 @@ fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
|
||||
|
||||
/// Attempt to insert an impl into this set of children, while comparing for
|
||||
/// specialization relationships.
|
||||
#[instrument(level = "debug", skip(self, tcx), ret)]
|
||||
fn insert(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
@ -92,18 +94,13 @@ fn insert(
|
||||
let mut last_lint = None;
|
||||
let mut replace_children = Vec::new();
|
||||
|
||||
debug!("insert(impl_def_id={:?}, simplified_self={:?})", impl_def_id, simplified_self,);
|
||||
|
||||
let possible_siblings = match simplified_self {
|
||||
Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)),
|
||||
None => PotentialSiblings::Unfiltered(iter_children(self)),
|
||||
};
|
||||
|
||||
for possible_sibling in possible_siblings {
|
||||
debug!(
|
||||
"insert: impl_def_id={:?}, simplified_self={:?}, possible_sibling={:?}",
|
||||
impl_def_id, simplified_self, possible_sibling,
|
||||
);
|
||||
debug!(?possible_sibling);
|
||||
|
||||
let create_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>| {
|
||||
let trait_ref = overlap.impl_header.trait_ref.unwrap();
|
||||
|
@ -243,7 +243,8 @@ fn resolve_associated_item<'tcx>(
|
||||
}
|
||||
}
|
||||
traits::ImplSource::Builtin(..) => {
|
||||
if Some(trait_ref.def_id) == tcx.lang_items().clone_trait() {
|
||||
let lang_items = tcx.lang_items();
|
||||
if Some(trait_ref.def_id) == lang_items.clone_trait() {
|
||||
// FIXME(eddyb) use lang items for methods instead of names.
|
||||
let name = tcx.item_name(trait_item_id);
|
||||
if name == sym::clone {
|
||||
@ -270,6 +271,22 @@ fn resolve_associated_item<'tcx>(
|
||||
let substs = tcx.erase_regions(rcvr_substs);
|
||||
Some(ty::Instance::new(trait_item_id, substs))
|
||||
}
|
||||
} else if Some(trait_ref.def_id) == lang_items.fn_ptr_trait() {
|
||||
if lang_items.fn_ptr_addr() == Some(trait_item_id) {
|
||||
let self_ty = trait_ref.self_ty();
|
||||
if !matches!(self_ty.kind(), ty::FnPtr(..)) {
|
||||
return Ok(None);
|
||||
}
|
||||
Some(Instance {
|
||||
def: ty::InstanceDef::FnPtrAddrShim(trait_item_id, self_ty),
|
||||
substs: rcvr_substs,
|
||||
})
|
||||
} else {
|
||||
tcx.sess.span_fatal(
|
||||
tcx.def_span(trait_item_id),
|
||||
"`FnPtrAddr` trait with unexpected assoc item",
|
||||
)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -922,3 +922,18 @@ impl<T: ?Sized> Copy for *mut T {}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: ?Sized> Copy for &T {}
|
||||
}
|
||||
|
||||
/// A common trait implemented by all function pointers.
|
||||
#[unstable(
|
||||
feature = "fn_ptr_trait",
|
||||
issue = "none",
|
||||
reason = "internal trait for implementing various traits for all function pointers"
|
||||
)]
|
||||
#[lang = "fn_ptr_trait"]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[rustc_deny_explicit_impl]
|
||||
pub trait FnPtr: Copy + Clone {
|
||||
/// Returns the address of the function pointer.
|
||||
#[lang = "fn_ptr_addr"]
|
||||
fn addr(self) -> *const ();
|
||||
}
|
||||
|
@ -1891,9 +1891,12 @@ pub fn hash<T: ?Sized, S: hash::Hasher>(hashee: *const T, into: &mut S) {
|
||||
hashee.hash(into);
|
||||
}
|
||||
|
||||
// If this is a unary fn pointer, it adds a doc comment.
|
||||
// Otherwise, it hides the docs entirely.
|
||||
macro_rules! maybe_fnptr_doc {
|
||||
#[cfg(bootstrap)]
|
||||
mod old_fn_ptr_impl {
|
||||
use super::*;
|
||||
// If this is a unary fn pointer, it adds a doc comment.
|
||||
// Otherwise, it hides the docs entirely.
|
||||
macro_rules! maybe_fnptr_doc {
|
||||
(@ #[$meta:meta] $item:item) => {
|
||||
#[doc(hidden)]
|
||||
#[$meta]
|
||||
@ -1910,15 +1913,15 @@ macro_rules! maybe_fnptr_doc {
|
||||
#[$meta]
|
||||
$item
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(strict_provenance_magic): function pointers have buggy codegen that
|
||||
// necessitates casting to a usize to get the backend to do the right thing.
|
||||
// for now I will break AVR to silence *a billion* lints. We should probably
|
||||
// have a proper "opaque function pointer type" to handle this kind of thing.
|
||||
// FIXME(strict_provenance_magic): function pointers have buggy codegen that
|
||||
// necessitates casting to a usize to get the backend to do the right thing.
|
||||
// for now I will break AVR to silence *a billion* lints. We should probably
|
||||
// have a proper "opaque function pointer type" to handle this kind of thing.
|
||||
|
||||
// Impls for function pointers
|
||||
macro_rules! fnptr_impls_safety_abi {
|
||||
// Impls for function pointers
|
||||
macro_rules! fnptr_impls_safety_abi {
|
||||
($FnTy: ty, $($Arg: ident),*) => {
|
||||
fnptr_impls_safety_abi! { #[stable(feature = "fnptr_impls", since = "1.4.0")] $FnTy, $($Arg),* }
|
||||
};
|
||||
@ -1995,9 +1998,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! fnptr_impls_args {
|
||||
macro_rules! fnptr_impls_args {
|
||||
($($Arg: ident),+) => {
|
||||
fnptr_impls_safety_abi! { extern "Rust" fn($($Arg),+) -> Ret, $($Arg),+ }
|
||||
fnptr_impls_safety_abi! { extern "C" fn($($Arg),+) -> Ret, $($Arg),+ }
|
||||
@ -2019,22 +2022,74 @@ macro_rules! fnptr_impls_args {
|
||||
fnptr_impls_safety_abi! { unsafe extern "C" fn() -> Ret, }
|
||||
fnptr_impls_safety_abi! { @c_unwind unsafe extern "C-unwind" fn() -> Ret, }
|
||||
};
|
||||
}
|
||||
|
||||
fnptr_impls_args! {}
|
||||
fnptr_impls_args! { T }
|
||||
fnptr_impls_args! { A, B }
|
||||
fnptr_impls_args! { A, B, C }
|
||||
fnptr_impls_args! { A, B, C, D }
|
||||
fnptr_impls_args! { A, B, C, D, E }
|
||||
fnptr_impls_args! { A, B, C, D, E, F }
|
||||
fnptr_impls_args! { A, B, C, D, E, F, G }
|
||||
fnptr_impls_args! { A, B, C, D, E, F, G, H }
|
||||
fnptr_impls_args! { A, B, C, D, E, F, G, H, I }
|
||||
fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J }
|
||||
fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K }
|
||||
fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L }
|
||||
}
|
||||
|
||||
fnptr_impls_args! {}
|
||||
fnptr_impls_args! { T }
|
||||
fnptr_impls_args! { A, B }
|
||||
fnptr_impls_args! { A, B, C }
|
||||
fnptr_impls_args! { A, B, C, D }
|
||||
fnptr_impls_args! { A, B, C, D, E }
|
||||
fnptr_impls_args! { A, B, C, D, E, F }
|
||||
fnptr_impls_args! { A, B, C, D, E, F, G }
|
||||
fnptr_impls_args! { A, B, C, D, E, F, G, H }
|
||||
fnptr_impls_args! { A, B, C, D, E, F, G, H, I }
|
||||
fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J }
|
||||
fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K }
|
||||
fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L }
|
||||
#[cfg(not(bootstrap))]
|
||||
mod new_fn_ptr_impl {
|
||||
use super::*;
|
||||
use crate::marker::FnPtr;
|
||||
|
||||
#[stable(feature = "fnptr_impls", since = "1.4.0")]
|
||||
impl<F: FnPtr> PartialEq for F {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.addr() == other.addr()
|
||||
}
|
||||
}
|
||||
#[stable(feature = "fnptr_impls", since = "1.4.0")]
|
||||
impl<F: FnPtr> Eq for F {}
|
||||
|
||||
#[stable(feature = "fnptr_impls", since = "1.4.0")]
|
||||
impl<F: FnPtr> PartialOrd for F {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.addr().partial_cmp(&other.addr())
|
||||
}
|
||||
}
|
||||
#[stable(feature = "fnptr_impls", since = "1.4.0")]
|
||||
impl<F: FnPtr> Ord for F {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.addr().cmp(&other.addr())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "fnptr_impls", since = "1.4.0")]
|
||||
impl<F: FnPtr> hash::Hash for F {
|
||||
fn hash<HH: hash::Hasher>(&self, state: &mut HH) {
|
||||
state.write_usize(self.addr() as _)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "fnptr_impls", since = "1.4.0")]
|
||||
impl<F: FnPtr> fmt::Pointer for F {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::pointer_fmt_inner(self.addr() as _, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "fnptr_impls", since = "1.4.0")]
|
||||
impl<F: FnPtr> fmt::Debug for F {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::pointer_fmt_inner(self.addr() as _, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Create a `const` raw pointer to a place, without creating an intermediate reference.
|
||||
///
|
||||
/// Creating a reference with `&`/`&mut` is only allowed if the pointer is properly aligned
|
||||
|
9
tests/ui/fn/fn-ptr-trait.rs
Normal file
9
tests/ui/fn/fn-ptr-trait.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#![feature(fn_ptr_trait)]
|
||||
// check-pass
|
||||
|
||||
use std::marker::FnPtr;
|
||||
|
||||
trait Foo {}
|
||||
impl<T> Foo for Vec<T> where T: FnPtr {}
|
||||
|
||||
fn main() {}
|
@ -90,16 +90,6 @@ LL | assert_eq!(Foo::Bar, i);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ `fn(usize) -> Foo {Foo::Bar}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
||||
|
|
||||
= help: the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}`
|
||||
= help: the following other types implement trait `Debug`:
|
||||
extern "C" fn() -> Ret
|
||||
extern "C" fn(A, B) -> Ret
|
||||
extern "C" fn(A, B, ...) -> Ret
|
||||
extern "C" fn(A, B, C) -> Ret
|
||||
extern "C" fn(A, B, C, ...) -> Ret
|
||||
extern "C" fn(A, B, C, D) -> Ret
|
||||
extern "C" fn(A, B, C, D, ...) -> Ret
|
||||
extern "C" fn(A, B, C, D, E) -> Ret
|
||||
and 118 others
|
||||
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug`
|
||||
@ -109,16 +99,6 @@ LL | assert_eq!(Foo::Bar, i);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ `fn(usize) -> Foo {Foo::Bar}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
||||
|
|
||||
= help: the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}`
|
||||
= help: the following other types implement trait `Debug`:
|
||||
extern "C" fn() -> Ret
|
||||
extern "C" fn(A, B) -> Ret
|
||||
extern "C" fn(A, B, ...) -> Ret
|
||||
extern "C" fn(A, B, C) -> Ret
|
||||
extern "C" fn(A, B, C, ...) -> Ret
|
||||
extern "C" fn(A, B, C, D) -> Ret
|
||||
extern "C" fn(A, B, C, D, ...) -> Ret
|
||||
extern "C" fn(A, B, C, D, E) -> Ret
|
||||
and 118 others
|
||||
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user