Skip to content

Commit 04accf1

Browse files
committed
add a builtin FnPtr trait
1 parent ed4dd0c commit 04accf1

File tree

17 files changed

+163
-13
lines changed

17 files changed

+163
-13
lines changed

compiler/rustc_const_eval/src/interpret/terminator.rs

+1
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
370370
| ty::InstanceDef::FnPtrShim(..)
371371
| ty::InstanceDef::DropGlue(..)
372372
| ty::InstanceDef::CloneShim(..)
373+
| ty::InstanceDef::FnPtrAddrShim(..)
373374
| ty::InstanceDef::Item(_) => {
374375
// We need MIR for this fn
375376
let Some((body, instance)) =

compiler/rustc_hir/src/lang_items.rs

+3
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ language_item_table! {
185185

186186
Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
187187

188+
FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0);
189+
FnPtrAddr, sym::fn_ptr_addr, fn_ptr_addr, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
190+
188191
Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None;
189192
Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
190193

compiler/rustc_middle/src/mir/mono.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,8 @@ impl<'tcx> CodegenUnit<'tcx> {
369369
| InstanceDef::Virtual(..)
370370
| InstanceDef::ClosureOnceShim { .. }
371371
| InstanceDef::DropGlue(..)
372-
| InstanceDef::CloneShim(..) => None,
372+
| InstanceDef::CloneShim(..)
373+
| InstanceDef::FnPtrAddrShim(..) => None,
373374
}
374375
}
375376
MonoItem::Static(def_id) => def_id.as_local().map(Idx::index),

compiler/rustc_middle/src/mir/visit.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,8 @@ macro_rules! make_mir_visitor {
402402

403403
ty::InstanceDef::FnPtrShim(_def_id, ty) |
404404
ty::InstanceDef::DropGlue(_def_id, Some(ty)) |
405-
ty::InstanceDef::CloneShim(_def_id, ty) => {
405+
ty::InstanceDef::CloneShim(_def_id, ty) |
406+
ty::InstanceDef::FnPtrAddrShim(_def_id, ty) => {
406407
// FIXME(eddyb) use a better `TyContext` here.
407408
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
408409
}

compiler/rustc_middle/src/ty/instance.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ pub enum InstanceDef<'tcx> {
9797
///
9898
/// The `DefId` is for `Clone::clone`, the `Ty` is the type `T` with the builtin `Clone` impl.
9999
CloneShim(DefId, Ty<'tcx>),
100+
101+
/// Compiler-generated `<T as FnPtr>::addr` implementation.
102+
///
103+
/// Automatically generated for all potentially higher-ranked `fn(I) -> R` types.
104+
///
105+
/// The `DefId` is for `FnPtr::Addr`, the `Ty` is the type `T`.
106+
FnPtrAddrShim(DefId, Ty<'tcx>),
100107
}
101108

102109
impl<'tcx> Instance<'tcx> {
@@ -152,7 +159,8 @@ impl<'tcx> InstanceDef<'tcx> {
152159
| InstanceDef::Intrinsic(def_id)
153160
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
154161
| InstanceDef::DropGlue(def_id, _)
155-
| InstanceDef::CloneShim(def_id, _) => def_id,
162+
| InstanceDef::CloneShim(def_id, _)
163+
| InstanceDef::FnPtrAddrShim(def_id, _) => def_id,
156164
}
157165
}
158166

@@ -168,7 +176,8 @@ impl<'tcx> InstanceDef<'tcx> {
168176
| InstanceDef::Intrinsic(..)
169177
| InstanceDef::ClosureOnceShim { .. }
170178
| InstanceDef::DropGlue(..)
171-
| InstanceDef::CloneShim(..) => None,
179+
| InstanceDef::CloneShim(..)
180+
| InstanceDef::FnPtrAddrShim(..) => None,
172181
}
173182
}
174183

@@ -183,7 +192,8 @@ impl<'tcx> InstanceDef<'tcx> {
183192
| InstanceDef::Intrinsic(def_id)
184193
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
185194
| InstanceDef::DropGlue(def_id, _)
186-
| InstanceDef::CloneShim(def_id, _) => ty::WithOptConstParam::unknown(def_id),
195+
| InstanceDef::CloneShim(def_id, _)
196+
| InstanceDef::FnPtrAddrShim(def_id, _) => ty::WithOptConstParam::unknown(def_id),
187197
}
188198
}
189199

@@ -265,6 +275,7 @@ impl<'tcx> InstanceDef<'tcx> {
265275
pub fn has_polymorphic_mir_body(&self) -> bool {
266276
match *self {
267277
InstanceDef::CloneShim(..)
278+
| InstanceDef::FnPtrAddrShim(..)
268279
| InstanceDef::FnPtrShim(..)
269280
| InstanceDef::DropGlue(_, Some(_)) => false,
270281
InstanceDef::ClosureOnceShim { .. }
@@ -299,6 +310,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> {
299310
InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"),
300311
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty),
301312
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty),
313+
InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({})", ty),
302314
}
303315
}
304316
}

