Add error note when trying fn as Fn trait

This commit is contained in:
Caleb Zulawski 2020-06-13 11:03:31 -04:00
parent 144206e6d8
commit c98b4c8fdd
3 changed files with 74 additions and 4 deletions

View file

@ -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)

View file

@ -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}`
}

View file

@ -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`.