Skip to content

Commit bf57e8a

Browse files
committed
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 #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.
2 parents 6066037 + 5ae6caa commit bf57e8a

File tree

23 files changed

+476
-180
lines changed

23 files changed

+476
-180
lines changed

compiler/rustc_const_eval/src/interpret/terminator.rs

+1
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
382382
| ty::InstanceDef::FnPtrShim(..)
383383
| ty::InstanceDef::DropGlue(..)
384384
| ty::InstanceDef::CloneShim(..)
385+
| ty::InstanceDef::FnPtrAddrShim(..)
385386
| ty::InstanceDef::Item(_) => {
386387
// We need MIR for this fn
387388
let Some((body, instance)) =

compiler/rustc_hir/src/lang_items.rs

+3
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ language_item_table! {
166166

167167
Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
168168

169+
FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0);
170+
FnPtrAddr, sym::fn_ptr_addr, fn_ptr_addr, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
171+
169172
Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None;
170173
Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
171174

compiler/rustc_middle/src/mir/mono.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,8 @@ impl<'tcx> CodegenUnit<'tcx> {
381381
| InstanceDef::Virtual(..)
382382
| InstanceDef::ClosureOnceShim { .. }
383383
| InstanceDef::DropGlue(..)
384-
| InstanceDef::CloneShim(..) => None,
384+
| InstanceDef::CloneShim(..)
385+
| InstanceDef::FnPtrAddrShim(..) => None,
385386
}
386387
}
387388
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
@@ -340,7 +340,8 @@ macro_rules! make_mir_visitor {
340340

341341
ty::InstanceDef::FnPtrShim(_def_id, ty) |
342342
ty::InstanceDef::DropGlue(_def_id, Some(ty)) |
343-
ty::InstanceDef::CloneShim(_def_id, ty) => {
343+
ty::InstanceDef::CloneShim(_def_id, ty) |
344+
ty::InstanceDef::FnPtrAddrShim(_def_id, ty) => {
344345
// FIXME(eddyb) use a better `TyContext` here.
345346
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
346347
}

compiler/rustc_middle/src/ty/instance.rs

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

101108
impl<'tcx> Instance<'tcx> {
@@ -151,7 +158,8 @@ impl<'tcx> InstanceDef<'tcx> {
151158
| InstanceDef::Intrinsic(def_id)
152159
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
153160
| InstanceDef::DropGlue(def_id, _)
154-
| InstanceDef::CloneShim(def_id, _) => def_id,
161+
| InstanceDef::CloneShim(def_id, _)
162+
| InstanceDef::FnPtrAddrShim(def_id, _) => def_id,
155163
}
156164
}
157165

@@ -167,7 +175,8 @@ impl<'tcx> InstanceDef<'tcx> {
167175
| InstanceDef::Intrinsic(..)
168176
| InstanceDef::ClosureOnceShim { .. }
169177
| InstanceDef::DropGlue(..)
170-
| InstanceDef::CloneShim(..) => None,
178+
| InstanceDef::CloneShim(..)
179+
| InstanceDef::FnPtrAddrShim(..) => None,
171180
}
172181
}
173182

@@ -182,7 +191,8 @@ impl<'tcx> InstanceDef<'tcx> {
182191
| InstanceDef::Intrinsic(def_id)
183192
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
184193
| InstanceDef::DropGlue(def_id, _)
185-
| InstanceDef::CloneShim(def_id, _) => ty::WithOptConstParam::unknown(def_id),
194+
| InstanceDef::CloneShim(def_id, _)
195+
| InstanceDef::FnPtrAddrShim(def_id, _) => ty::WithOptConstParam::unknown(def_id),
186196
}
187197
}
188198

@@ -268,6 +278,7 @@ impl<'tcx> InstanceDef<'tcx> {
268278
pub fn has_polymorphic_mir_body(&self) -> bool {
269279
match *self {
270280
InstanceDef::CloneShim(..)
281+
| InstanceDef::FnPtrAddrShim(..)
271282
| InstanceDef::FnPtrShim(..)
272283
| InstanceDef::DropGlue(_, Some(_)) => false,
273284
InstanceDef::ClosureOnceShim { .. }
@@ -306,6 +317,7 @@ fn fmt_instance(
306317
InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"),
307318
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty),
308319
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty),
320+
InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({})", ty),
309321
}
310322
}
311323

compiler/rustc_middle/src/ty/mod.rs

