Skip to content

Commit

Permalink
Rollup merge of rust-lang#73306 - calebzulawski:target-feature-11-fn-…
Browse files Browse the repository at this point in the history
…trait-soundness, r=nikomatsakis

Don't implement Fn* traits for #[target_feature] functions

Closes rust-lang#72012.
  • Loading branch information
Manishearth committed Jul 2, 2020
2 parents c6d35e7 + 51858da commit d7e8c0c
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 1 deletion.
18 changes: 18 additions & 0 deletions src/librustc_trait_selection/traits/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,24 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
);
}

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_target_feature_fn =
if let ty::FnDef(def_id, _) = trait_ref.skip_binder().self_ty().kind {
!self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
} else {
false
};
if is_fn_trait && is_target_feature_fn {
err.note(
"`#[target_feature]` functions do not implement the `Fn` traits",
);
}

// Try to report a help message
if !trait_ref.has_infer_types_or_consts()
&& self.predicate_can_apply(obligation.param_env, trait_ref)
Expand Down
16 changes: 15 additions & 1 deletion src/librustc_trait_selection/traits/select/candidate_assembly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidates.ambiguous = true; // Could wind up being a fn() type.
}
// Provide an impl, but only for suitable `fn` pointers.
ty::FnDef(..) | ty::FnPtr(_) => {
ty::FnPtr(_) => {
if let ty::FnSig {
unsafety: hir::Unsafety::Normal,
abi: Abi::Rust,
Expand All @@ -317,6 +317,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidates.vec.push(FnPointerCandidate);
}
}
// Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
ty::FnDef(def_id, _) => {
if let ty::FnSig {
unsafety: hir::Unsafety::Normal,
abi: Abi::Rust,
c_variadic: false,
..
} = self_ty.fn_sig(self.tcx()).skip_binder()
{
if self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() {
candidates.vec.push(FnPointerCandidate);
}
}
}
_ => {}
}

Expand Down
34 changes: 34 additions & 0 deletions src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// only-x86_64

#![feature(target_feature_11)]

#[target_feature(enable = "avx")]
fn foo() {}

#[target_feature(enable = "avx")]
unsafe fn foo_unsafe() {}

fn call(f: impl Fn()) {
f()
}

fn call_mut(f: impl FnMut()) {
f()
}

fn call_once(f: impl FnOnce()) {
f()
}

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}`
}
81 changes: 81 additions & 0 deletions src/test/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
error[E0277]: expected a `std::ops::Fn<()>` closure, found `fn() {foo}`
--> $DIR/fn-traits.rs:24:10
|
LL | fn call(f: impl Fn()) {
| ---- required by this bound in `call`
...
LL | call(foo);
| ^^^ expected an `Fn<()>` closure, found `fn() {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: `#[target_feature]` functions do not implement the `Fn` traits

error[E0277]: expected a `std::ops::FnMut<()>` closure, found `fn() {foo}`
--> $DIR/fn-traits.rs:25:14
|
LL | fn call_mut(f: impl FnMut()) {
| ------- required by this bound in `call_mut`
...
LL | call_mut(foo);
| ^^^ expected an `FnMut<()>` closure, found `fn() {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: `#[target_feature]` functions do not implement the `Fn` traits

error[E0277]: expected a `std::ops::FnOnce<()>` closure, found `fn() {foo}`
--> $DIR/fn-traits.rs:26:15
|
LL | fn call_once(f: impl FnOnce()) {
| -------- required by this bound in `call_once`
...
LL | call_once(foo);
| ^^^ expected an `FnOnce<()>` closure, found `fn() {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: `#[target_feature]` functions do not implement the `Fn` traits

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 */ }
= note: `#[target_feature]` functions do not implement the `Fn` traits

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 */ }
= note: `#[target_feature]` functions do not implement the `Fn` traits

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 */ }
= note: `#[target_feature]` functions do not implement the `Fn` traits

error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0277`.

0 comments on commit d7e8c0c

Please sign in to comment.