diff --git a/text/0000-target-feature.md b/text/0000-target-feature.md new file mode 100644 index 00000000000..968f778dd84 --- /dev/null +++ b/text/0000-target-feature.md @@ -0,0 +1,588 @@ +- Feature Name: `target_feature` / `cfg_target_feature` / `cfg_feature_enabled` +- Start Date: 2017-06-26 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Motivation and Summary +[summary]: #summary + +While architectures like `x86_64` or `ARMv8` define the lowest-common denominator of instructions that all CPUs must support, many CPUs extend these with vector ([AVX](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions)), bitwise manipulation ([BMI](https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets)) and/or cryptographic ([AES](https://en.wikipedia.org/wiki/AES_instruction_set)) instruction sets. By default, the Rust compiler produces portable binaries that are able to run on all CPUs of a particular architecture. Users that know in which CPUs their binaries are going to run on are able to allow the compiler to use these extra instructions by using the compiler flags `--target-feature` and `--target-cpu`. Running these binaries on mismatching CPUs is undefined behavior. Currently, these users have no way in stable Rust to: + +- determine which features are available at compile-time, and +- determine which features are available at run-time, and +- embed code for different sets of features into the same binary, + +such that the programs can use different algorithms depending on the features available, and allowing portable ust binaries to efficiently run on many CPU families of a particular architecture. + +The objective of this RFC is to extend the Rust language to solve these three problems, and it does so by adding the following three language features: + +- **compile-time feature detection**: using configuration macros `cfg!(target_feature = "avx2")` to detect whether a feature is enabled or disabled in a context (`#![cfg(target_feature = "avx2")]`, ...), +- **run-time feature detection**: using the `cfg_feature_enabled!("avx2")` API to detect whether the current host supports the feature, and +- **unconditional code generation**: using the function attribute `#[target_feature(enable = "avx2")]` to allow the compiler to generate code under the assumption that this code will only be reached in hosts that support the feature. + +# Detailed design +[design]: #detailed-design + +## Target features + +Each rustc target has a default set of target features that can be controled via +the backend compilation options. The target features for each target should +be documented by the compiler and the backends (e.g. LLVM). + +This RFC does not add any target features to the language but it +specifies the process for adding target features. Each target feature must: + +- Be proposed in its own mini-RFC, RFC, or rustc-issue and follow a FCP period, +- Be behind its own feature gate macro of the form `target_feature_feature_name` + (where `feature_name` should be replaced by the name of the feature ). +- When possible, be detectable at run-time via the `cfg_feature_enabled!("name")` API. +- Include whether some backend-specific compilation options should enable the + feature. + +To use unstable target features on nightly, crates must opt into them as usual by +writing, for example, `#![allow(target_feature_avx2)]`. Since this is currently +not required, a grace period of one full release cycle will be given in which +this will raise a soft error before turning this requirement into a hard error. + +## Backend compilation options + +There are currently two ways of passing target feature information to rustc's code +generation backend on stable Rust. + +- `-C --target-feature=+/-backend_target_feature_name`: where `+/-` add/remove + features from the default feature set of the platform for the whole crate. + +- `-C --target-cpu=backend_cpu_name`, which changes the default feature set of + the crate to be that of all features enabled for `backend_cpu_name`. + +These two options are available on stable Rust and have been defacto stabilized. +Their semantics are LLVM specific and depend on what LLVM actually does with the +features. + +This RFC proposes to keep these options "as is", and add one new compiler option, +`--enable-features="feature0,feature1,..."`, (the analogous `--disable-features` +is discussed in the "Future Extensions" section) that supports only stabilized +target features. + +This allows us to preserve backwards compatibility while choosing different feature +names and semantics than the ones provided by the LLVM backend. + +The effect of `--enable-features=feature-list` is to enable all features implicitly +for all functions of a crate. That is, anywhere within the crate the values of the macro +`cfg!(target_feature = "feature")` and `cfg_feature_enabled!("feature")` are `true`. + +Whether the backend compilation options `-C --target-feature/--target-cpu` also enable +some stabilized features or not should be resolved by the RFCs suggesting the stabilization +of particular target features. + +## Unconditional code generation: `#[target_feature]` + +(note: the function attribute `#[target_feature]` is similar to clang's and +gcc's +[`__attribute__ ((__target__ ("feature")))`](https://clang.llvm.org/docs/AttributeReference.html#target-gnu-target).) + +This RFC introduces a function attribute that only applies to unsafe functions: [`#[target_feature(enable = +"feature_list")]`](https://github.com/rust-lang/rust/pull/38079) (the analogous `#[target_feature(disable = "feature_list")]` is discussed in the "Future Extensions" section): + +- This attribute _extends_ the feature set of a function beyond its default feature set, which _allows_ the compiler to generate code under the assumption that the function's code will only be reached on hardware that supports its feature set. +- Calling a function on a target that does not support its features is _undefined behavior_ (see the "On the unsafety of `#[target_feature]`" section). +- The compiler will not inline functions in contexts that do not support all the functions features. +- In `#[target_feature(enable = "feature")]` functions the value of `cfg!(target_feature = "feature")` and `cfg_feature_enabled!("feature")` is always `true` (otherwise undefined behavior did already happen). + +Note 0: the current RFC does not introduce any ABI issues in stable Rust. ABI issues with some unstable language features are explored in the "Unresolved Questions" section. + +Note 1: a function has the features of the crate where the function is defined +/- `#[target_feature]` annotations. Iff the function +is inlined into a context that extends its feature set, then the compiler is allowed to generate code for the function using this extended feature set (sub-note: inlining is forbidden in the opposite case). + +**Example 0 (basics):** + +This example covers how to use `#[target_feature]` with run-time feature detection to dispatch to different +function implementations depending on the features supported by the CPU at run-time: + +```rust +// This function will be optimized for different targets +#[inline(always)] fn foo_impl() { ... } + +// This generates a stub for CPUs that support SSE4: +#[target_feature(enable = "sse4")] unsafe fn foo_sse4() { + // Inlining `foo_impl` here is fine because `foo_sse4` + // extends `foo_impl` feature set + foo_impl() +} + +// This generates a stub for CPUs that support AVX: +#[target_feature(enable = "avx")] unsafe fn foo_avx() { foo_impl() } + +// This function returns the best implementation of `foo` depending +// on which target features the host CPU does support at run-time: +fn initialize_global_foo_ptr() -> fn () -> () { + if cfg_feature_enabled!("avx") { + unsafe { foo_avx } + } else if cfg_feature_enabled!("sse4") { + unsafe { foo_sse4 } + } else { + foo_impl // use the default version + } +} + +// During binary initialization we can set a global function pointer +// to the best implementation of foo depending on the features that +// the CPU where the binary is running does support: +lazy_static! { + static ref GLOBAL_FOO_PTR: fn() -> () = { + initialize_foo() + }; +} +// ^^ note: the ABI of this function pointer is independent of the target features + + +fn main() { + // Finally, we can use the function pointer to dispatch to the best implementation: + global_foo_ptr(); +} +``` + +**Example 1 (inlining):** + +```rust +#[target_feature(enable = "avx")] unsafe fn foo(); +#[target_feature(enable = "avx")] #[inline] unsafe fn baz(); // OK +#[target_feature(enable = "avx")] #[inline(always)] unsafe fn bar(); // OK + +#[target_feature(enable = "sse3")] +unsafe fn moo() { + // This function supports SSE3 but not AVX + if cfg_feature_enabled!("avx") { + foo(); // OK: foo is not inlined into moo + baz(); // OK: baz is not inlined into moo + bar(); + // ^ ERROR: bar cannot be inlined across mismatching features + // did you meant to make bar #[inline] instead of #[inline(always)]? + // Note: the logic to detect this is the same as for the call + // to baz, but in this case rustc must emit an error because an + // #[inline(always)] function cannot be inlined in this call site. + } +} +``` + +## Conditional compilation: `cfg!(target_feature)` + +The +[`cfg!(target_feature = "feature_name")`](https://github.com/rust-lang/rust/issues/29717) macro +allows querying at compile-time whether a target feature is enabled in the +current context. It returns `true` if the feature is enabled, and `false` +otherwise. + +In a function annotated with `#[target_feature(enable = "feature_name")]` the macro +`cfg!(target_feature = "feature_name")` expands to `true` if the generated +code for the function uses the feature ([current bug](https://github.com/rust-lang/rust/issues/42515). + +Note: how accurate `cfg!(target_feature)` can be made is an "Unresolved Question" (see the section below). Ideally, when `cfg!(target_feature)` is used in a function that does not support the feature, it should still return true in the cases where the function gets inlined into a context that does support the feature. This can happen often if the function is generic, or an `#[inline]` function defined in a different crate. This can results in errors at monomorphization time only if `#![cfg(target_feature)]` is used, but not if `if cfg!(target_feature)` is used since in this case all branches need to type-check properly. + +**Example 3 (conditional compilation):** + +```rust +fn bzhi_u32(x: u32, bit_position: u32) -> u32 { + // Conditional compilation: both branches must be syntactically valid, + // but it suffices that the true branch type-checks: + #[cfg(target_feature = "bmi2")] { + // if this code is being compiled with BMI2 support, use a BMI2 instruction: + unsafe { intrinsic::bmi2::bzhi(x, bit_position) } + } + #[cfg(not(target_feature = "bmi2"))] { + // otherwise, call a portable emulation of the BMI2 instruction + portable_emulation::bzhi(x, bit_position) + } +} + +fn bzhi_u64(x: u64, bit_position: u64) -> u64 { + // Here both branches must type-check and whether the false branch is removed + // or not is left up to the optimizer. + if cfg!(target_feature = "bmi2") { // `cfg!` expands to `true` or `false` at compile-time + // if target has the BMI2 instruction set, use a BMI2 instruction: + unsafe { intrinsic::bmi2::bzhi(x, bit_position) } + // ^^^ NOTE: this function cannot be inlined unless `bzhi_u64` supports + // the required features + } else { + // otherwise call an algorithm that emulates the instruction: + portable_emulation::bzhi(x, bit_position) + } +} +``` + +**Example 4 (value of `cfg!` within `#[target_feature]`):** + +```rust +#[target_feature("+avx")] +unsafe fn foo() { + if cfg!(target_feature = "avx") { /* this branch is always taken */ } + else { /* this branch is never taken */ } + #[cfg(not(target_feature = "avx"))] { + // this is dead code + } +} +``` + +## Run-time feature detection + +Writing safe wrappers around `unsafe` functions annotated with +`#[target_feature]` requires run-time feature detection. This RFC adds the following +macro to the standard library: + +- `cfg_feature_enabled!("feature") -> bool-expr` + +with the following semantics: "if the host hardware on which the current code is running +supports the `"feature"`, the `bool-expr` that `cfg_feature_enabled!` expands to has +value `true`, and `false` otherwise. + +If the result is known at compile-time, the macro approach allows expanding the result +without performing any run-time detection at all. This RFC does not guarantee that this +is the case, but [the current implementation](https://github.com/rust-lang-nursery/stdsimd) +does this. + +Examples of using run-time feature detection have been shown throughout this RFC, there +isn't really more to it. + +If the API of run-time feature detection turns out to be controversial before +stabilization, a follow-up RFC that focus on run-time feature detection will need +to be merged, blocking the stabilization of this RFC. + +# How We Teach This +[how-we-teach-this]: #how-we-teach-this + +There are two parts to this story, the low-level part, and the high-level part. + +**Example 5 (high-level usage of target features):** + +**note**: `ifunc` is not part of this RFC, but just an example of what can be built on top of it. + +In the high-level part we have the `ifunc` function attribute, implemented as a procedural macro (some of these macros [already](https://github.com/alexcrichton/cfg-specialize/blob/master/cfg-specialize-macros) [exist](https://github.com/parched/runtime-target-feature-rs)): + +```rust +#[ifunc("default", "sse4", "avx", "avx2")] //< MAGIC +fn foo() {} + +fn main() { + foo(); // dispatches to the best implementation at run-time + #[cfg(target_feature = "sse4")] { + foo(); // dispatches to the sse4 implementation at compile-time + } +} +``` + +The following example covers what `ifunc` might expand to. + +**Example 6 (ifunc expansion):** + +```rust +// Copy-pastes "foo" and generates code for multiple target features: +unsafe fn foo_default() { ...foo tokens... } +#[target_feature(enable = "sse4")] unsafe fn foo_sse4() { ...foo tokens... } +#[target_feature(enable = "avx")] unsafe fn foo_avx() { ...foo tokens... } +#[target_feature(enable = "avx2")] unsafe fn foo_avx2() { ...foo tokens... } + +// Initializes `foo` on binary initialization +static foo_ptr: fn() -> () = initialize_foo(); + +fn initialize_foo() -> typeof(foo) { + // run-time feature detection: + if cfg_feature_enabled!("avx2") { return unsafe { foo_avx2 } } + if cfg_feature_enabled!("avx") { return unsafe { foo_avx } } + if cfg_feature_enabled!("sse4") { return unsafe { foo_sse4 } } + foo_default +} + +// Wrap foo to do compile-time dispatch +#[inline(always)] fn foo() { + #[cfg(target_feature = "avx2")] + { unsafe { foo_avx2() } } + #[cfg(and(target_feature = "avx"), not(target_feature = "avx2")))] + { unsafe { foo_avx() } } + #[cfg(and(not(target_feature = "sse4")), not(target_feature = "avx")))] + { unsafe { foo_sse4() } } + #[cfg(not(target_feature = "sse4"))] + { foo_ptr() } +} +``` + +Note that there are many solutions to this problem and they have different +trade-offs, but these can be explored in procedural macros. When wrapping unsafe +intrinsics, conditional compilation can be used to create zero-cost wrappers: + +**Example 7 (three-layered approach to target features):** + +```rust +// Raw unsafe intrinsic: in LLVM, std::intrinsic, etc. +// Calling this on an unsupported target is undefined behavior. +extern "C" { fn raw_intrinsic_function(f64, f64) -> f64; } + +// Software emulation of the intrinsic, +// works on all architectures. +fn software_emulation_of_raw_intrinsic_function(f64, f64) -> f64; + +// Safe zero-cost wrapper over the intrinsic +// (i.e. can be inlined) +fn my_intrinsic(a: f64, b: f64) -> f64 { + #[cfg(target_feature = "some_feature")] { + // If "some_feature" is enabled, it is safe to call the + // raw intrinsic function + unsafe { raw_intrinsic_function(a, b) } + } + #[cfg(not(target_feature = "some_feature"))] { + // if "some_feature" is disabled calling + // the raw intrinsic function is undefined behavior (per LLVM), + // we call the safe software emulation of the intrinsic: + software_emulation_of_raw_intrinsic_function(a, b) + } +} + +#[ifunc("default", "avx")] +fn my_intrinsic_rt(a: f64, b: f64) -> f64 { my_intrinsic(a, b) } +``` +Due to the low-level and high-level nature of these feature we will need two +kinds of documentation. For the low level part: + +- document how to do compile-time and run-time feature detection using `cfg!(target_feature)` and `cfg_feature_enabled!`, +- document how to use `#[target_feature]`, +- document how to use all of these together to solve problems like in the examples of this RFC. + +For the high-level part we should aim to bring third-party crates implementing +`ifunc!` or similar close to 1.0 releases before stabilization. + +# Drawbacks +[drawbacks]: #drawbacks + +- Obvious increase in language complexity. + +The main drawback of not solving this issue is that many libraries that require +conditional feature-dependent compilation or run-time selection of code for +different features (SIMD, BMI, AES, ...) cannot be written efficiently in stable +Rust. + + +# Alternatives +[alternatives]: #alternatives + +# Backend options + +An alternative would be to mix stable, unstable, unknown, +and backend-specific features into `--target-feature`. + +## Make `#[target_feature]` safe + +Calling a function annotated with `#[target_feature]` on a host that does not +support the feature invokes undefined behavior in LLVM, the assembler, and +possibly the hardware [See this comment](https://github.com/rust-lang/rfcs/pull/2045#issuecomment-311325202). + +That is, calling a function on a target that does not support its feature set is +_undefined behavior_ and this RFC cannot specify otherwise. The main reason is that `target_feature` is a promise from the user to the toolchain and the hardware, that the code will not be reached in a CPU that does not support the feature. LLVM, the assembler, and the hardware all assume that the user will not violate this contract, and there is little that the Rust compiler can do to make this safer: + - The Rust compiler cannot emit a compile-time diagnostic because it cannot know whether the user is going to run the binary in a CPU that supports the features or not. + - A run-time diagnostic _always_ incurs a run-time cost, and is only possible iff the absence of a feature can be detected at run-time (the "Future Extensions" section of this RFC discusses how to implement "Run-time diagnostics" to detect this, when possible). + +However, the `--target-feature/--target-cpu` compiler options allows one to implicitly generate binaries that reliably run into undefined behavior without needing any `unsafe` annotations at all, so the answer to the question "Should `#[target_feature]` be safe/unsafe?" is indeed a hard one. + +The main differences between `#[target_feature]` and `--target-feature`/`--enable-feature` are the following: +- `--target-feature/--enable-feature` are "backend options" while `#[target_feature]` is part of the language +- `--target-feature/--enable-feature` is specified by whoever compiles the code, while `#[target_feature]` is specified by whoever writes the code +- compiling safe Rust code for a particular target, and then running the binary on that target, can only produce undefined behavior iff `#[target_feature]` is safe. + +This RFC chooses that the `#[target_feature]` attribute only applies to `unsafe fn`s, so that if one compiles safe Rust source code for a particular target, and then runs the binary on that particular target, no unsafety can result. + +Note that we can always make `#[target_feature]` safe in the future without breaking backwards compatibility, but the opposite is not true. That is, if somebody figures out a way of making `#[target_feature]` safe such that the above holds, we can always make that change. + +## Guarantee no segfaults from `unsafe` code + +Calling a `#[target_feature]`-annotated function on a platform that does not +support it invokes undefined behavior. We could guarantee that this does not +happen by always doing run-time feature detection, introducing a run-time cost +in the process, and by only accepting features for which run-time feature +detection can be done. + +This RFC considers that any run-time cost is unacceptable as a default +for a combination of language features whose main domain of use is a performance +sensitive one. + +The "Future Extension"s section discusses how to implement this in an opt-in way, +e.g., as a sort of binary instrumentation. + +## Make `#[target_feature] + #[inline(always)]` incompatible + +This RFC requires the compiler to error when a function marked with both `#[target_feature]` and the `#[inline(always)]` attribute cannot be inlined in a particular call site due to incompatible features. So we might consider to simplify this RFC by just making these attributes incompatible. + +While this is technically correct, the compiler must detect when any function (`#[inline(always)]`, `#[inline]`, generics, ...) is inlined into an incompatible context, and prevent this from happening. Erroring if the function is `#[inline(always)]` does not significantly simplify the RFC nor the compiler implementation. + +## Removing run-time feature detection from this RFC + +This RFC adds an API for run-time feature detection to the +standard library. + +The alternative would be to implement similar functionality as a third-party crate that +might eventually be moved into the nursery. [Such crates already exist](https://docs.rs/cupid/) + +In particular, the API proposed in this RFC is "stringly-typed" (to make it uniform with the other features being proposed), but arguably a third party crate might want to use an `enum` to allow pattern-matching on features. These APIs have not been sufficiently explored in the ecosystem yet. + +The main arguments in favor of including run-time feature detection in this RFC are: + +- it is impossible to write safe wrappers around `#[target_feature]` without it +- implementing it requires the `asm!` macro or linking to a C library (or linking + to a C wrapper around assembly), +- run-time detection should be kept in sync with the addition of new target features, +- the compiler might want to use LLVM's run-time feature detection which is part + of compiler-rt. + +The consensus in the internal forums and previous discussions seem to be that this +is worth it. + +It might turn out that the people from the future are able to come up with a better +API. But in that case we can always deprecate the current API and include the new +one in the standard library. + +## Adding full cpuid support to the standard library + +The `cfg_feature_enable!` macro is designed to work specifically with the features +that can be used via `cfg_target_feature` and `#[target_feature]`. However, in the +grand scheme of things, run-time detection of these features is only a small part +of the information provided by `cpuid`-like CPU instructions. + +Currently at least two great implementations of cpuid-like functionality exists in +Rust for x86: [cupid](https://github.com/shepmaster/cupid) and +[rust-cpuid](https://github.com/gz/rust-cpuid). Adding the macro to the standard library +does not prevent us from adding more comprehensive functionality in the future, and +it does not prevent us from reusing any of these libraries in the internal +implementation of the macro. + +# Unresolved questions +[unresolved]: #unresolved-questions + +## How accurate should cfg!(feature) be? + +What happens if the macro `cfg!(target_feature = "feature_name")` is used inside a function for which `feature_name` is not enabled, but that function gets inlined into a context in which the feature is enabled? We want the macro to accurately return `true` in this case, that is, to be as accurate as possible so that users always get the most efficient algorithms, but whether this is even possible is an unresolved question. + +This might result in monomorphization errors if `#![cfg(target_feature)]` is used, but not if `if cfg!(target_feature)` is used since in this case all branches need to type-check properly. + +We might want to ammend this RFC with more concrete semantics about this as we improve the compiler. + +## How do we handle ABI issues with portable vector types? + +The ABI of `#[target_feature]` functions does not change for all types currently available in stable Rust. However, there are types that we might want to add to the language at some point, like portable vector types, for which this is not the case. + +The behavior of `#[target_feature]` for those types should be specified in the RFC that proposes to stabilize those types, and this RFC should be ammended as necessary. + +The following examples showcase some potential problems when calling functions with mismatching ABIs, or when using function pointers. + +Whether we can warn, or hard error at compile-time in these cases remains to be explored. + +**Example 8 (ABI):** + +```rust +#[target_feature(enable = "sse2")] +unsafe fn foo_sse2(a: f32x8) -> f32x8 { a } // ABI: 2x 128bit registers + +#[target_feature(enable = "avx2")] +unsafe fn foo_avx2(a: f32x8) -> f32x8 { // ABI: 1x 256bit register + foo_sse2(a) // ABI mismatch: + //^ should this perform an implicit conversion, produce a hard error, or just undefined behavior? +} + +#[target_feature(enable = "sse2")] +unsafe fn bar() { + type fn_ptr = fn(f32x8) -> f32x8; + let mut p0: fn_ptr = foo_sse2; // OK + let p1: fn_ptr = foo_avx2; // ERROR: mismatching ABI + let p2 = foo_avx2; // OK + p0 = p2; // ERROR: mismatching ABI +} +``` + +# Future Extensions + +## Mutually exclusive features + +In some cases, e.g., when enabling AVX but disabling SSE4 the compiler should probably produce an error, but for other features like `thumb_mode` the behavior is less clear. These issues should be addressed by the RFC proposing the stabilizaiton of the target features that need them, as future extensions to this RFC. + +## Safely inlining `#[target_feature]` functions on more contexts + +The problem is the following: + +```rust +#[target_feature(enable = "sse3")] +unsafe fn baz() { + if some_opaque_code() { + unsafe { foo_avx2(); } + } +} +``` + +If `foo_avx2` gets inlined into `baz`, optimizations that reorder its instructions +across the if condition might introduce undefined behavior. + +Maybe, one could make `cfg_feature_enabled!` a bit magical, so that when it is +used in the typical ways the compiler can infer whether inlining is safe, e.g., + +```rust +#[target_feature(enable = "sse3")] +unsafe fn baz() { + // -- sse3 boundary start (applies to fn arguments as well) + // -- sse3 boundary ends + if cfg_feature_enabled!("avx") { + // -- avx boundary starts + unsafe { foo_avx(); } + // can be inlined here, but its code cannot be + // reordered out of the avx boundary + // -- avx boundary ends + } + // -- sse3 boundary starts + // -- sse3 boundary ends (applies to drop as well) +} +``` + +Whether this is worth it or can be done at all is an unresolved question. This RFC does not propose any of this, but leaves the door open for such an extension to be explored and proposed independently in a follow-up RFC. + +## Run-time diagnostics + +Calling a `#[target_feature]`-annotated function on a platform that does not +support it invokes undefined behavior. A friendly compiler could use run-time +feature detection to check whether calling the function is safe and emit a nice +`panic!` message. + +This can be done, for example, by desugaring this: + +```rust +#[target_feature(enable = "avx")] unsafe fn foo(); +``` + +into this: + +```rust +#[target_feature(enable = "avx")] unsafe fn foo_impl() { ...foo tokens... }; + +// this function will be called if avx is not available: +fn foo_fallback() { + panic!("calling foo() requires a target with avx support") +} + +// run-time feature detection on initialization +static foo_ptr: fn() -> () = if cfg_feature_enabled!("avx") { + unsafe { foo_impl } +} else { + foo_fallback +}; + +// dispatches foo via function pointer to produce nice diagnostic +unsafe fn foo() { foo_ptr() } +``` + +This is not required for safety and can be implemented into the compiler as an opt-in instrumentation pass without +going through the RFC process. However, a proposal to enable this by default should go through the RFC process. + +## Disabling features + +This RFC does not allow disabling target features, but suggest an analogous syntax to do so (`#[target_feature(disable = "feature-list")]`, `--disable-feature=feature-list`). Disabling features can result in some [non-sensical situations](https://internals.rust-lang.org/t/pre-rfc-stabilization-of-target-feature/5176/26) and should be pursued as a future extension of this RFC once we want to stabilize a target feature for which it makes sense. + +# Acknowledgements +[acknowledgments]: #acknowledgements + +@parched @burntsushi @alexcrichton @est31 @pedrocr @chandlerc @RalfJung @matthieu-m + +- `#[target_feature]` Pull-Request: https://github.com/rust-lang/rust/pull/38079 +- `cfg_target_feature` tracking issue: https://github.com/rust-lang/rust/issues/29717