+10-29
Original file line numberDiff line numberDiff line change
@@ -2279,76 +2279,56 @@ impl<'tcx> TyCtxt<'tcx> {
22792279

22802280
/// Returns `true` if the impls are the same polarity and the trait either
22812281
/// has no items or is annotated `#[marker]` and prevents item overrides.
2282+
#[instrument(level = "debug", skip(self), ret)]
22822283
pub fn impls_are_allowed_to_overlap(
22832284
self,
22842285
def_id1: DefId,
22852286
def_id2: DefId,
22862287
) -> Option<ImplOverlapKind> {
2288+
let impl_trait_ref1 = self.impl_trait_ref(def_id1);
2289+
let impl_trait_ref2 = self.impl_trait_ref(def_id2);
22872290
// If either trait impl references an error, they're allowed to overlap,
22882291
// as one of them essentially doesn't exist.
2289-
if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.subst_identity().references_error())
2290-
|| self
2291-
.impl_trait_ref(def_id2)
2292-
.map_or(false, |tr| tr.subst_identity().references_error())
2292+
if impl_trait_ref1.map_or(false, |tr| tr.subst_identity().references_error())
2293+
|| impl_trait_ref2.map_or(false, |tr| tr.subst_identity().references_error())
22932294
{
22942295
return Some(ImplOverlapKind::Permitted { marker: false });
22952296
}
22962297

22972298
match (self.impl_polarity(def_id1), self.impl_polarity(def_id2)) {
22982299
(ImplPolarity::Reservation, _) | (_, ImplPolarity::Reservation) => {
22992300
// `#[rustc_reservation_impl]` impls don't overlap with anything
2300-
debug!(
2301-
"impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (reservations)",
2302-
def_id1, def_id2
2303-
);
23042301
return Some(ImplOverlapKind::Permitted { marker: false });
23052302
}
23062303
(ImplPolarity::Positive, ImplPolarity::Negative)
23072304
| (ImplPolarity::Negative, ImplPolarity::Positive) => {
23082305
// `impl AutoTrait for Type` + `impl !AutoTrait for Type`
2309-
debug!(
2310-
"impls_are_allowed_to_overlap({:?}, {:?}) - None (differing polarities)",
2311-
def_id1, def_id2
2312-
);
23132306
return None;
23142307
}
23152308
(ImplPolarity::Positive, ImplPolarity::Positive)
23162309
| (ImplPolarity::Negative, ImplPolarity::Negative) => {}
23172310
};
23182311

23192312
let is_marker_overlap = {
2320-
let is_marker_impl = |def_id: DefId| -> bool {
2321-
let trait_ref = self.impl_trait_ref(def_id);
2313+
let is_marker_impl = |trait_ref: Option<EarlyBinder<TraitRef<'_>>>| -> bool {
23222314
trait_ref.map_or(false, |tr| self.trait_def(tr.skip_binder().def_id).is_marker)
23232315
};
2324-
is_marker_impl(def_id1) && is_marker_impl(def_id2)
2316+
is_marker_impl(impl_trait_ref1) && is_marker_impl(impl_trait_ref2)
23252317
};
23262318

23272319
if is_marker_overlap {
2328-
debug!(
2329-
"impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (marker overlap)",
2330-
def_id1, def_id2
2331-
);
23322320
Some(ImplOverlapKind::Permitted { marker: true })
23332321
} else {
23342322
if let Some(self_ty1) = self.issue33140_self_ty(def_id1) {
23352323
if let Some(self_ty2) = self.issue33140_self_ty(def_id2) {
23362324
if self_ty1 == self_ty2 {
2337-
debug!(
2338-
"impls_are_allowed_to_overlap({:?}, {:?}) - issue #33140 HACK",
2339-
def_id1, def_id2
2340-
);
23412325
return Some(ImplOverlapKind::Issue33140);
23422326
} else {
2343-
debug!(
2344-
"impls_are_allowed_to_overlap({:?}, {:?}) - found {:?} != {:?}",
2345-
def_id1, def_id2, self_ty1, self_ty2
2346-
);
2327+
debug!("found {self_ty1:?} != {self_ty2:?}");
23472328
}
23482329
}
23492330
}
23502331

2351-
debug!("impls_are_allowed_to_overlap({:?}, {:?}) = None", def_id1, def_id2);
23522332
None
23532333
}
23542334
}
@@ -2405,7 +2385,8 @@ impl<'tcx> TyCtxt<'tcx> {
24052385
| ty::InstanceDef::Virtual(..)
24062386
| ty::InstanceDef::ClosureOnceShim { .. }
24072387
| ty::InstanceDef::DropGlue(..)
2408-
| ty::InstanceDef::CloneShim(..) => self.mir_shims(instance),
2388+
| ty::InstanceDef::CloneShim(..)
2389+
| ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance),
24092390
}
24102391
}
24112392

