From ed4dd0ca460a3761349375090748f8c31580a9bf Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 25 Jul 2022 15:53:47 +0200 Subject: [PATCH 1/4] cool new attribute --- compiler/rustc_feature/src/builtin_attrs.rs | 5 +++++ compiler/rustc_passes/src/check_attr.rs | 21 ++++++++++++++++++ compiler/rustc_span/src/symbol.rs | 1 + .../src/traits/select/candidate_assembly.rs | 22 +++++++++++++++++-- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index c806df8214586..d2dbc756c0e9a 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -605,6 +605,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_conversion_suggestion, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE ), + + rustc_attr!( + rustc_select_quick_discard, Normal, template!(Word), ErrorFollowing, INTERNAL_UNSTABLE + ), + // Prevents field reads in the marked trait or method to be considered // during dead code analysis. rustc_attr!( diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f88997f884a44..9dba62b36a447 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -121,6 +121,9 @@ impl CheckAttrVisitor<'_> { sym::rustc_lint_diagnostics => { self.check_rustc_lint_diagnostics(&attr, span, target) } + sym::rustc_select_quick_discard => { + self.check_rustc_select_quick_discard(&attr, span, target) + } sym::rustc_clean | sym::rustc_dirty | sym::rustc_if_this_changed @@ -1431,6 +1434,24 @@ impl CheckAttrVisitor<'_> { self.check_applied_to_fn_or_method(attr, span, target) } + fn check_rustc_select_quick_discard( + &self, + attr: &Attribute, + span: Span, + target: Target, + ) -> bool { + if matches!(target, Target::Impl) { + true + } else { + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to an impl") + .span_label(span, "not an impl") + .emit(); + false + } + } + /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph /// option is passed to the compiler. fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 54d67c5254188..c9a41b960310e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1260,6 +1260,7 @@ symbols! { rustc_promotable, rustc_regions, rustc_reservation_impl, + rustc_select_quick_discard, rustc_serialize, rustc_skip_array_during_method_dispatch, rustc_specialization_trait, 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 6e8581128dd8e..ff517c0d115ed 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -13,6 +13,7 @@ use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable}; +use rustc_span::sym; use rustc_target::spec::abi::Abi; use crate::traits; @@ -135,8 +136,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // candidate which assumes $0 == int, one that assumes `$0 == // usize`, etc. This spells an ambiguity. - let mut candidates = self.filter_impls(candidates, stack.obligation); - + let candidates = self.filter_impls(candidates, stack.obligation); + let mut candidates = candidates + .into_iter() + .map(|c| match c { + ImplCandidate(impl_def) + if self.tcx().has_attr(impl_def, sym::rustc_select_quick_discard) => + { + match self.evaluate_candidate(stack, &c) { + Ok(eval) if eval.may_apply() => Ok(Some(c)), + Ok(_) => Ok(None), + Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)), + Err(OverflowError::ErrorReporting) => Err(ErrorReporting), + Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))), + } + } + _ => Ok(Some(c)), + }) + .flat_map(Result::transpose) + .collect::, _>>()?; // If there is more than one candidate, first winnow them down // by considering extra conditions (nested obligations and so // forth). We don't winnow if there is exactly one From 04accf14959c1d28b5fef2198d18bc8625bb6dfa Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 20 Jul 2022 14:32:58 +0200 Subject: [PATCH 2/4] add a builtin `FnPtr` trait --- .../src/interpret/terminator.rs | 1 + compiler/rustc_hir/src/lang_items.rs | 3 ++ compiler/rustc_middle/src/mir/mono.rs | 3 +- compiler/rustc_middle/src/mir/visit.rs | 3 +- compiler/rustc_middle/src/ty/instance.rs | 18 ++++++-- compiler/rustc_middle/src/ty/mod.rs | 3 +- .../rustc_middle/src/ty/structural_impls.rs | 8 +++- compiler/rustc_mir_transform/src/inline.rs | 3 +- .../rustc_mir_transform/src/inline/cycle.rs | 5 ++- compiler/rustc_mir_transform/src/shim.rs | 36 ++++++++++++++++ compiler/rustc_monomorphize/src/collector.rs | 3 +- .../src/partitioning/default.rs | 6 ++- compiler/rustc_span/src/symbol.rs | 2 + .../src/traits/select/candidate_assembly.rs | 41 +++++++++++++++++++ compiler/rustc_ty_utils/src/instance.rs | 19 ++++++++- compiler/rustc_typeck/src/coherence/mod.rs | 12 ++++++ library/core/src/marker.rs | 10 +++++ 17 files changed, 163 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 42de0dbdca928..14539c22fe73f 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -370,6 +370,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 13b3e954e1f58..92b0c57b1d699 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -185,6 +185,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 21ae121e1ce69..f603327f149eb 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -369,7 +369,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 891608764017c..41dfe935a4da1 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -402,7 +402,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 33a46f809b0d3..46a519818f1ff 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -97,6 +97,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> { @@ -152,7 +159,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, } } @@ -168,7 +176,8 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::Intrinsic(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::DropGlue(..) - | InstanceDef::CloneShim(..) => None, + | InstanceDef::CloneShim(..) + | InstanceDef::FnPtrAddrShim(..) => None, } } @@ -183,7 +192,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), } } @@ -265,6 +275,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 { .. } @@ -299,6 +310,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> { 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 281a126554694..621359c26156c 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2148,7 +2148,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_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 1f1f80ed61828..1d018e8601355 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -640,6 +640,9 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { ty::InstanceDef::CloneShim(def_id, ty) => { Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?)) } + ty::InstanceDef::FnPtrAddrShim(def_id, ty) => { + Some(ty::InstanceDef::FnPtrAddrShim(def_id, tcx.lift(ty)?)) + } } } } @@ -943,6 +946,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { CloneShim(did, ty) => { CloneShim(did.try_fold_with(folder)?, ty.try_fold_with(folder)?) } + FnPtrAddrShim(did, ty) => { + FnPtrAddrShim(did.try_fold_with(folder)?, ty.try_fold_with(folder)?) + } }, }) } @@ -957,7 +963,7 @@ impl<'tcx> TypeVisitable<'tcx> for ty::instance::Instance<'tcx> { VTableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { did.visit_with(visitor) } - FnPtrShim(did, ty) | CloneShim(did, ty) => { + FnPtrShim(did, ty) | CloneShim(did, ty) | FnPtrAddrShim(did, ty) => { did.visit_with(visitor)?; ty.visit_with(visitor) } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 1e46b0a0e8164..cc324d88eddc4 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -251,7 +251,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 7810218fd6744..9a870443c8ca7 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -79,11 +79,14 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( // These have MIR and if that MIR is inlined, substituted and then inlining is run // again, a function item can end up getting inlined. Thus we'll be able to cause // a cycle that way + // + // FIXME: `FnPtrAddrShim` should not be able to cause recursion. InstanceDef::VTableShim(_) | InstanceDef::ReifyShim(_) | InstanceDef::FnPtrShim(..) | InstanceDef::ClosureOnceShim { .. } - | InstanceDef::CloneShim(..) => {} + | InstanceDef::CloneShim(..) + | InstanceDef::FnPtrAddrShim(..) => {} 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 3620e94bec7d7..2b42ab1cda398 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -78,6 +78,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) } @@ -788,3 +789,38 @@ 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.bound_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); + let rvalue = Rvalue::Cast( + CastKind::PointerExposeAddress, + Operand::Move(Place::from(Local::new(1))), + tcx.types.usize, + ); + 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 68b65658c72b5..f7d98af5cbec8 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -997,7 +997,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 15276569c32f5..b4504a004e5fe 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 c9a41b960310e..a31934bf916c5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -728,6 +728,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/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index ff517c0d115ed..e2c9dd7551cff 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -323,6 +323,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_candidates_for_unsizing(obligation, &mut candidates); } else if lang_items.destruct_trait() == Some(def_id) { self.assemble_const_destruct_candidates(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 @@ -1002,4 +1004,43 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + 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::Never + | ty::Tuple(..) + | ty::Projection(..) + | ty::Opaque(..) + | 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 979e997f24491..ec64451d29f08 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -363,7 +363,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 { @@ -387,6 +388,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/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs index ae9ebe5909144..ee41c1732e067 100644 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ b/compiler/rustc_typeck/src/coherence/mod.rs @@ -68,6 +68,18 @@ fn enforce_trait_manually_implementable( return; } + if did == li.fn_ptr_trait() { + struct_span_err!( + tcx.sess, + impl_header_span, + E0322, + "explicit impls for the `FnPtr` trait are not permitted" + ) + .span_label(impl_header_span, "impl of `FnPtr` not allowed") + .emit(); + return; + } + if did == li.sized_trait() { struct_span_err!( tcx.sess, diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 2c57897956fcd..b4073c1d37b6d 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -838,3 +838,13 @@ 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 = "recently added")] +#[lang = "fn_ptr_trait"] +#[cfg(not(bootstrap))] +pub trait FnPtr { + /// Returns the adress of the function pointer. + #[lang = "fn_ptr_addr"] + fn addr(self) -> usize; +} From 0cdb95969c73c7df69a93244d978ad49dfeeb8b3 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 20 Jul 2022 23:26:57 +0200 Subject: [PATCH 3/4] use the `FnPtr` trait --- .../src/traits/coherence.rs | 7 + library/core/src/marker.rs | 2 +- library/core/src/ptr/mod.rs | 276 +++++++++++------- 3 files changed, 177 insertions(+), 108 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 9983438233e1e..d60d00dde6409 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -412,6 +412,13 @@ pub fn trait_ref_is_knowable<'tcx>( trait_ref: ty::TraitRef<'tcx>, ) -> Option { debug!("trait_ref_is_knowable(trait_ref={:?})", trait_ref); + 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 None; + } + if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() { // A downstream or cousin crate is allowed to implement some // substitution of this trait-ref. diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index b4073c1d37b6d..05ceec182c178 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -843,7 +843,7 @@ mod copy_impls { #[unstable(feature = "fn_ptr_trait", issue = "none", reason = "recently added")] #[lang = "fn_ptr_trait"] #[cfg(not(bootstrap))] -pub trait FnPtr { +pub trait FnPtr: Copy + Clone { /// Returns the adress of the function pointer. #[lang = "fn_ptr_addr"] fn addr(self) -> usize; diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 40e28e636d851..2d0685e1fb866 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1825,138 +1825,200 @@ pub fn hash(hashee: *const T, into: &mut S) { hashee.hash(into); } -// If this is a unary fn pointer, it adds a doc comment. -// Otherwise, it hides the docs entirely. -macro_rules! maybe_fnptr_doc { - (@ #[$meta:meta] $item:item) => { - #[doc(hidden)] - #[$meta] - $item - }; - ($a:ident @ #[$meta:meta] $item:item) => { - #[cfg_attr(not(bootstrap), doc(fake_variadic))] - #[doc = "This trait is implemented for function pointers with up to twelve arguments."] - #[$meta] - $item - }; - ($a:ident $($rest_a:ident)+ @ #[$meta:meta] $item:item) => { - #[doc(hidden)] - #[$meta] - $item - }; -} +#[cfg(bootstrap)] +mod old_fn_ptr_impl { + use super::*; + // If this is a unary fn pointer, it adds a doc comment. + // Otherwise, it hides the docs entirely. + macro_rules! maybe_fnptr_doc { + (@ #[$meta:meta] $item:item) => { + #[doc(hidden)] + #[$meta] + $item + }; + ($a:ident @ #[$meta:meta] $item:item) => { + #[cfg_attr(not(bootstrap), doc(fake_variadic))] + #[doc = "This trait is implemented for function pointers with up to twelve arguments."] + #[$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 -// 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 -// have a proper "opaque function pointer type" to handle this kind of thing. - -// Impls for function pointers -macro_rules! fnptr_impls_safety_abi { - ($FnTy: ty, $($Arg: ident),*) => { - maybe_fnptr_doc! { - $($Arg)* @ - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl PartialEq for $FnTy { - #[inline] - fn eq(&self, other: &Self) -> bool { - *self as usize == *other as usize + // FIXME(strict_provenance_magic): function pointers have buggy codegen that + // 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 + // have a proper "opaque function pointer type" to handle this kind of thing. + + // Impls for function pointers + macro_rules! fnptr_impls_safety_abi { + ($FnTy: ty, $($Arg: ident),*) => { + maybe_fnptr_doc! { + $($Arg)* @ + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl PartialEq for $FnTy { + #[inline] + fn eq(&self, other: &Self) -> bool { + *self as usize == *other as usize + } } } - } - maybe_fnptr_doc! { - $($Arg)* @ - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl Eq for $FnTy {} - } + maybe_fnptr_doc! { + $($Arg)* @ + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl Eq for $FnTy {} + } - maybe_fnptr_doc! { - $($Arg)* @ - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl PartialOrd for $FnTy { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - (*self as usize).partial_cmp(&(*other as usize)) + maybe_fnptr_doc! { + $($Arg)* @ + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl PartialOrd for $FnTy { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + (*self as usize).partial_cmp(&(*other as usize)) + } } } - } - maybe_fnptr_doc! { - $($Arg)* @ - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl Ord for $FnTy { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - (*self as usize).cmp(&(*other as usize)) + maybe_fnptr_doc! { + $($Arg)* @ + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl Ord for $FnTy { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + (*self as usize).cmp(&(*other as usize)) + } } } - } - maybe_fnptr_doc! { - $($Arg)* @ - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl hash::Hash for $FnTy { - fn hash(&self, state: &mut HH) { - state.write_usize(*self as usize) + maybe_fnptr_doc! { + $($Arg)* @ + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl hash::Hash for $FnTy { + fn hash(&self, state: &mut HH) { + state.write_usize(*self as usize) + } } } - } - maybe_fnptr_doc! { - $($Arg)* @ - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl fmt::Pointer for $FnTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::pointer_fmt_inner(*self as usize, f) + maybe_fnptr_doc! { + $($Arg)* @ + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl fmt::Pointer for $FnTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::pointer_fmt_inner(*self as usize, f) + } } } - } - maybe_fnptr_doc! { - $($Arg)* @ - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl fmt::Debug for $FnTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::pointer_fmt_inner(*self as usize, f) + maybe_fnptr_doc! { + $($Arg)* @ + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl fmt::Debug for $FnTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::pointer_fmt_inner(*self as usize, f) + } } } } } -} -macro_rules! fnptr_impls_args { - ($($Arg: ident),+) => { - 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! { 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),+ } - }; - () => { - // No variadic functions with 0 parameters - fnptr_impls_safety_abi! { extern "Rust" fn() -> Ret, } - fnptr_impls_safety_abi! { extern "C" fn() -> Ret, } - fnptr_impls_safety_abi! { unsafe extern "Rust" fn() -> Ret, } - fnptr_impls_safety_abi! { unsafe extern "C" fn() -> Ret, } - }; + macro_rules! fnptr_impls_args { + ($($Arg: ident),+) => { + 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! { 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),+ } + }; + () => { + // No variadic functions with 0 parameters + fnptr_impls_safety_abi! { extern "Rust" fn() -> Ret, } + fnptr_impls_safety_abi! { extern "C" fn() -> Ret, } + fnptr_impls_safety_abi! { unsafe extern "Rust" fn() -> Ret, } + fnptr_impls_safety_abi! { unsafe extern "C" 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! {} -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 } +#[cfg(not(bootstrap))] +mod new_fn_ptr_impl { + use super::*; + use crate::marker::FnPtr; + + #[stable(feature = "fnptr_impls", since = "1.4.0")] + #[rustc_select_quick_discard] + impl PartialEq for F { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.addr() == other.addr() + } + } + #[stable(feature = "fnptr_impls", since = "1.4.0")] + #[rustc_select_quick_discard] + impl Eq for F {} + + #[stable(feature = "fnptr_impls", since = "1.4.0")] + #[rustc_select_quick_discard] + impl PartialOrd for F { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.addr().partial_cmp(&other.addr()) + } + } + #[stable(feature = "fnptr_impls", since = "1.4.0")] + #[rustc_select_quick_discard] + impl Ord for F { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.addr().cmp(&other.addr()) + } + } + + #[stable(feature = "fnptr_impls", since = "1.4.0")] + #[rustc_select_quick_discard] + impl hash::Hash for F { + fn hash(&self, state: &mut HH) { + state.write_usize(self.addr()) + } + } + #[stable(feature = "fnptr_impls", since = "1.4.0")] + #[rustc_select_quick_discard] + impl fmt::Pointer for F { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::pointer_fmt_inner(self.addr(), f) + } + } + + #[stable(feature = "fnptr_impls", since = "1.4.0")] + #[rustc_select_quick_discard] + impl fmt::Debug for F { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::pointer_fmt_inner(self.addr(), f) + } + } +} /// 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 From 9d0a8e5eb0adf5aedd57db8dd0acc5967da3c92a Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 25 Jul 2022 16:10:00 +0200 Subject: [PATCH 4/4] update tests --- src/test/ui/issues/issue-59488.stderr | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/test/ui/issues/issue-59488.stderr b/src/test/ui/issues/issue-59488.stderr index 7ce3dedaa887b..47fd9fdb65472 100644 --- a/src/test/ui/issues/issue-59488.stderr +++ b/src/test/ui/issues/issue-59488.stderr @@ -94,16 +94,6 @@ LL | assert_eq!(Foo::Bar, i); | ^^^^^^^^^^^^^^^^^^^^^^^ `fn(usize) -> Foo {Foo::Bar}` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = help: the trait `Debug` is not implemented for `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 68 others = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 9 previous errors