compiler/rustc_middle/src/ty/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2148,7 +2148,8 @@ impl<'tcx> TyCtxt<'tcx> {
21482148
| ty::InstanceDef::Virtual(..)
21492149
| ty::InstanceDef::ClosureOnceShim { .. }
21502150
| ty::InstanceDef::DropGlue(..)
2151-
| ty::InstanceDef::CloneShim(..) => self.mir_shims(instance),
2151+
| ty::InstanceDef::CloneShim(..)
2152+
| ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance),
21522153
}
21532154
}
21542155

compiler/rustc_middle/src/ty/structural_impls.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,9 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> {
640640
ty::InstanceDef::CloneShim(def_id, ty) => {
641641
Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?))
642642
}
643+
ty::InstanceDef::FnPtrAddrShim(def_id, ty) => {
644+
Some(ty::InstanceDef::FnPtrAddrShim(def_id, tcx.lift(ty)?))
645+
}
643646
}
644647
}
645648
}
@@ -943,6 +946,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> {
943946
CloneShim(did, ty) => {
944947
CloneShim(did.try_fold_with(folder)?, ty.try_fold_with(folder)?)
945948
}
949+
FnPtrAddrShim(did, ty) => {
950+
FnPtrAddrShim(did.try_fold_with(folder)?, ty.try_fold_with(folder)?)
951+
}
946952
},
947953
})
948954
}
@@ -957,7 +963,7 @@ impl<'tcx> TypeVisitable<'tcx> for ty::instance::Instance<'tcx> {
957963
VTableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => {
958964
did.visit_with(visitor)
959965
}
960-
FnPtrShim(did, ty) | CloneShim(did, ty) => {
966+
FnPtrShim(did, ty) | CloneShim(did, ty) | FnPtrAddrShim(did, ty) => {
961967
did.visit_with(visitor)?;
962968
ty.visit_with(visitor)
963969
}

compiler/rustc_mir_transform/src/inline.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ impl<'tcx> Inliner<'tcx> {
251251
| InstanceDef::FnPtrShim(..)
252252
| InstanceDef::ClosureOnceShim { .. }
253253
| InstanceDef::DropGlue(..)
254-
| InstanceDef::CloneShim(..) => return Ok(()),
254+
| InstanceDef::CloneShim(..)
255+
| InstanceDef::FnPtrAddrShim(..) => return Ok(()),
255256
}
256257

257258
if self.tcx.is_constructor(callee_def_id) {

compiler/rustc_mir_transform/src/inline/cycle.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,14 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
7979
// These have MIR and if that MIR is inlined, substituted and then inlining is run
8080
// again, a function item can end up getting inlined. Thus we'll be able to cause
8181
// a cycle that way
82+
//
83+
// FIXME: `FnPtrAddrShim` should not be able to cause recursion.
8284
InstanceDef::VTableShim(_)
8385
| InstanceDef::ReifyShim(_)
8486
| InstanceDef::FnPtrShim(..)
8587
| InstanceDef::ClosureOnceShim { .. }
86-
| InstanceDef::CloneShim(..) => {}
88+
| InstanceDef::CloneShim(..)
89+
| InstanceDef::FnPtrAddrShim(..) => {}
8790
InstanceDef::DropGlue(..) => {
8891
// FIXME: A not fully substituted drop shim can cause ICEs if one attempts to
8992
// have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this

compiler/rustc_mir_transform/src/shim.rs

+36
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
7878
build_drop_shim(tcx, def_id, ty)
7979
}
8080
ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty),
81+
ty::InstanceDef::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty),
8182
ty::InstanceDef::Virtual(..) => {
8283
bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance)
8384
}
@@ -788,3 +789,38 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> {
788789

789790
body
790791
}
792+
793+
/// ```ignore (pseudo-impl)
794+
/// impl FnPtr for fn(u32) {
795+
/// fn addr(self) -> usize {
796+
/// self as usize
797+
/// }
798+
/// }
799+
/// ```
800+
fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> {
801+
assert!(matches!(self_ty.kind(), ty::FnPtr(..)), "expected fn ptr, found {self_ty}");
802+
let span = tcx.def_span(def_id);
803+
let Some(sig) = tcx.bound_fn_sig(def_id).subst(tcx, &[self_ty.into()]).no_bound_vars() else {
804+
span_bug!(span, "FnPtr::addr with bound vars for `{self_ty}`");
805+
};
806+
let locals = local_decls_for_sig(&sig, span);
807+
808+
let source_info = SourceInfo::outermost(span);
809+
let rvalue = Rvalue::Cast(
810+
CastKind::PointerExposeAddress,
811+
Operand::Move(Place::from(Local::new(1))),
812+
tcx.types.usize,
813+
);
814+
let stmt = Statement {
815+
source_info,
816+
kind: StatementKind::Assign(Box::new((Place::return_place(), rvalue))),
817+
};
818+
let statements = vec![stmt];
819+
let start_block = BasicBlockData {
820+
statements,
821+
terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }),
822+
is_cleanup: false,
823+
};
824+
let source = MirSource::from_instance(ty::InstanceDef::FnPtrAddrShim(def_id, self_ty));
825+
new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span)
826+
}

compiler/rustc_monomorphize/src/collector.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,8 @@ fn visit_instance_use<'tcx>(
997997
| ty::InstanceDef::ClosureOnceShim { .. }
998998
| ty::InstanceDef::Item(..)
999999
| ty::InstanceDef::FnPtrShim(..)
1000-
| ty::InstanceDef::CloneShim(..) => {
1000+
| ty::InstanceDef::CloneShim(..)
1001+
| ty::InstanceDef::FnPtrAddrShim(..) => {
10011002
output.push(create_fn_mono_item(tcx, instance, source));
10021003
}
10031004
}

compiler/rustc_monomorphize/src/partitioning/default.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ fn characteristic_def_id_of_mono_item<'tcx>(
278278
| ty::InstanceDef::Intrinsic(..)
279279
| ty::InstanceDef::DropGlue(..)
280280
| ty::InstanceDef::Virtual(..)
281-
| ty::InstanceDef::CloneShim(..) => return None,
281+
| ty::InstanceDef::CloneShim(..)
282+
| ty::InstanceDef::FnPtrAddrShim(..) => return None,
282283
};
283284

284285
// If this is a method, we want to put it into the same module as
@@ -432,7 +433,8 @@ fn mono_item_visibility<'tcx>(
432433
| InstanceDef::Intrinsic(..)
433434
| InstanceDef::ClosureOnceShim { .. }
434435
| InstanceDef::DropGlue(..)
435-
| InstanceDef::CloneShim(..) => return Visibility::Hidden,
436+
| InstanceDef::CloneShim(..)
437+
| InstanceDef::FnPtrAddrShim(..) => return Visibility::Hidden,
436438
};
437439

