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
23 changed files with 476 additions and 180 deletions
|
@ -382,6 +382,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
| ty::InstanceDef::FnPtrShim(..)
|
| ty::InstanceDef::FnPtrShim(..)
|
||||||
| ty::InstanceDef::DropGlue(..)
|
| ty::InstanceDef::DropGlue(..)
|
||||||
| ty::InstanceDef::CloneShim(..)
|
| ty::InstanceDef::CloneShim(..)
|
||||||
|
| ty::InstanceDef::FnPtrAddrShim(..)
|
||||||
| ty::InstanceDef::Item(_) => {
|
| ty::InstanceDef::Item(_) => {
|
||||||
// We need MIR for this fn
|
// We need MIR for this fn
|
||||||
let Some((body, instance)) =
|
let Some((body, instance)) =
|
||||||
|
|
|
@ -166,6 +166,9 @@ language_item_table! {
|
||||||
|
|
||||||
Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
|
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;
|
Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None;
|
||||||
Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
|
Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
|
||||||
|
|
||||||
|
|
|
@ -381,7 +381,8 @@ impl<'tcx> CodegenUnit<'tcx> {
|
||||||
| InstanceDef::Virtual(..)
|
| InstanceDef::Virtual(..)
|
||||||
| InstanceDef::ClosureOnceShim { .. }
|
| InstanceDef::ClosureOnceShim { .. }
|
||||||
| InstanceDef::DropGlue(..)
|
| InstanceDef::DropGlue(..)
|
||||||
| InstanceDef::CloneShim(..) => None,
|
| InstanceDef::CloneShim(..)
|
||||||
|
| InstanceDef::FnPtrAddrShim(..) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MonoItem::Static(def_id) => def_id.as_local().map(Idx::index),
|
MonoItem::Static(def_id) => def_id.as_local().map(Idx::index),
|
||||||
|
|
|
@ -340,7 +340,8 @@ macro_rules! make_mir_visitor {
|
||||||
|
|
||||||
ty::InstanceDef::FnPtrShim(_def_id, ty) |
|
ty::InstanceDef::FnPtrShim(_def_id, ty) |
|
||||||
ty::InstanceDef::DropGlue(_def_id, Some(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.
|
// FIXME(eddyb) use a better `TyContext` here.
|
||||||
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
|
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.
|
/// The `DefId` is for `Clone::clone`, the `Ty` is the type `T` with the builtin `Clone` impl.
|
||||||
CloneShim(DefId, Ty<'tcx>),
|
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> {
|
impl<'tcx> Instance<'tcx> {
|
||||||
|
@ -151,7 +158,8 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||||
| InstanceDef::Intrinsic(def_id)
|
| InstanceDef::Intrinsic(def_id)
|
||||||
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
|
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
|
||||||
| InstanceDef::DropGlue(def_id, _)
|
| 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::Intrinsic(..)
|
||||||
| InstanceDef::ClosureOnceShim { .. }
|
| InstanceDef::ClosureOnceShim { .. }
|
||||||
| InstanceDef::DropGlue(..)
|
| InstanceDef::DropGlue(..)
|
||||||
| InstanceDef::CloneShim(..) => None,
|
| InstanceDef::CloneShim(..)
|
||||||
|
| InstanceDef::FnPtrAddrShim(..) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +191,8 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||||
| InstanceDef::Intrinsic(def_id)
|
| InstanceDef::Intrinsic(def_id)
|
||||||
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
|
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
|
||||||
| InstanceDef::DropGlue(def_id, _)
|
| 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 {
|
pub fn has_polymorphic_mir_body(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
InstanceDef::CloneShim(..)
|
InstanceDef::CloneShim(..)
|
||||||
|
| InstanceDef::FnPtrAddrShim(..)
|
||||||
| InstanceDef::FnPtrShim(..)
|
| InstanceDef::FnPtrShim(..)
|
||||||
| InstanceDef::DropGlue(_, Some(_)) => false,
|
| InstanceDef::DropGlue(_, Some(_)) => false,
|
||||||
InstanceDef::ClosureOnceShim { .. }
|
InstanceDef::ClosureOnceShim { .. }
|
||||||
|
@ -306,6 +317,7 @@ fn fmt_instance(
|
||||||
InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"),
|
InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"),
|
||||||
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty),
|
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty),
|
||||||
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty),
|
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty),
|
||||||
|
InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({})", ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2279,17 +2279,18 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
|
|
||||||
/// Returns `true` if the impls are the same polarity and the trait either
|
/// Returns `true` if the impls are the same polarity and the trait either
|
||||||
/// has no items or is annotated `#[marker]` and prevents item overrides.
|
/// has no items or is annotated `#[marker]` and prevents item overrides.
|
||||||
|
#[instrument(level = "debug", skip(self), ret)]
|
||||||
pub fn impls_are_allowed_to_overlap(
|
pub fn impls_are_allowed_to_overlap(
|
||||||
self,
|
self,
|
||||||
def_id1: DefId,
|
def_id1: DefId,
|
||||||
def_id2: DefId,
|
def_id2: DefId,
|
||||||
) -> Option<ImplOverlapKind> {
|
) -> 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,
|
// If either trait impl references an error, they're allowed to overlap,
|
||||||
// as one of them essentially doesn't exist.
|
// as one of them essentially doesn't exist.
|
||||||
if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.subst_identity().references_error())
|
if impl_trait_ref1.map_or(false, |tr| tr.subst_identity().references_error())
|
||||||
|| self
|
|| impl_trait_ref2.map_or(false, |tr| tr.subst_identity().references_error())
|
||||||
.impl_trait_ref(def_id2)
|
|
||||||
.map_or(false, |tr| tr.subst_identity().references_error())
|
|
||||||
{
|
{
|
||||||
return Some(ImplOverlapKind::Permitted { marker: false });
|
return Some(ImplOverlapKind::Permitted { marker: false });
|
||||||
}
|
}
|
||||||
|
@ -2297,19 +2298,11 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
match (self.impl_polarity(def_id1), self.impl_polarity(def_id2)) {
|
match (self.impl_polarity(def_id1), self.impl_polarity(def_id2)) {
|
||||||
(ImplPolarity::Reservation, _) | (_, ImplPolarity::Reservation) => {
|
(ImplPolarity::Reservation, _) | (_, ImplPolarity::Reservation) => {
|
||||||
// `#[rustc_reservation_impl]` impls don't overlap with anything
|
// `#[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 });
|
return Some(ImplOverlapKind::Permitted { marker: false });
|
||||||
}
|
}
|
||||||
(ImplPolarity::Positive, ImplPolarity::Negative)
|
(ImplPolarity::Positive, ImplPolarity::Negative)
|
||||||
| (ImplPolarity::Negative, ImplPolarity::Positive) => {
|
| (ImplPolarity::Negative, ImplPolarity::Positive) => {
|
||||||
// `impl AutoTrait for Type` + `impl !AutoTrait for Type`
|
// `impl AutoTrait for Type` + `impl !AutoTrait for Type`
|
||||||
debug!(
|
|
||||||
"impls_are_allowed_to_overlap({:?}, {:?}) - None (differing polarities)",
|
|
||||||
def_id1, def_id2
|
|
||||||
);
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
(ImplPolarity::Positive, ImplPolarity::Positive)
|
(ImplPolarity::Positive, ImplPolarity::Positive)
|
||||||
|
@ -2317,38 +2310,25 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_marker_overlap = {
|
let is_marker_overlap = {
|
||||||
let is_marker_impl = |def_id: DefId| -> bool {
|
let is_marker_impl = |trait_ref: Option<EarlyBinder<TraitRef<'_>>>| -> bool {
|
||||||
let trait_ref = self.impl_trait_ref(def_id);
|
|
||||||
trait_ref.map_or(false, |tr| self.trait_def(tr.skip_binder().def_id).is_marker)
|
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 {
|
if is_marker_overlap {
|
||||||
debug!(
|
|
||||||
"impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (marker overlap)",
|
|
||||||
def_id1, def_id2
|
|
||||||
);
|
|
||||||
Some(ImplOverlapKind::Permitted { marker: true })
|
Some(ImplOverlapKind::Permitted { marker: true })
|
||||||
} else {
|
} else {
|
||||||
if let Some(self_ty1) = self.issue33140_self_ty(def_id1) {
|
if let Some(self_ty1) = self.issue33140_self_ty(def_id1) {
|
||||||
if let Some(self_ty2) = self.issue33140_self_ty(def_id2) {
|
if let Some(self_ty2) = self.issue33140_self_ty(def_id2) {
|
||||||
if self_ty1 == self_ty2 {
|
if self_ty1 == self_ty2 {
|
||||||
debug!(
|
|
||||||
"impls_are_allowed_to_overlap({:?}, {:?}) - issue #33140 HACK",
|
|
||||||
def_id1, def_id2
|
|
||||||
);
|
|
||||||
return Some(ImplOverlapKind::Issue33140);
|
return Some(ImplOverlapKind::Issue33140);
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!("found {self_ty1:?} != {self_ty2:?}");
|
||||||
"impls_are_allowed_to_overlap({:?}, {:?}) - found {:?} != {:?}",
|
|
||||||
def_id1, def_id2, self_ty1, self_ty2
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("impls_are_allowed_to_overlap({:?}, {:?}) = None", def_id1, def_id2);
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2405,7 +2385,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
| ty::InstanceDef::Virtual(..)
|
| ty::InstanceDef::Virtual(..)
|
||||||
| ty::InstanceDef::ClosureOnceShim { .. }
|
| ty::InstanceDef::ClosureOnceShim { .. }
|
||||||
| ty::InstanceDef::DropGlue(..)
|
| ty::InstanceDef::DropGlue(..)
|
||||||
| ty::InstanceDef::CloneShim(..) => self.mir_shims(instance),
|
| ty::InstanceDef::CloneShim(..)
|
||||||
|
| ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -270,7 +270,8 @@ impl<'tcx> Inliner<'tcx> {
|
||||||
| InstanceDef::FnPtrShim(..)
|
| InstanceDef::FnPtrShim(..)
|
||||||
| InstanceDef::ClosureOnceShim { .. }
|
| InstanceDef::ClosureOnceShim { .. }
|
||||||
| InstanceDef::DropGlue(..)
|
| InstanceDef::DropGlue(..)
|
||||||
| InstanceDef::CloneShim(..) => return Ok(()),
|
| InstanceDef::CloneShim(..)
|
||||||
|
| InstanceDef::FnPtrAddrShim(..) => return Ok(()),
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.tcx.is_constructor(callee_def_id) {
|
if self.tcx.is_constructor(callee_def_id) {
|
||||||
|
|
|
@ -84,6 +84,9 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
|
||||||
| InstanceDef::FnPtrShim(..)
|
| InstanceDef::FnPtrShim(..)
|
||||||
| InstanceDef::ClosureOnceShim { .. }
|
| InstanceDef::ClosureOnceShim { .. }
|
||||||
| InstanceDef::CloneShim(..) => {}
|
| InstanceDef::CloneShim(..) => {}
|
||||||
|
|
||||||
|
// This shim does not call any other functions, thus there can be no recursion.
|
||||||
|
InstanceDef::FnPtrAddrShim(..) => continue,
|
||||||
InstanceDef::DropGlue(..) => {
|
InstanceDef::DropGlue(..) => {
|
||||||
// FIXME: A not fully substituted drop shim can cause ICEs if one attempts to
|
// 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
|
// 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)
|
build_drop_shim(tcx, def_id, ty)
|
||||||
}
|
}
|
||||||
ty::InstanceDef::CloneShim(def_id, ty) => build_clone_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(..) => {
|
ty::InstanceDef::Virtual(..) => {
|
||||||
bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance)
|
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
|
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::ClosureOnceShim { .. }
|
||||||
| ty::InstanceDef::Item(..)
|
| ty::InstanceDef::Item(..)
|
||||||
| ty::InstanceDef::FnPtrShim(..)
|
| ty::InstanceDef::FnPtrShim(..)
|
||||||
| ty::InstanceDef::CloneShim(..) => {
|
| ty::InstanceDef::CloneShim(..)
|
||||||
|
| ty::InstanceDef::FnPtrAddrShim(..) => {
|
||||||
output.push(create_fn_mono_item(tcx, instance, source));
|
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::Intrinsic(..)
|
||||||
| ty::InstanceDef::DropGlue(..)
|
| ty::InstanceDef::DropGlue(..)
|
||||||
| ty::InstanceDef::Virtual(..)
|
| 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
|
// 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::Intrinsic(..)
|
||||||
| InstanceDef::ClosureOnceShim { .. }
|
| InstanceDef::ClosureOnceShim { .. }
|
||||||
| InstanceDef::DropGlue(..)
|
| 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
|
// The `start_fn` lang item is actually a monomorphized instance of a
|
||||||
|
|
|
@ -722,6 +722,8 @@ symbols! {
|
||||||
fn_mut,
|
fn_mut,
|
||||||
fn_once,
|
fn_once,
|
||||||
fn_once_output,
|
fn_once_output,
|
||||||
|
fn_ptr_addr,
|
||||||
|
fn_ptr_trait,
|
||||||
forbid,
|
forbid,
|
||||||
forget,
|
forget,
|
||||||
format,
|
format,
|
||||||
|
|
|
@ -153,6 +153,12 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
|
||||||
goal: Goal<'tcx, Self>,
|
goal: Goal<'tcx, Self>,
|
||||||
) -> QueryResult<'tcx>;
|
) -> 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>`
|
// 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.
|
// family of traits where `A` is given by the signature of the type.
|
||||||
fn consider_builtin_fn_trait_candidates(
|
fn consider_builtin_fn_trait_candidates(
|
||||||
|
@ -331,6 +337,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
G::consider_builtin_copy_clone_candidate(self, goal)
|
G::consider_builtin_copy_clone_candidate(self, goal)
|
||||||
} else if lang_items.pointer_like() == Some(trait_def_id) {
|
} else if lang_items.pointer_like() == Some(trait_def_id) {
|
||||||
G::consider_builtin_pointer_like_candidate(self, goal)
|
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) {
|
} 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)
|
G::consider_builtin_fn_trait_candidates(self, goal, kind)
|
||||||
} else if lang_items.tuple_trait() == Some(trait_def_id) {
|
} else if lang_items.tuple_trait() == Some(trait_def_id) {
|
||||||
|
|
|
@ -261,6 +261,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
bug!("`PointerLike` does not have an associated type: {:?}", goal);
|
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(
|
fn consider_builtin_fn_trait_candidates(
|
||||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||||
goal: Goal<'tcx, Self>,
|
goal: Goal<'tcx, Self>,
|
||||||
|
|
|
@ -222,9 +222,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
let self_ty = tcx.erase_regions(goal.predicate.self_ty());
|
let self_ty = tcx.erase_regions(goal.predicate.self_ty());
|
||||||
|
|
||||||
if let Ok(layout) = tcx.layout_of(goal.param_env.and(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() == tcx.data_layout.pointer_size
|
||||||
&& layout.layout.size() == usize_layout.size()
|
&& layout.layout.align().abi == tcx.data_layout.pointer_align.abi
|
||||||
&& layout.layout.align().abi == usize_layout.align().abi
|
|
||||||
{
|
{
|
||||||
// FIXME: We could make this faster by making a no-constraints response
|
// FIXME: We could make this faster by making a no-constraints response
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
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(
|
fn consider_builtin_fn_trait_candidates(
|
||||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||||
goal: Goal<'tcx, Self>,
|
goal: Goal<'tcx, Self>,
|
||||||
|
|
|
@ -411,11 +411,24 @@ fn resolve_negative_obligation<'tcx>(
|
||||||
infcx.resolve_regions(&outlives_env).is_empty()
|
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)]
|
#[instrument(level = "debug", skip(tcx), ret)]
|
||||||
pub fn trait_ref_is_knowable<'tcx>(
|
pub fn trait_ref_is_knowable<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
trait_ref: ty::TraitRef<'tcx>,
|
trait_ref: ty::TraitRef<'tcx>,
|
||||||
) -> Result<(), Conflict> {
|
) -> 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() {
|
if orphan_check_trait_ref(trait_ref, InCrate::Remote).is_ok() {
|
||||||
// A downstream or cousin crate is allowed to implement some
|
// A downstream or cousin crate is allowed to implement some
|
||||||
// substitution of this trait-ref.
|
// substitution of this trait-ref.
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
//! candidates. See the [rustc dev guide] for more details.
|
//! candidates. See the [rustc dev guide] for more details.
|
||||||
//!
|
//!
|
||||||
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
|
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
|
||||||
|
|
||||||
|
use hir::def_id::DefId;
|
||||||
use hir::LangItem;
|
use hir::LangItem;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_infer::traits::ObligationCause;
|
use rustc_infer::traits::ObligationCause;
|
||||||
|
@ -96,6 +98,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
self.assemble_candidate_for_tuple(obligation, &mut candidates);
|
self.assemble_candidate_for_tuple(obligation, &mut candidates);
|
||||||
} else if lang_items.pointer_like() == Some(def_id) {
|
} else if lang_items.pointer_like() == Some(def_id) {
|
||||||
self.assemble_candidate_for_ptr_sized(obligation, &mut candidates);
|
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 {
|
} else {
|
||||||
if lang_items.clone_trait() == Some(def_id) {
|
if lang_items.clone_trait() == Some(def_id) {
|
||||||
// Same builtin conditions as `Copy`, i.e., every type which has builtin support
|
// 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`.
|
/// Searches for impls that might apply to `obligation`.
|
||||||
|
#[instrument(level = "debug", skip(self, candidates))]
|
||||||
fn assemble_candidates_from_impls(
|
fn assemble_candidates_from_impls(
|
||||||
&mut self,
|
&mut self,
|
||||||
obligation: &TraitObligation<'tcx>,
|
obligation: &TraitObligation<'tcx>,
|
||||||
candidates: &mut SelectionCandidateSet<'tcx>,
|
candidates: &mut SelectionCandidateSet<'tcx>,
|
||||||
) {
|
) {
|
||||||
debug!(?obligation, "assemble_candidates_from_impls");
|
|
||||||
|
|
||||||
// Essentially any user-written impl will match with an error type,
|
// Essentially any user-written impl will match with an error type,
|
||||||
// so creating `ImplCandidates` isn't useful. However, we might
|
// so creating `ImplCandidates` isn't useful. However, we might
|
||||||
// end up finding a candidate elsewhere (e.g. a `BuiltinCandidate` for `Sized`)
|
// 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) {
|
if self.fast_reject_trait_refs(obligation, &impl_trait_ref.0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if self.reject_fn_ptr_impls(
|
||||||
|
impl_def_id,
|
||||||
|
obligation,
|
||||||
|
impl_trait_ref.skip_binder().self_ty(),
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.infcx.probe(|_| {
|
self.infcx.probe(|_| {
|
||||||
if let Ok(_substs) = self.match_impl(impl_def_id, impl_trait_ref, obligation) {
|
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(
|
fn assemble_candidates_from_auto_impls(
|
||||||
&mut self,
|
&mut self,
|
||||||
obligation: &TraitObligation<'tcx>,
|
obligation: &TraitObligation<'tcx>,
|
||||||
|
@ -853,13 +956,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
return;
|
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))
|
if let Ok(layout) = self.tcx().layout_of(obligation.param_env.and(self_ty))
|
||||||
&& layout.layout.size() == usize_layout.size()
|
&& layout.layout.size() == self.tcx().data_layout.pointer_size
|
||||||
&& layout.layout.align().abi == usize_layout.align().abi
|
&& layout.layout.align().abi == self.tcx().data_layout.pointer_align.abi
|
||||||
{
|
{
|
||||||
candidates.vec.push(BuiltinCandidate { has_nested: false });
|
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.
|
/// The result of attempting to insert an impl into a group of children.
|
||||||
|
#[derive(Debug)]
|
||||||
enum Inserted<'tcx> {
|
enum Inserted<'tcx> {
|
||||||
/// The impl was inserted as a new child in this group of children.
|
/// The impl was inserted as a new child in this group of children.
|
||||||
BecameNewSibling(Option<FutureCompatOverlapError<'tcx>>),
|
BecameNewSibling(Option<FutureCompatOverlapError<'tcx>>),
|
||||||
|
@ -82,6 +83,7 @@ impl<'tcx> ChildrenExt<'tcx> for Children {
|
||||||
|
|
||||||
/// Attempt to insert an impl into this set of children, while comparing for
|
/// Attempt to insert an impl into this set of children, while comparing for
|
||||||
/// specialization relationships.
|
/// specialization relationships.
|
||||||
|
#[instrument(level = "debug", skip(self, tcx), ret)]
|
||||||
fn insert(
|
fn insert(
|
||||||
&mut self,
|
&mut self,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
|
@ -92,18 +94,13 @@ impl<'tcx> ChildrenExt<'tcx> for Children {
|
||||||
let mut last_lint = None;
|
let mut last_lint = None;
|
||||||
let mut replace_children = Vec::new();
|
let mut replace_children = Vec::new();
|
||||||
|
|
||||||
debug!("insert(impl_def_id={:?}, simplified_self={:?})", impl_def_id, simplified_self,);
|
|
||||||
|
|
||||||
let possible_siblings = match simplified_self {
|
let possible_siblings = match simplified_self {
|
||||||
Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)),
|
Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)),
|
||||||
None => PotentialSiblings::Unfiltered(iter_children(self)),
|
None => PotentialSiblings::Unfiltered(iter_children(self)),
|
||||||
};
|
};
|
||||||
|
|
||||||
for possible_sibling in possible_siblings {
|
for possible_sibling in possible_siblings {
|
||||||
debug!(
|
debug!(?possible_sibling);
|
||||||
"insert: impl_def_id={:?}, simplified_self={:?}, possible_sibling={:?}",
|
|
||||||
impl_def_id, simplified_self, possible_sibling,
|
|
||||||
);
|
|
||||||
|
|
||||||
let create_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>| {
|
let create_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>| {
|
||||||
let trait_ref = overlap.impl_header.trait_ref.unwrap();
|
let trait_ref = overlap.impl_header.trait_ref.unwrap();
|
||||||
|
|
|
@ -243,7 +243,8 @@ fn resolve_associated_item<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
traits::ImplSource::Builtin(..) => {
|
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.
|
// FIXME(eddyb) use lang items for methods instead of names.
|
||||||
let name = tcx.item_name(trait_item_id);
|
let name = tcx.item_name(trait_item_id);
|
||||||
if name == sym::clone {
|
if name == sym::clone {
|
||||||
|
@ -270,6 +271,22 @@ fn resolve_associated_item<'tcx>(
|
||||||
let substs = tcx.erase_regions(rcvr_substs);
|
let substs = tcx.erase_regions(rcvr_substs);
|
||||||
Some(ty::Instance::new(trait_item_id, 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 {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -922,3 +922,18 @@ mod copy_impls {
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
impl<T: ?Sized> Copy for &T {}
|
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,150 +1891,205 @@ pub fn hash<T: ?Sized, S: hash::Hasher>(hashee: *const T, into: &mut S) {
|
||||||
hashee.hash(into);
|
hashee.hash(into);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a unary fn pointer, it adds a doc comment.
|
#[cfg(bootstrap)]
|
||||||
// Otherwise, it hides the docs entirely.
|
mod old_fn_ptr_impl {
|
||||||
macro_rules! maybe_fnptr_doc {
|
use super::*;
|
||||||
(@ #[$meta:meta] $item:item) => {
|
// If this is a unary fn pointer, it adds a doc comment.
|
||||||
#[doc(hidden)]
|
// Otherwise, it hides the docs entirely.
|
||||||
#[$meta]
|
macro_rules! maybe_fnptr_doc {
|
||||||
$item
|
(@ #[$meta:meta] $item:item) => {
|
||||||
};
|
#[doc(hidden)]
|
||||||
($a:ident @ #[$meta:meta] $item:item) => {
|
#[$meta]
|
||||||
#[doc(fake_variadic)]
|
$item
|
||||||
#[doc = "This trait is implemented for function pointers with up to twelve arguments."]
|
};
|
||||||
#[$meta]
|
($a:ident @ #[$meta:meta] $item:item) => {
|
||||||
$item
|
#[doc(fake_variadic)]
|
||||||
};
|
#[doc = "This trait is implemented for function pointers with up to twelve arguments."]
|
||||||
($a:ident $($rest_a:ident)+ @ #[$meta:meta] $item:item) => {
|
#[$meta]
|
||||||
#[doc(hidden)]
|
$item
|
||||||
#[$meta]
|
};
|
||||||
$item
|
($a:ident $($rest_a:ident)+ @ #[$meta:meta] $item:item) => {
|
||||||
};
|
#[doc(hidden)]
|
||||||
}
|
#[$meta]
|
||||||
|
$item
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME(strict_provenance_magic): function pointers have buggy codegen that
|
// FIXME(strict_provenance_magic): function pointers have buggy codegen that
|
||||||
// necessitates casting to a usize to get the backend to do the right thing.
|
// 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
|
// 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.
|
// have a proper "opaque function pointer type" to handle this kind of thing.
|
||||||
|
|
||||||
// Impls for function pointers
|
// Impls for function pointers
|
||||||
macro_rules! fnptr_impls_safety_abi {
|
macro_rules! fnptr_impls_safety_abi {
|
||||||
($FnTy: ty, $($Arg: ident),*) => {
|
($FnTy: ty, $($Arg: ident),*) => {
|
||||||
fnptr_impls_safety_abi! { #[stable(feature = "fnptr_impls", since = "1.4.0")] $FnTy, $($Arg),* }
|
fnptr_impls_safety_abi! { #[stable(feature = "fnptr_impls", since = "1.4.0")] $FnTy, $($Arg),* }
|
||||||
};
|
};
|
||||||
(@c_unwind $FnTy: ty, $($Arg: ident),*) => {
|
(@c_unwind $FnTy: ty, $($Arg: ident),*) => {
|
||||||
fnptr_impls_safety_abi! { #[unstable(feature = "c_unwind", issue = "74990")] $FnTy, $($Arg),* }
|
fnptr_impls_safety_abi! { #[unstable(feature = "c_unwind", issue = "74990")] $FnTy, $($Arg),* }
|
||||||
};
|
};
|
||||||
(#[$meta:meta] $FnTy: ty, $($Arg: ident),*) => {
|
(#[$meta:meta] $FnTy: ty, $($Arg: ident),*) => {
|
||||||
maybe_fnptr_doc! {
|
maybe_fnptr_doc! {
|
||||||
$($Arg)* @
|
$($Arg)* @
|
||||||
#[$meta]
|
#[$meta]
|
||||||
impl<Ret, $($Arg),*> PartialEq for $FnTy {
|
impl<Ret, $($Arg),*> PartialEq for $FnTy {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
*self as usize == *other as usize
|
*self as usize == *other as usize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
maybe_fnptr_doc! {
|
maybe_fnptr_doc! {
|
||||||
$($Arg)* @
|
$($Arg)* @
|
||||||
#[$meta]
|
#[$meta]
|
||||||
impl<Ret, $($Arg),*> Eq for $FnTy {}
|
impl<Ret, $($Arg),*> Eq for $FnTy {}
|
||||||
}
|
}
|
||||||
|
|
||||||
maybe_fnptr_doc! {
|
maybe_fnptr_doc! {
|
||||||
$($Arg)* @
|
$($Arg)* @
|
||||||
#[$meta]
|
#[$meta]
|
||||||
impl<Ret, $($Arg),*> PartialOrd for $FnTy {
|
impl<Ret, $($Arg),*> PartialOrd for $FnTy {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
(*self as usize).partial_cmp(&(*other as usize))
|
(*self as usize).partial_cmp(&(*other as usize))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
maybe_fnptr_doc! {
|
maybe_fnptr_doc! {
|
||||||
$($Arg)* @
|
$($Arg)* @
|
||||||
#[$meta]
|
#[$meta]
|
||||||
impl<Ret, $($Arg),*> Ord for $FnTy {
|
impl<Ret, $($Arg),*> Ord for $FnTy {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
(*self as usize).cmp(&(*other as usize))
|
(*self as usize).cmp(&(*other as usize))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
maybe_fnptr_doc! {
|
maybe_fnptr_doc! {
|
||||||
$($Arg)* @
|
$($Arg)* @
|
||||||
#[$meta]
|
#[$meta]
|
||||||
impl<Ret, $($Arg),*> hash::Hash for $FnTy {
|
impl<Ret, $($Arg),*> hash::Hash for $FnTy {
|
||||||
fn hash<HH: hash::Hasher>(&self, state: &mut HH) {
|
fn hash<HH: hash::Hasher>(&self, state: &mut HH) {
|
||||||
state.write_usize(*self as usize)
|
state.write_usize(*self as usize)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
maybe_fnptr_doc! {
|
maybe_fnptr_doc! {
|
||||||
$($Arg)* @
|
$($Arg)* @
|
||||||
#[$meta]
|
#[$meta]
|
||||||
impl<Ret, $($Arg),*> fmt::Pointer for $FnTy {
|
impl<Ret, $($Arg),*> fmt::Pointer for $FnTy {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::pointer_fmt_inner(*self as usize, f)
|
fmt::pointer_fmt_inner(*self as usize, f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
maybe_fnptr_doc! {
|
maybe_fnptr_doc! {
|
||||||
$($Arg)* @
|
$($Arg)* @
|
||||||
#[$meta]
|
#[$meta]
|
||||||
impl<Ret, $($Arg),*> fmt::Debug for $FnTy {
|
impl<Ret, $($Arg),*> fmt::Debug for $FnTy {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::pointer_fmt_inner(*self as usize, f)
|
fmt::pointer_fmt_inner(*self as usize, f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! fnptr_impls_args {
|
macro_rules! fnptr_impls_args {
|
||||||
($($Arg: ident),+) => {
|
($($Arg: ident),+) => {
|
||||||
fnptr_impls_safety_abi! { extern "Rust" fn($($Arg),+) -> Ret, $($Arg),+ }
|
fnptr_impls_safety_abi! { extern "Rust" fn($($Arg),+) -> Ret, $($Arg),+ }
|
||||||
fnptr_impls_safety_abi! { extern "C" fn($($Arg),+) -> Ret, $($Arg),+ }
|
fnptr_impls_safety_abi! { extern "C" fn($($Arg),+) -> Ret, $($Arg),+ }
|
||||||
fnptr_impls_safety_abi! { extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ }
|
fnptr_impls_safety_abi! { extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ }
|
||||||
fnptr_impls_safety_abi! { @c_unwind extern "C-unwind" fn($($Arg),+) -> Ret, $($Arg),+ }
|
fnptr_impls_safety_abi! { @c_unwind extern "C-unwind" fn($($Arg),+) -> Ret, $($Arg),+ }
|
||||||
fnptr_impls_safety_abi! { @c_unwind extern "C-unwind" fn($($Arg),+ , ...) -> Ret, $($Arg),+ }
|
fnptr_impls_safety_abi! { @c_unwind extern "C-unwind" fn($($Arg),+ , ...) -> Ret, $($Arg),+ }
|
||||||
fnptr_impls_safety_abi! { unsafe extern "Rust" fn($($Arg),+) -> Ret, $($Arg),+ }
|
fnptr_impls_safety_abi! { unsafe extern "Rust" fn($($Arg),+) -> Ret, $($Arg),+ }
|
||||||
fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),+) -> Ret, $($Arg),+ }
|
fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),+) -> Ret, $($Arg),+ }
|
||||||
fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ }
|
fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ }
|
||||||
fnptr_impls_safety_abi! { @c_unwind unsafe extern "C-unwind" fn($($Arg),+) -> Ret, $($Arg),+ }
|
fnptr_impls_safety_abi! { @c_unwind unsafe extern "C-unwind" fn($($Arg),+) -> Ret, $($Arg),+ }
|
||||||
fnptr_impls_safety_abi! { @c_unwind unsafe extern "C-unwind" fn($($Arg),+ , ...) -> Ret, $($Arg),+ }
|
fnptr_impls_safety_abi! { @c_unwind unsafe extern "C-unwind" fn($($Arg),+ , ...) -> Ret, $($Arg),+ }
|
||||||
};
|
};
|
||||||
() => {
|
() => {
|
||||||
// No variadic functions with 0 parameters
|
// No variadic functions with 0 parameters
|
||||||
fnptr_impls_safety_abi! { extern "Rust" fn() -> Ret, }
|
fnptr_impls_safety_abi! { extern "Rust" fn() -> Ret, }
|
||||||
fnptr_impls_safety_abi! { extern "C" fn() -> Ret, }
|
fnptr_impls_safety_abi! { extern "C" fn() -> Ret, }
|
||||||
fnptr_impls_safety_abi! { @c_unwind extern "C-unwind" fn() -> Ret, }
|
fnptr_impls_safety_abi! { @c_unwind extern "C-unwind" fn() -> Ret, }
|
||||||
fnptr_impls_safety_abi! { unsafe extern "Rust" fn() -> Ret, }
|
fnptr_impls_safety_abi! { unsafe extern "Rust" fn() -> Ret, }
|
||||||
fnptr_impls_safety_abi! { unsafe extern "C" fn() -> Ret, }
|
fnptr_impls_safety_abi! { unsafe extern "C" fn() -> Ret, }
|
||||||
fnptr_impls_safety_abi! { @c_unwind unsafe extern "C-unwind" 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! {}
|
#[cfg(not(bootstrap))]
|
||||||
fnptr_impls_args! { T }
|
mod new_fn_ptr_impl {
|
||||||
fnptr_impls_args! { A, B }
|
use super::*;
|
||||||
fnptr_impls_args! { A, B, C }
|
use crate::marker::FnPtr;
|
||||||
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 }
|
|
||||||
|
|
||||||
|
#[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.
|
/// 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
|
/// 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`
|
| ^^^^^^^^^^^^^^^^^^^^^^^ `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 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)
|
= 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`
|
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`
|
| ^^^^^^^^^^^^^^^^^^^^^^^ `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 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)
|
= 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
|
error: aborting due to 10 previous errors
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue