From 25a3ea519111ff1d7dbd0366fcabe48158e78fc1 Mon Sep 17 00:00:00 2001 From: skippy10110 Date: Tue, 19 Apr 2022 23:28:09 -0300 Subject: [PATCH] add a new linting pass for used obligations --- .../src/obligation_forest/mod.rs | 30 +++++++- .../src/obligation_forest/tests.rs | 1 + compiler/rustc_infer/src/infer/at.rs | 1 + compiler/rustc_infer/src/infer/mod.rs | 38 ++++++++-- .../src/traits/chalk_fulfill.rs | 18 +++-- .../src/traits/error_reporting/mod.rs | 3 +- .../src/traits/error_reporting/warnings.rs | 75 +++++++++++++++++++ .../src/traits/fulfill.rs | 9 ++- .../rustc_trait_selection/src/traits/mod.rs | 13 ---- .../src/traits/query/evaluate_obligation.rs | 6 +- .../src/traits/select/mod.rs | 4 +- .../rustc_traits/src/evaluate_obligation.rs | 3 +- compiler/rustc_typeck/src/check/coercion.rs | 17 +++-- library/core/src/task/wake.rs | 2 + ...precated-partialeq-fn-pointer.chalk.stderr | 26 +++++++ ...recated-partialeq-fn-pointer.normal.stderr | 26 +++++++ .../deprecated-partialeq-fn-pointer.rs | 31 ++++++++ 17 files changed, 263 insertions(+), 40 deletions(-) create mode 100644 compiler/rustc_trait_selection/src/traits/error_reporting/warnings.rs create mode 100644 src/test/ui/deprecated-partialeq-fn-pointer/deprecated-partialeq-fn-pointer.chalk.stderr create mode 100644 src/test/ui/deprecated-partialeq-fn-pointer/deprecated-partialeq-fn-pointer.normal.stderr create mode 100644 src/test/ui/deprecated-partialeq-fn-pointer/deprecated-partialeq-fn-pointer.rs diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index 5fe2a1fb84bd7..47c77c6c305da 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -102,6 +102,7 @@ pub trait ObligationProcessor { fn process_obligation( &mut self, obligation: &mut Self::Obligation, + tracer: impl Fn() -> Vec, ) -> ProcessResult; /// As we do the cycle check, we invoke this callback when we @@ -436,7 +437,11 @@ impl ObligationForest { // be computed with the initial length, and we would miss the appended // nodes. Therefore we use a `while` loop. let mut index = 0; - while let Some(node) = self.nodes.get_mut(index) { + while index < self.nodes.len() { + let (nodes, [node, ..]) = self.nodes.split_at_mut(index) else { + todo!("FIXME(skippy)") + }; + // `processor.process_obligation` can modify the predicate within // `node.obligation`, and that predicate is the key used for // `self.active_cache`. This means that `self.active_cache` can get @@ -447,7 +452,28 @@ impl ObligationForest { continue; } - match processor.process_obligation(&mut node.obligation) { + let parent_index = if node.has_parent { Some(node.dependents[0]) } else { None }; + let tracer = || { + let mut trace = vec![]; + if let Some(mut index) = parent_index { + loop { + let node = &nodes[index]; + trace.push(node.obligation.clone()); + if node.has_parent { + // The first dependent is the parent, which is treated + // specially. + index = node.dependents[0]; + } else { + // No parent; treat all dependents non-specially. + break; + } + } + } + + trace + }; + + match processor.process_obligation(&mut node.obligation, tracer) { ProcessResult::Unchanged => { // No change in state. } diff --git a/compiler/rustc_data_structures/src/obligation_forest/tests.rs b/compiler/rustc_data_structures/src/obligation_forest/tests.rs index 371c62c063fa7..e850d4f101b37 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/tests.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/tests.rs @@ -77,6 +77,7 @@ where fn process_obligation( &mut self, obligation: &mut Self::Obligation, + _tracer: impl Fn() -> Vec, ) -> ProcessResult { (self.process_obligation)(obligation) } diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 58c309a5c52ea..82e15d208e83e 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -77,6 +77,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err_count_on_creation: self.err_count_on_creation, in_snapshot: self.in_snapshot.clone(), universe: self.universe.clone(), + query_mode: self.query_mode.clone(), } } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 6ec929f98950e..3fc1b0577b51a 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -354,6 +354,21 @@ pub struct InferCtxt<'a, 'tcx> { /// when we enter into a higher-ranked (`for<..>`) type or trait /// bound. universe: Cell, + + pub query_mode: TraitQueryMode, +} + +/// The mode that trait queries run in. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum TraitQueryMode { + /// Standard/un-canonicalized queries get accurate + /// spans etc. passed in and hence can do reasonable + /// error reporting on their own. + Standard, + /// Canonicalized queries get dummy spans and hence + /// must generally propagate errors to + /// pre-canonicalization callsites. + Canonical, } /// See the `error_reporting` module for more details. @@ -615,14 +630,26 @@ impl<'tcx> InferCtxtBuilder<'tcx> { where T: TypeFoldable<'tcx>, { - self.enter(|infcx| { - let (value, subst) = - infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical); - f(infcx, value, subst) - }) + self.enter_with_query_mode( + |infcx| { + let (value, subst) = + infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical); + f(infcx, value, subst) + }, + TraitQueryMode::Canonical, + ) } pub fn enter(&mut self, f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>) -> R) -> R { + self.enter_with_query_mode(f, TraitQueryMode::Standard) + } + + // FIXME(skippy) specifically needs reviewer feedback (see query_mode field) + fn enter_with_query_mode( + &mut self, + f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>) -> R, + query_mode: TraitQueryMode, + ) -> R { let InferCtxtBuilder { tcx, defining_use_anchor, ref fresh_typeck_results } = *self; let in_progress_typeck_results = fresh_typeck_results.as_ref(); f(InferCtxt { @@ -640,6 +667,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { in_snapshot: Cell::new(false), skip_leak_check: Cell::new(false), universe: Cell::new(ty::UniverseIndex::ROOT), + query_mode, }) } } diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index 93c2f202545f6..62f67cee7f4d7 100644 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs @@ -2,6 +2,7 @@ use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferCtxt; +use crate::traits::error_reporting::warnings::InferCtxtExt as _; use crate::traits::query::NoSolution; use crate::traits::{ ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, ObligationCause, @@ -109,12 +110,17 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { &orig_values, &response, ) { - Ok(infer_ok) => next_round.extend( - infer_ok.obligations.into_iter().map(|obligation| { - assert!(!infcx.is_in_snapshot()); - infcx.resolve_vars_if_possible(obligation) - }), - ), + Ok(infer_ok) => { + infcx.check_obligation_lints(&obligation, || { + vec![obligation.clone()] + }); + next_round.extend(infer_ok.obligations.into_iter().map( + |obligation| { + assert!(!infcx.is_in_snapshot()); + infcx.resolve_vars_if_possible(obligation) + }, + )) + } Err(_err) => errors.push(FulfillmentError { obligation: obligation.clone(), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 50e4fafdd6c82..7e38dacf193ef 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1,5 +1,6 @@ pub mod on_unimplemented; pub mod suggestions; +pub mod warnings; use super::{ DerivedObligationCause, EvaluationResult, FulfillmentContext, FulfillmentError, @@ -2067,7 +2068,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { ); let mut selcx = SelectionContext::with_query_mode( &self, - crate::traits::TraitQueryMode::Standard, + crate::infer::TraitQueryMode::Standard, ); match selcx.select_from_obligation(&obligation) { Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/warnings.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/warnings.rs new file mode 100644 index 0000000000000..c2185ef7e8c12 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/warnings.rs @@ -0,0 +1,75 @@ +use super::PredicateObligation; + +use crate::infer::InferCtxt; + +use hir::{ItemKind, TyKind}; +use rustc_hir as hir; +use rustc_hir::Node; +use rustc_infer::infer::TraitQueryMode; +use rustc_lint_defs::builtin::DEPRECATED_IN_FUTURE; +use rustc_middle::ty::{self, Binder}; + +pub trait InferCtxtExt<'tcx> { + fn check_obligation_lints( + &self, + obligation: &PredicateObligation<'tcx>, + tracer: impl Fn() -> Vec>, + ); +} + +impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { + fn check_obligation_lints( + &self, + obligation: &PredicateObligation<'tcx>, + tracer: impl Fn() -> Vec>, + ) { + if self.query_mode != TraitQueryMode::Standard { + return; + } + + if let Some(trait_obligation) = { + let binder = obligation.predicate.kind(); + match binder.no_bound_vars() { + None => match binder.skip_binder() { + ty::PredicateKind::Trait(data) => Some(obligation.with(binder.rebind(data))), + _ => None, + }, + Some(pred) => match pred { + ty::PredicateKind::Trait(data) => Some(obligation.with(Binder::dummy(data))), + _ => None, + }, + } + } { + let self_ty = trait_obligation.self_ty().skip_binder(); + let trait_ref = trait_obligation.predicate.skip_binder().trait_ref; + + // FIXME(skippy) lint experiment: deprecate PartialEq on function pointers + if let ty::FnPtr(_) = self_ty.kind() + && self.tcx.lang_items().eq_trait() == Some(trait_ref.def_id) + { + let node = self.tcx.hir().get(trait_obligation.cause.body_id); + let mut skip = false; + if let Node::Item(item) = node + && let ItemKind::Impl(impl_item) = &item.kind + && let TyKind::BareFn(_) = impl_item.self_ty.kind + { + skip = true; + } + if !skip { + let root_obligation = tracer().last().unwrap_or(&obligation).clone(); + self.tcx.struct_span_lint_hir( + DEPRECATED_IN_FUTURE, + root_obligation.cause.body_id, + root_obligation.cause.span, + |lint| { + lint.build( + "FIXME(skippy) PartialEq on function pointers has been deprecated.", + ) + .emit(); + }, + ); + } + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 053e871c14f6e..52cf232a92d9b 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -25,6 +25,7 @@ use super::Unimplemented; use super::{FulfillmentError, FulfillmentErrorCode}; use super::{ObligationCause, PredicateObligation}; +use crate::traits::error_reporting::warnings::InferCtxtExt as _; use crate::traits::error_reporting::InferCtxtExt as _; use crate::traits::project::PolyProjectionObligation; use crate::traits::project::ProjectionCacheKeyExt as _; @@ -275,6 +276,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { fn process_obligation( &mut self, pending_obligation: &mut Self::Obligation, + tracer: impl Fn() -> Vec, ) -> ProcessResult { // If we were stalled on some unresolved variables, first check whether // any of them have been resolved; if not, don't bother doing more work @@ -313,7 +315,12 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { return ProcessResult::Unchanged; } - self.process_changed_obligations(pending_obligation) + let result = self.process_changed_obligations(pending_obligation); + if let ProcessResult::Changed(_) = result { + let tracer = || tracer().into_iter().map(|x| x.obligation).collect(); + self.selcx.infcx().check_obligation_lints(&pending_obligation.obligation, tracer); + } + result } fn process_backedge<'c, I>( diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 8240f5c542a61..4491bf75e21db 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -98,19 +98,6 @@ impl SkipLeakCheck { } } -/// The mode that trait queries run in. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum TraitQueryMode { - /// Standard/un-canonicalized queries get accurate - /// spans etc. passed in and hence can do reasonable - /// error reporting on their own. - Standard, - /// Canonicalized queries get dummy spans and hence - /// must generally propagate errors to - /// pre-canonicalization callsites. - Canonical, -} - /// Creates predicate obligations from the generic bounds. pub fn predicates_for_generics<'tcx>( cause: ObligationCause<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index db45ee3fed7db..c341d63d4e1b3 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,10 +1,8 @@ use rustc_middle::ty; use crate::infer::canonical::OriginalQueryValues; -use crate::infer::InferCtxt; -use crate::traits::{ - EvaluationResult, OverflowError, PredicateObligation, SelectionContext, TraitQueryMode, -}; +use crate::infer::{InferCtxt, TraitQueryMode}; +use crate::traits::{EvaluationResult, OverflowError, PredicateObligation, SelectionContext}; pub trait InferCtxtExt<'tcx> { fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index e69bf6eca90ef..0ad44b3d2f70b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -16,10 +16,10 @@ use super::wf; use super::{ DerivedObligationCause, ErrorReporting, ImplDerivedObligation, ImplDerivedObligationCause, Normalized, Obligation, ObligationCause, ObligationCauseCode, Overflow, PredicateObligation, - Selection, SelectionError, SelectionResult, TraitObligation, TraitQueryMode, + Selection, SelectionError, SelectionResult, TraitObligation, }; -use crate::infer::{InferCtxt, InferOk, TypeFreshener}; +use crate::infer::{InferCtxt, InferOk, TraitQueryMode, TypeFreshener}; use crate::traits::error_reporting::InferCtxtExt; use crate::traits::project::ProjectAndUnifyResult; use crate::traits::project::ProjectionCacheKeyExt; diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index 2404b7ff4b54a..1325f18b1cfa9 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -2,9 +2,10 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use rustc_span::source_map::DUMMY_SP; +use rustc_trait_selection::infer::TraitQueryMode; use rustc_trait_selection::traits::query::CanonicalPredicateGoal; use rustc_trait_selection::traits::{ - EvaluationResult, Obligation, ObligationCause, OverflowError, SelectionContext, TraitQueryMode, + EvaluationResult, Obligation, ObligationCause, OverflowError, SelectionContext, }; crate fn provide(p: &mut Providers) { diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index b44baf83cbe1e..186a5d95ca3ad 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -58,7 +58,8 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{self, BytePos, DesugaringKind, Span}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::warnings::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; use smallvec::{smallvec, SmallVec}; @@ -604,15 +605,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // and almost never more than 3. By using a SmallVec we avoid an // allocation, at the (very small) cost of (occasionally) having to // shift subsequent elements down when removing the front element. - let mut queue: SmallVec<[_; 4]> = smallvec![traits::predicate_for_trait_def( + let root_obligation = traits::predicate_for_trait_def( self.tcx, self.fcx.param_env, cause, coerce_unsized_did, 0, coerce_source, - &[coerce_target.into()] - )]; + &[coerce_target.into()], + ); + // FIXME(skippy) extra .clone() here due to root_obligation + let mut queue: SmallVec<[_; 4]> = smallvec![root_obligation.clone()]; let mut has_unsized_tuple_coercion = false; let mut has_trait_upcasting_coercion = false; @@ -692,7 +695,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // be silent, as it causes a type mismatch later. } - Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), + Ok(Some(impl_source)) => { + // FIXME(skippy) just has the root_obligation, not the entire trace + self.check_obligation_lints(&obligation, || vec![root_obligation.clone()]); + queue.extend(impl_source.nested_obligations()); + } } } diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 27af227a1f27f..a35c382b0abe8 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -73,6 +73,8 @@ impl RawWaker { /// any other `data` pointer will cause undefined behavior. #[stable(feature = "futures_api", since = "1.36.0")] #[derive(PartialEq, Copy, Clone, Debug)] +// FIXME(skippy) for PartialEq on function pointers +#[allow(deprecated_in_future)] pub struct RawWakerVTable { /// This function will be called when the [`RawWaker`] gets cloned, e.g. when /// the [`Waker`] in which the [`RawWaker`] is stored gets cloned. diff --git a/src/test/ui/deprecated-partialeq-fn-pointer/deprecated-partialeq-fn-pointer.chalk.stderr b/src/test/ui/deprecated-partialeq-fn-pointer/deprecated-partialeq-fn-pointer.chalk.stderr new file mode 100644 index 0000000000000..f1c413a376172 --- /dev/null +++ b/src/test/ui/deprecated-partialeq-fn-pointer/deprecated-partialeq-fn-pointer.chalk.stderr @@ -0,0 +1,26 @@ +warning: FIXME(skippy) PartialEq on function pointers has been deprecated. + --> $DIR/deprecated-partialeq-fn-pointer.rs:25:6 + | +LL | impl Bla2 for u32 {} + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/deprecated-partialeq-fn-pointer.rs:5:9 + | +LL | #![warn(deprecated_in_future)] + | ^^^^^^^^^^^^^^^^^^^^ + +warning: FIXME(skippy) PartialEq on function pointers has been deprecated. + --> $DIR/deprecated-partialeq-fn-pointer.rs:16:19 + | +LL | let _ = x == y; + | ^^ + +warning: FIXME(skippy) PartialEq on function pointers has been deprecated. + --> $DIR/deprecated-partialeq-fn-pointer.rs:30:15 + | +LL | let _ = x == y; + | ^^ + +warning: 3 warnings emitted + diff --git a/src/test/ui/deprecated-partialeq-fn-pointer/deprecated-partialeq-fn-pointer.normal.stderr b/src/test/ui/deprecated-partialeq-fn-pointer/deprecated-partialeq-fn-pointer.normal.stderr new file mode 100644 index 0000000000000..f1c413a376172 --- /dev/null +++ b/src/test/ui/deprecated-partialeq-fn-pointer/deprecated-partialeq-fn-pointer.normal.stderr @@ -0,0 +1,26 @@ +warning: FIXME(skippy) PartialEq on function pointers has been deprecated. + --> $DIR/deprecated-partialeq-fn-pointer.rs:25:6 + | +LL | impl Bla2 for u32 {} + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/deprecated-partialeq-fn-pointer.rs:5:9 + | +LL | #![warn(deprecated_in_future)] + | ^^^^^^^^^^^^^^^^^^^^ + +warning: FIXME(skippy) PartialEq on function pointers has been deprecated. + --> $DIR/deprecated-partialeq-fn-pointer.rs:16:19 + | +LL | let _ = x == y; + | ^^ + +warning: FIXME(skippy) PartialEq on function pointers has been deprecated. + --> $DIR/deprecated-partialeq-fn-pointer.rs:30:15 + | +LL | let _ = x == y; + | ^^ + +warning: 3 warnings emitted + diff --git a/src/test/ui/deprecated-partialeq-fn-pointer/deprecated-partialeq-fn-pointer.rs b/src/test/ui/deprecated-partialeq-fn-pointer/deprecated-partialeq-fn-pointer.rs new file mode 100644 index 0000000000000..6f969479f116c --- /dev/null +++ b/src/test/ui/deprecated-partialeq-fn-pointer/deprecated-partialeq-fn-pointer.rs @@ -0,0 +1,31 @@ +// check-pass +// revisions: normal chalk +// [chalk]compile-flags: -Zchalk + +#![warn(deprecated_in_future)] + +fn test() {} + +trait Bla { + fn foo(); +} +impl Bla for u32 { + fn foo() { + let x: fn() = main; + let y: fn() = test; + let _ = x == y; //~ WARN FIXME(skippy) PartialEq on function pointers has been deprecated. + } +} + +trait Bla2 +where + T: PartialEq, +{ +} +impl Bla2 for u32 {} //~ WARN FIXME(skippy) PartialEq on function pointers has been deprecated. + +fn main() { + let x: fn() = main; + let y: fn() = test; + let _ = x == y; //~ WARN FIXME(skippy) PartialEq on function pointers has been deprecated. +}