From c98b4c8fdde7812d7af5a060a1e22fd7e3775d3f Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sat, 13 Jun 2020 11:03:31 -0400 Subject: [PATCH] Add error note when trying fn as Fn trait --- .../traits/error_reporting/mod.rs | 21 +++++++++ .../rfc-2396-target_feature-11/fn-traits.rs | 10 ++++ .../fn-traits.stderr | 47 +++++++++++++++++-- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs index 1b72a4bf84f1..39530853318e 100644 --- a/src/librustc_trait_selection/traits/error_reporting/mod.rs +++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs @@ -286,6 +286,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .starts_with("std::convert::From<"); let is_unsize = { Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() }; + let is_fn_trait = [ + self.tcx.lang_items().fn_trait(), + self.tcx.lang_items().fn_mut_trait(), + self.tcx.lang_items().fn_once_trait(), + ] + .contains(&Some(trait_ref.def_id())); + let is_safe_target_feature_fn = + if let ty::FnDef(def_id, _) = trait_ref.skip_binder().self_ty().kind { + trait_ref.skip_binder().self_ty().fn_sig(self.tcx).unsafety() + == hir::Unsafety::Normal + && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() + } else { + false + }; let (message, note) = if is_try && is_from { ( Some(format!( @@ -427,6 +441,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ); } + if is_fn_trait && is_safe_target_feature_fn { + err.note(&format!( + "`{}` has `#[target_feature]` and is unsafe to call", + trait_ref.skip_binder().self_ty(), + )); + } + // Try to report a help message if !trait_ref.has_infer_types_or_consts() && self.predicate_can_apply(obligation.param_env, trait_ref) diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs index 58070b69bc1f..5c838fd719cd 100644 --- a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs @@ -5,6 +5,9 @@ #[target_feature(enable = "avx")] fn foo() {} +#[target_feature(enable = "avx")] +unsafe fn foo_unsafe() {} + fn call(f: impl Fn()) { f() } @@ -21,4 +24,11 @@ fn main() { call(foo); //~ ERROR expected a `std::ops::Fn<()>` closure, found `fn() {foo}` call_mut(foo); //~ ERROR expected a `std::ops::FnMut<()>` closure, found `fn() {foo}` call_once(foo); //~ ERROR expected a `std::ops::FnOnce<()>` closure, found `fn() {foo}` + + call(foo_unsafe); + //~^ ERROR expected a `std::ops::Fn<()>` closure, found `unsafe fn() {foo_unsafe}` + call_mut(foo_unsafe); + //~^ ERROR expected a `std::ops::FnMut<()>` closure, found `unsafe fn() {foo_unsafe}` + call_once(foo_unsafe); + //~^ ERROR expected a `std::ops::FnOnce<()>` closure, found `unsafe fn() {foo_unsafe}` } diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr index 6dcf31b4c742..3ae85af76d30 100644 --- a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr +++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr @@ -1,5 +1,5 @@ error[E0277]: expected a `std::ops::Fn<()>` closure, found `fn() {foo}` - --> $DIR/fn-traits.rs:21:10 + --> $DIR/fn-traits.rs:24:10 | LL | fn call(f: impl Fn()) { | ---- required by this bound in `call` @@ -9,9 +9,10 @@ LL | call(foo); | = help: the trait `std::ops::Fn<()>` is not implemented for `fn() {foo}` = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ } + = note: `fn() {foo}` has `#[target_feature]` and is unsafe to call error[E0277]: expected a `std::ops::FnMut<()>` closure, found `fn() {foo}` - --> $DIR/fn-traits.rs:22:14 + --> $DIR/fn-traits.rs:25:14 | LL | fn call_mut(f: impl FnMut()) { | ------- required by this bound in `call_mut` @@ -21,9 +22,10 @@ LL | call_mut(foo); | = help: the trait `std::ops::FnMut<()>` is not implemented for `fn() {foo}` = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ } + = note: `fn() {foo}` has `#[target_feature]` and is unsafe to call error[E0277]: expected a `std::ops::FnOnce<()>` closure, found `fn() {foo}` - --> $DIR/fn-traits.rs:23:15 + --> $DIR/fn-traits.rs:26:15 | LL | fn call_once(f: impl FnOnce()) { | -------- required by this bound in `call_once` @@ -33,7 +35,44 @@ LL | call_once(foo); | = help: the trait `std::ops::FnOnce<()>` is not implemented for `fn() {foo}` = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ } + = note: `fn() {foo}` has `#[target_feature]` and is unsafe to call -error: aborting due to 3 previous errors +error[E0277]: expected a `std::ops::Fn<()>` closure, found `unsafe fn() {foo_unsafe}` + --> $DIR/fn-traits.rs:28:10 + | +LL | fn call(f: impl Fn()) { + | ---- required by this bound in `call` +... +LL | call(foo_unsafe); + | ^^^^^^^^^^ expected an `Fn<()>` closure, found `unsafe fn() {foo_unsafe}` + | + = help: the trait `std::ops::Fn<()>` is not implemented for `unsafe fn() {foo_unsafe}` + = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ } + +error[E0277]: expected a `std::ops::FnMut<()>` closure, found `unsafe fn() {foo_unsafe}` + --> $DIR/fn-traits.rs:30:14 + | +LL | fn call_mut(f: impl FnMut()) { + | ------- required by this bound in `call_mut` +... +LL | call_mut(foo_unsafe); + | ^^^^^^^^^^ expected an `FnMut<()>` closure, found `unsafe fn() {foo_unsafe}` + | + = help: the trait `std::ops::FnMut<()>` is not implemented for `unsafe fn() {foo_unsafe}` + = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ } + +error[E0277]: expected a `std::ops::FnOnce<()>` closure, found `unsafe fn() {foo_unsafe}` + --> $DIR/fn-traits.rs:32:15 + | +LL | fn call_once(f: impl FnOnce()) { + | -------- required by this bound in `call_once` +... +LL | call_once(foo_unsafe); + | ^^^^^^^^^^ expected an `FnOnce<()>` closure, found `unsafe fn() {foo_unsafe}` + | + = help: the trait `std::ops::FnOnce<()>` is not implemented for `unsafe fn() {foo_unsafe}` + = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ } + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0277`.