Add a builtin `FnPtr` trait

This commit is contained in:
lcnr 2022-07-20 14:32:58 +02:00 committed by Oli Scherer
parent 7a0600714a
commit 0c13565ca6
20 changed files with 310 additions and 27 deletions

View File

@ -382,6 +382,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
| 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)) =

View File

@ -166,6 +166,9 @@ language_item_table! {
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;

View File

@ -381,7 +381,8 @@ impl<'tcx> CodegenUnit<'tcx> {
| 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),

View File

@ -340,7 +340,8 @@ macro_rules! make_mir_visitor {
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));
}

View File

@ -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 @@ impl<'tcx> InstanceDef<'tcx> {
| 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 @@ impl<'tcx> InstanceDef<'tcx> {
| InstanceDef::Intrinsic(..)
| InstanceDef::ClosureOnceShim { .. }
| InstanceDef::DropGlue(..)
| InstanceDef::CloneShim(..) => None,
| InstanceDef::CloneShim(..)
| InstanceDef::FnPtrAddrShim(..) => None,
}
}
@ -182,7 +191,8 @@ impl<'tcx> InstanceDef<'tcx> {
| 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 @@ impl<'tcx> InstanceDef<'tcx> {
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),
}
}

View File

@ -2284,12 +2284,12 @@ impl<'tcx> TyCtxt<'tcx> {
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 });
}
@ -2317,11 +2317,10 @@ impl<'tcx> TyCtxt<'tcx> {
};
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 {
@ -2405,7 +2404,8 @@ impl<'tcx> TyCtxt<'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),
}
}

View File

@ -270,7 +270,8 @@ impl<'tcx> Inliner<'tcx> {
| InstanceDef::FnPtrShim(..)
| InstanceDef::ClosureOnceShim { .. }
| InstanceDef::DropGlue(..)
| InstanceDef::CloneShim(..) => return Ok(()),
| InstanceDef::CloneShim(..)
| InstanceDef::FnPtrAddrShim(..) => return Ok(()),
}
if self.tcx.is_constructor(callee_def_id) {

View File

@ -84,6 +84,9 @@ pub(crate) fn mir_callgraph_reachable<'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

View File

@ -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)
}

View File

@ -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));
}
}

View File

@ -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

View File

@ -722,6 +722,8 @@ symbols! {
fn_mut,
fn_once,
fn_once_output,
fn_ptr_addr,
fn_ptr_trait,
forbid,
forget,
format,

View File

@ -153,6 +153,12 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
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 @@ impl<'tcx> EvalCtxt<'_, '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) {

View File

@ -261,6 +261,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
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>,

View File

@ -222,9 +222,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
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 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
}
}
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>,

View File

@ -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.

View File

@ -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 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
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 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
/// 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 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
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 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
);
}
/// 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 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
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;
}
}
}
}

View File

@ -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
}

View File

@ -922,3 +922,18 @@ mod copy_impls {
#[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 {
/// Returns the address of the function pointer.
#[lang = "fn_ptr_addr"]
fn addr(self) -> *const ();
}

View 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() {}