Change:
(Moved from with an PoC.) The following program () demonstrates how current implementation of allows using a targetfeature from safe code without ensuring it's actually available: This is unsound because it allows executing (e.g.) AVX instructions on CPUs that do not implement them, which is UB. It only works because "safe fns with targetfeatures" are erroneously considered to implement the FnOnce/FnMut/Fn traits.
Proposed resolution: It would probably be better to add the to functions implicitly during lowering to HIR. Implicit is already to functions in blocks in the same way. That would make the unsafety checker the only place (besides AST lowering) where they would be treated specially. Like, "yes, the function is unsafe, but we know that it's safe to call in this specific context, so the unsafe block can be omitted". The special coercion checks would no longer be necessary in that case.
Or perhaps the RFC should be revised to still require writing and only unsafety checker need to be enhanced to allow calling unsafe functions without unsafe blocks in the situations known to be safe. That would be even less magic, which is good, IMO. The ergonomic regression would be minor because would still be not required at call sites.
Good catch. Another option is that referencing, not calling, a target-feature function is unsafe, but I guess that is a bit inconsistent given that this already compiles (): In other words, accessing a safe target feature 1.1 function would be unsafe (unless the target feature is in scope), not just calling it. I think that this is what I would expect somehow, but I would expect it to apply uniformly to both safe and unsafe target feature functions (and that's not an option). The downside of this approach is that we don't know why the function was declared unsafe, not really -- was it only because of the target feature? Or were there additional requirements that would have made it unsafe?
For example, a whole bunch of functions take raw pointers and use them to access memory, so they need to be unsafe even if the respective target feature is enabled in the calling context.
The downside of this approach is that we don't know why the function was declared unsafe, not really -- was it only because of the target feature? Or were there additional requirements that would have made it unsafe? Ah, right, that's a good argument. The "unsafety checker enhancement" should only affect unsafe functions that are made unsafe through , but not other means (explicit or block).
I'm new to the compiler, but it seemed non-trivial to change functions to unsafe when lowering to HIR. It looks to me like you'd end up with just as many exceptions (ignoring the unsafe in some scenarios), in addition tracking the origin of the . On the other hand it was pretty trivial to not implement the Fn traits instead. I'll open a PR with my fix, but please close it if the other method is preferred.
So there are sort of two questions here. One of them is what user-visible behavior is permitted, and the other is how precisely that is implemented in the compiler. I think the latter only concerns the team in-so-far as it represents the impact on the language spec and on the "mental model" that users must keep in mind. Nominating for the lang-team meeting to discuss solution, which is simply to make "target-feature" functions not implement the traits (just as functions do not implement the traits, along with functions with non-Rust ABIs etc).
I think I was tagged by mistake :wink:
Apologies! Fixed. Github autocompleted that for me. Here I was feeling so grateful to it.
We discussed this in . The conclusion was that the fix that proposed is good for right now. It's already the case that many items do not implement the function trait (e.g., those with custom ABIs or which are unsafe), so this would be "yet another" case (admittedly a somewhat different one in that this case doesn't show up in the type of function pointers, for better or worse). We uncovered a few other interesting observations: pointed out that their ideal preference would be to have something like " only implements " within an unsafe block, but of course the trait system has no precedent for that. It would be sort of like a attached to the impl block (i.e., the impl is usable in some context where unsafe code is allowed). Another possibility might be " only implements within a fn that has a suitable target feature in scope". This is an interesting idea because, although the pointer could escape in the form of a , the idea is that once you enter , you've already demonstrated that you possess the target feature, so it's ok if the value escapes. (This would be like a clause.) To that end, I was thinking that you could model this last point using closures, but I found that it doesn't work. In particular, gives an error, even if it appears inside of a function annotated with . This seems like something we should discuss, but it's a separate issue. We did discuss briefly whether target features should be part of the type of function pointers but I think the conclusion was 'well maybe but there is already stable code out there that lets you coerce, and it seems rather niche'. We also discussed whether we should permit one to annotate closures with target-feature annotations. In any case, those are things we can address separately from this issue, but the point is that adopting more accepting solutions can be done in the future.
I'm going to move the question of whether closures should "inherit" target features to the main tracking issue -- I'm not sure if that is something that was discussed as part of the original RFC discussion?
Opened to discuss closures.

Discussion:
); } <ins> 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", ); } </ins> // Try to report a help message if !trait_ref.has_infer_types_or_consts() && self.predicate_can_apply(obligation.param_env, trait_ref)

candidates.ambiguous = true; // Could wind up being a fn() type. } // Provide an impl, but only for suitable `fn` pointers. <del> ty::FnDef(..) | ty::FnPtr(_) => { </del> <ins> ty::FnPtr(_) => { </ins> if let ty::FnSig { unsafety: hir::Unsafety::Normal, abi: Abi::Rust,

candidates.vec.push(FnPointerCandidate); } } <ins> // 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); } } } </ins> _ => {} }

<ins> // 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}` } </ins>

<ins> 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`. </ins>