diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index fc83985eaca..54d67bd1f29 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -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)) = diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 0863d65d8f9..07faecdb6a7 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -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; diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 7a05ee2ff37..90397f01bc8 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -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), diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index cffdd7ff37f..9b31ad783fc 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -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)); } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index aa10a651c07..036b4477679 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -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 `::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), } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c6a56df5a5e..48b400f7c97 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2284,12 +2284,12 @@ impl<'tcx> TyCtxt<'tcx> { def_id1: DefId, def_id2: DefId, ) -> Option { + 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>>| -> 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), } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 4e7f68437e7..b69186c9451 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -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) { diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index 792457c80b0..faf404c7771 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -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 diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 970a0a8d4bf..06a6deeee43 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -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) +} diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index aff27e5664b..8e7012c2774 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -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)); } } diff --git a/compiler/rustc_monomorphize/src/partitioning/default.rs b/compiler/rustc_monomorphize/src/partitioning/default.rs index 3c7425d83c4..64968a76ab5 100644 --- a/compiler/rustc_monomorphize/src/partitioning/default.rs +++ b/compiler/rustc_monomorphize/src/partitioning/default.rs @@ -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 diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4a1abdf6318..0ed99353145 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -722,6 +722,8 @@ symbols! { fn_mut, fn_once, fn_once_output, + fn_ptr_addr, + fn_ptr_trait, forbid, forget, format, diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 995fec78c40..8dad3f312c9 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -153,6 +153,12 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable> + 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` // 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) { diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 14c5b83c6ca..2b104703aab 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -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>, diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index ade45d199f0..718c82c8f1f 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -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>, diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 98e00e8223b..89e91eb298b 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -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` 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. diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index e06eff34df2..234d773d64d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -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 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 Trait for Vec` 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; + } + } + } } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 2eaeca73da7..ad70154c98e 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -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 } diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 9a0fd1f5f51..c0cb12df5b0 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -922,3 +922,18 @@ mod copy_impls { #[stable(feature = "rust1", since = "1.0.0")] impl 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 (); +} diff --git a/tests/ui/fn/fn-ptr-trait.rs b/tests/ui/fn/fn-ptr-trait.rs new file mode 100644 index 00000000000..45918ae5b61 --- /dev/null +++ b/tests/ui/fn/fn-ptr-trait.rs @@ -0,0 +1,9 @@ +#![feature(fn_ptr_trait)] +// check-pass + +use std::marker::FnPtr; + +trait Foo {} +impl Foo for Vec where T: FnPtr {} + +fn main() {}