compiler/rustc_mir_transform/src/inline.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ impl<'tcx> Inliner<'tcx> {
270270
| InstanceDef::FnPtrShim(..)
271271
| InstanceDef::ClosureOnceShim { .. }
272272
| InstanceDef::DropGlue(..)
273-
| InstanceDef::CloneShim(..) => return Ok(()),
273+
| InstanceDef::CloneShim(..)
274+
| InstanceDef::FnPtrAddrShim(..) => return Ok(()),
274275
}
275276

276277
if self.tcx.is_constructor(callee_def_id) {

compiler/rustc_mir_transform/src/inline/cycle.rs

+3
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
8484
| InstanceDef::FnPtrShim(..)
8585
| InstanceDef::ClosureOnceShim { .. }
8686
| InstanceDef::CloneShim(..) => {}
87+
88+
// This shim does not call any other functions, thus there can be no recursion.
89+
InstanceDef::FnPtrAddrShim(..) => continue,
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

+37
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
7777
build_drop_shim(tcx, def_id, ty)
7878
}
7979
ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty),
80+
ty::InstanceDef::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty),
8081
ty::InstanceDef::Virtual(..) => {
8182
bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance)
8283
}
@@ -861,3 +862,39 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> {
861862

862863
body
863864
}
865+
866+
/// ```ignore (pseudo-impl)
867+
/// impl FnPtr for fn(u32) {
868+
/// fn addr(self) -> usize {
869+
/// self as usize
870+
/// }
871+
/// }
872+
/// ```
873+
fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> {
874+
assert!(matches!(self_ty.kind(), ty::FnPtr(..)), "expected fn ptr, found {self_ty}");
875+
let span = tcx.def_span(def_id);
876+
let Some(sig) = tcx.fn_sig(def_id).subst(tcx, &[self_ty.into()]).no_bound_vars() else {
877+
span_bug!(span, "FnPtr::addr with bound vars for `{self_ty}`");
878+
};
879+
let locals = local_decls_for_sig(&sig, span);
880+
881+
let source_info = SourceInfo::outermost(span);
882+
// FIXME: use `expose_addr` once we figure out whether function pointers have meaningful provenance.
883+
let rvalue = Rvalue::Cast(
884+
CastKind::FnPtrToPtr,
885+
Operand::Move(Place::from(Local::new(1))),
886+
tcx.mk_imm_ptr(tcx.types.unit),
887+
);
888+
let stmt = Statement {
889+
source_info,
890+
kind: StatementKind::Assign(Box::new((Place::return_place(), rvalue))),
891+
};
892+
let statements = vec![stmt];
893+
let start_block = BasicBlockData {
894+
statements,
895+
terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }),
896+
is_cleanup: false,
897+
};
898+
let source = MirSource::from_instance(ty::InstanceDef::FnPtrAddrShim(def_id, self_ty));
899+
new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span)
900+
}

compiler/rustc_monomorphize/src/collector.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,8 @@ fn visit_instance_use<'tcx>(
974974
| ty::InstanceDef::ClosureOnceShim { .. }
975975
| ty::InstanceDef::Item(..)
976976
| ty::InstanceDef::FnPtrShim(..)
977-
| ty::InstanceDef::CloneShim(..) => {
977+
| ty::InstanceDef::CloneShim(..)
978+
| ty::InstanceDef::FnPtrAddrShim(..) => {
978979
output.push(create_fn_mono_item(tcx, instance, source));
979980
}
980981
}

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
@@ -722,6 +722,8 @@ symbols! {
722722
fn_mut,
723723
fn_once,
724724
fn_once_output,
725+
fn_ptr_addr,
726+
fn_ptr_trait,
725727
forbid,
726728
forget,
727729
format,

compiler/rustc_trait_selection/src/solve/assembly.rs

+8
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
153153
goal: Goal<'tcx, Self>,
154154
) -> QueryResult<'tcx>;
155155

156+
// A type is a `FnPtr` if it is of `FnPtr` type.
157+
fn consider_builtin_fn_ptr_trait_candidate(
158+
ecx: &mut EvalCtxt<'_, 'tcx>,
159+
goal: Goal<'tcx, Self>,
160+
) -> QueryResult<'tcx>;
161+
156162
// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>`
157163
// family of traits where `A` is given by the signature of the type.
158164
fn consider_builtin_fn_trait_candidates(
@@ -331,6 +337,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
331337
G::consider_builtin_copy_clone_candidate(self, goal)
332338
} else if lang_items.pointer_like() == Some(trait_def_id) {
333339
G::consider_builtin_pointer_like_candidate(self, goal)
340+
} else if lang_items.fn_ptr_trait() == Some(trait_def_id) {
341+
G::consider_builtin_fn_ptr_trait_candidate(self, goal)
334342
} else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) {
335343
G::consider_builtin_fn_trait_candidates(self, goal, kind)
336344
} else if lang_items.tuple_trait() == Some(trait_def_id) {

compiler/rustc_trait_selection/src/solve/project_goals.rs

+7
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
261261
bug!("`PointerLike` does not have an associated type: {:?}", goal);
262262
}
263263

264+
fn consider_builtin_fn_ptr_trait_candidate(
265+
_ecx: &mut EvalCtxt<'_, 'tcx>,
266+
goal: Goal<'tcx, Self>,
267+
) -> QueryResult<'tcx> {
268+
bug!("`FnPtr` does not have an associated type: {:?}", goal);
269+
}
270+
264271
fn consider_builtin_fn_trait_candidates(
265272
ecx: &mut EvalCtxt<'_, 'tcx>,
266273
goal: Goal<'tcx, Self>,

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
222222
let self_ty = tcx.erase_regions(goal.predicate.self_ty());
223223

224224
if let Ok(layout) = tcx.layout_of(goal.param_env.and(self_ty))
225-
&& let usize_layout = tcx.layout_of(ty::ParamEnv::empty().and(tcx.types.usize)).unwrap().layout
226-
&& layout.layout.size() == usize_layout.size()
227-
&& layout.layout.align().abi == usize_layout.align().abi
225+
&& layout.layout.size() == tcx.data_layout.pointer_size
226+
&& layout.layout.align().abi == tcx.data_layout.pointer_align.abi
228227
{
229228
// FIXME: We could make this faster by making a no-constraints response
230229
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
@@ -233,6 +232,17 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
233232
}
234233
}
235234

235+
fn consider_builtin_fn_ptr_trait_candidate(
236+
ecx: &mut EvalCtxt<'_, 'tcx>,
237+
goal: Goal<'tcx, Self>,
238+
) -> QueryResult<'tcx> {
239+
if let ty::FnPtr(..) = goal.predicate.self_ty().kind() {
240+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
241+
} else {
242+
Err(NoSolution)
243+
}
244+
}
245+
236246
fn consider_builtin_fn_trait_candidates(
237247
ecx: &mut EvalCtxt<'_, 'tcx>,
238248
goal: Goal<'tcx, Self>,

compiler/rustc_trait_selection/src/traits/coherence.rs

+13
Original file line numberDiff line numberDiff line change
@@ -411,11 +411,24 @@ fn resolve_negative_obligation<'tcx>(
411411
infcx.resolve_regions(&outlives_env).is_empty()
412412
}
413413

414+
/// Returns whether all impls which would apply to the `trait_ref`
415+
/// e.g. `Ty: Trait<Arg>` are already known in the local crate.
416+
///
417+
/// This both checks whether any downstream or sibling crates could
418+
/// implement it and whether an upstream crate can add this impl
419+
/// without breaking backwards compatibility.
414420
#[instrument(level = "debug", skip(tcx), ret)]
415421
pub fn trait_ref_is_knowable<'tcx>(
416422
tcx: TyCtxt<'tcx>,
417423
trait_ref: ty::TraitRef<'tcx>,
418424
) -> Result<(), Conflict> {
425+
if Some(trait_ref.def_id) == tcx.lang_items().fn_ptr_trait() {
426+
// The only types implementing `FnPtr` are function pointers,
427+
// so if there's no impl of `FnPtr` in the current crate,
428+
// then such an impl will never be added in the future.
429+
return Ok(());
430+
}
431+
419432
if orphan_check_trait_ref(trait_ref, InCrate::Remote).is_ok() {
420433
// A downstream or cousin crate is allowed to implement some
421434
// substitution of this trait-ref.

0 commit comments

Comments
 (0)