438440
// The `start_fn` lang item is actually a monomorphized instance of a

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,8 @@ symbols! {
728728
fn_mut,
729729
fn_once,
730730
fn_once_output,
731+
fn_ptr_addr,
732+
fn_ptr_trait,
731733
forbid,
732734
forget,
733735
format,

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+41
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
323323
self.assemble_candidates_for_unsizing(obligation, &mut candidates);
324324
} else if lang_items.destruct_trait() == Some(def_id) {
325325
self.assemble_const_destruct_candidates(obligation, &mut candidates);
326+
} else if lang_items.fn_ptr_trait() == Some(def_id) {
327+
self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates);
326328
} else {
327329
if lang_items.clone_trait() == Some(def_id) {
328330
// Same builtin conditions as `Copy`, i.e., every type which has builtin support
@@ -1002,4 +1004,43 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
10021004
}
10031005
}
10041006
}
1007+
1008+
fn assemble_candidates_for_fn_ptr_trait(
1009+
&mut self,
1010+
obligation: &TraitObligation<'tcx>,
1011+
candidates: &mut SelectionCandidateSet<'tcx>,
1012+
) {
1013+
let self_ty = self.infcx().shallow_resolve(obligation.self_ty());
1014+
match self_ty.skip_binder().kind() {
1015+
ty::FnPtr(_) => candidates.vec.push(BuiltinCandidate { has_nested: false }),
1016+
ty::Bool
1017+
| ty::Char
1018+
| ty::Int(_)
1019+
| ty::Uint(_)
1020+
| ty::Float(_)
1021+
| ty::Adt(..)
1022+
| ty::Foreign(..)
1023+
| ty::Str
1024+
| ty::Array(..)
1025+
| ty::Slice(_)
1026+
| ty::RawPtr(_)
1027+
| ty::Ref(..)
1028+
| ty::FnDef(..)
1029+
| ty::Placeholder(..)
1030+
| ty::Dynamic(..)
1031+
| ty::Closure(..)
1032+
| ty::Generator(..)
1033+
| ty::GeneratorWitness(..)
1034+
| ty::Never
1035+
| ty::Tuple(..)
1036+
| ty::Projection(..)
1037+
| ty::Opaque(..)
1038+
| ty::Param(..)
1039+
| ty::Bound(..)
1040+
| ty::Error(_) => {}
1041+
ty::Infer(_) => {
1042+
candidates.ambiguous = true;
1043+
}
1044+
}
1045+
}
10051046
}

compiler/rustc_ty_utils/src/instance.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,8 @@ fn resolve_associated_item<'tcx>(
363363
}
364364
}
365365
traits::ImplSource::Builtin(..) => {
366-
if Some(trait_ref.def_id) == tcx.lang_items().clone_trait() {
366+
let lang_items = tcx.lang_items();
367+
if Some(trait_ref.def_id) == lang_items.clone_trait() {
367368
// FIXME(eddyb) use lang items for methods instead of names.
368369
let name = tcx.item_name(trait_item_id);
369370
if name == sym::clone {
@@ -387,6 +388,22 @@ fn resolve_associated_item<'tcx>(
387388
let substs = tcx.erase_regions(rcvr_substs);
388389
Some(ty::Instance::new(trait_item_id, substs))
389390
}
391+
} else if Some(trait_ref.def_id) == lang_items.fn_ptr_trait() {
392+
if lang_items.fn_ptr_addr() == Some(trait_item_id) {
393+
let self_ty = trait_ref.self_ty();
394+
if !matches!(self_ty.kind(), ty::FnPtr(..)) {
395+
return Ok(None);
396+
}
397+
Some(Instance {
398+
def: ty::InstanceDef::FnPtrAddrShim(trait_item_id, self_ty),
399+
substs: rcvr_substs,
400+
})
401+
} else {
402+
tcx.sess.span_fatal(
403+
tcx.def_span(trait_item_id),
404+
"`FnPtrAddr` trait with unexpected assoc item",
405+
)
406+
}
390407
} else {
391408
None
392409
}

compiler/rustc_typeck/src/coherence/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@ fn enforce_trait_manually_implementable(
6868
return;
6969
}
7070

71+
if did == li.fn_ptr_trait() {
72+
struct_span_err!(
73+
tcx.sess,
74+
impl_header_span,
75+
E0322,
76+
"explicit impls for the `FnPtr` trait are not permitted"
77+
)
78+
.span_label(impl_header_span, "impl of `FnPtr` not allowed")
79+
.emit();
80+
return;
81+
}
82+
7183
if did == li.sized_trait() {
7284
struct_span_err!(
7385
tcx.sess,

library/core/src/marker.rs

+10
Original file line numberDiff line numberDiff line change
@@ -838,3 +838,13 @@ mod copy_impls {
838838
#[stable(feature = "rust1", since = "1.0.0")]
839839
impl<T: ?Sized> Copy for &T {}
840840
}
841+
842+
/// A common trait implemented by all function pointers.
843+
#[unstable(feature = "fn_ptr_trait", issue = "none", reason = "recently added")]
844+
#[lang = "fn_ptr_trait"]
845+
#[cfg(not(bootstrap))]
846+
pub trait FnPtr {
847+
/// Returns the adress of the function pointer.
848+
#[lang = "fn_ptr_addr"]
849+
fn addr(self) -> usize;
850+
}

0 commit comments

Comments
 (0)