The Unstable Book
Welcome to the Unstable Book! This book consists of a number of chapters, each one organized by a "feature flag." That is, when using an unstable feature of Rust, you must use a flag, like this:
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; fn main() { let mut coroutine = #[coroutine] || { yield 1; return "foo" }; match Pin::new(&mut coroutine).resume(()) { CoroutineState::Yielded(1) => {} _ => panic!("unexpected value from resume"), } match Pin::new(&mut coroutine).resume(()) { CoroutineState::Complete("foo") => {} _ => panic!("unexpected value from resume"), } }
The coroutines
feature has a chapter describing how to use it.
Because this documentation relates to unstable features, we make no guarantees that what is contained here is accurate or up to date. It's developed on a best-effort basis. Each page will have a link to its tracking issue with the latest developments; you might want to check those as well.
Compiler flags
branch-protection
This option lets you enable branch authentication instructions on AArch64.
This option is only accepted when targeting AArch64 architectures.
It takes some combination of the following values, separated by a ,
.
pac-ret
- Enable pointer authentication for non-leaf functions.leaf
- Enable pointer authentication for all functions, including leaf functions.b-key
- Sign return addresses with key B, instead of the default key A.bti
- Enable branch target identification.
leaf
and b-key
are only valid if pac-ret
was previously specified.
For example, -Z branch-protection=bti,pac-ret,leaf
is valid, but
-Z branch-protection=bti,leaf,pac-ret
is not.
Rust's standard library does not ship with BTI or pointer authentication enabled by default. In Cargo projects the standard library can be recompiled with pointer authentication using the nightly build-std feature.
cf-protection
The tracking issue for this feature is: #93754.
This option enables control-flow enforcement technology (CET) on x86; a more detailed description of
CET is available here. Similar to clang
, this flag takes one of the following values:
none
- Disable CET completely (this is the default).branch
- Enable indirect branch tracking (IBT
).return
- Enable shadow stack (SHSTK
).full
- Enable bothbranch
andreturn
.
This flag only applies to the LLVM backend: it sets the cf-protection-branch
and
cf-protection-return
flags on LLVM modules. Note, however, that all compiled modules linked
together must have the flags set for the compiled output to be CET-enabled. Currently, Rust's
standard library does not ship with CET enabled by default, so you may need to rebuild all standard
modules with a cargo
command like:
$ RUSTFLAGS="-Z cf-protection=full" cargo +nightly build -Z build-std --target x86_64-unknown-linux-gnu
Detection
An ELF binary is CET-enabled if it has the IBT
and SHSTK
tags, e.g.:
$ readelf -a target/x86_64-unknown-linux-gnu/debug/example | grep feature:
Properties: x86 feature: IBT, SHSTK
Troubleshooting
To display modules that are not CET enabled, examine the linker errors available when cet-report
is enabled:
$ RUSTC_LOG=rustc_codegen_ssa::back::link=info rustc-custom -v -Z cf-protection=full -C link-arg="-Wl,-z,cet-report=warning" -o example example.rs
...
/usr/bin/ld: /.../build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-d73f7266be14cb8b.rlib(std-d73f7266be14cb8b.std.f7443020-cgu.12.rcgu.o): warning: missing IBT and SHSTK properties
codegen-backend
The tracking issue for this feature is: #77933.
This feature allows you to specify a path to a dynamic library to use as rustc's code generation backend at runtime.
Set the -Zcodegen-backend=<path>
compiler flag to specify the location of the
backend. The library must be of crate type dylib
and must contain a function
named __rustc_codegen_backend
with a signature of fn() -> Box<dyn rustc_codegen_ssa::traits::CodegenBackend>
.
Example
See also the codegen-backend/hotplug
test for a working example.
use rustc_codegen_ssa::traits::CodegenBackend;
struct MyBackend;
impl CodegenBackend for MyBackend {
// Implement codegen methods
}
#[no_mangle]
pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
Box::new(MyBackend)
}
Unstable codegen options
All of these options are passed to rustc
via the -C
flag, short for "codegen". The flags are
stable but some of their values are individually unstable, and also require using -Z unstable-options
to be accepted.
linker-flavor
In addition to the stable set of linker flavors, the following unstable values also exist:
ptx
: userust-ptx-linker
for Nvidia NVPTX GPGPU support.bpf
: usebpf-linker
for eBPF support.llbc
: for linking in llvm bitcode. Install the preview rustup componentsllvm-bitcode-linker
andllvm-tools
to use as a self-contained linker by passing-Zunstable-options -Clink-self-contained=+linker
together with-Clinker-flavor=llbc
. Can currently only be used for Nvidia NVPTX targets (nvptx64-nvidia-cuda
).
Additionally, a set of more precise linker flavors also exists, for example allowing targets to declare that they use the LLD linker by default. The following values are currently unstable, and the goal is for them to become stable, and preferred in practice over the existing stable values:
gnu
: unix-like linker with GNU extensionsgnu-lld
:gnu
using LLDgnu-cc
:gnu
using a C/C++ compiler as the linker drivergnu-lld-cc
:gnu
using LLD and a C/C++ compiler as the linker driverdarwin
: unix-like linker for Apple targetsdarwin-lld
:darwin
using LLDdarwin-cc
:darwin
using a C/C++ compiler as the linker driverdarwin-lld-cc
:darwin
using LLD and a C/C++ compiler as the linker driverwasm-lld
: unix-like linker for Wasm targets, with LLDwasm-lld-cc
: unix-like linker for Wasm targets, with LLD and a C/C++ compiler as the linker driverunix
: basic unix-like linker for "any other Unix" targets (Solaris/illumos, L4Re, MSP430, etc), not supported with LLD.unix-cc
:unix
using a C/C++ compiler as the linker drivermsvc-lld
: MSVC-style linker for Windows and UEFI, with LLDem-cc
: emscripten compiler frontend, similar towasm-lld-cc
with a different interface
link-self-contained
This flag generally controls whether the linker will use libraries and objects shipped with Rust
instead of those in the system. The stable boolean values for this flag are coarse-grained
(everything or nothing), but there exists a set of unstable values with finer-grained control,
-Clink-self-contained
can accept a comma-separated list of components, individually enabled
(+component
) or disabled (-component
):
crto
: CRT objects (e.g. onwindows-gnu
,musl
,wasi
targets)libc
: libc static library (e.g. onmusl
,wasi
targets)unwind
: libgcc/libunwind (e.g. onwindows-gnu
,fuchsia
,fortanix
,gnullvm
targets)linker
: linker, dlltool, and their necessary libraries (e.g. onwindows-gnu
and forrust-lld
)sanitizers
: sanitizer runtime librariesmingw
: other MinGW libs and Windows import libs
Out of the above self-contained linking components, linker
is the only one currently implemented
(beyond parsing the CLI options).
It refers to the LLD linker, built from the same LLVM revision used by rustc (named rust-lld
to
avoid naming conflicts), that is distributed via rustup
with the compiler (and is used by default
for the wasm targets). One can also opt-in to use it by combining this flag with an appropriate
linker flavor: for example, -Clinker-flavor=gnu-lld-cc -Clink-self-contained=+linker
will use the
toolchain's rust-lld
as the linker.
control-flow-guard
The tracking issue for this feature is: #68793.
The rustc flag -Z control-flow-guard
enables the Windows Control Flow Guard (CFG) platform security feature.
CFG is an exploit mitigation designed to enforce control-flow integrity for software running on supported Windows platforms (Windows 8.1 onwards). Specifically, CFG uses runtime checks to validate the target address of every indirect call/jump before allowing the call to complete.
During compilation, the compiler identifies all indirect calls/jumps and adds CFG checks. It also emits metadata containing the relative addresses of all address-taken functions. At runtime, if the binary is run on a CFG-aware operating system, the loader uses the CFG metadata to generate a bitmap of the address space and marks those addresses that contain valid targets. On each indirect call, the inserted check determines whether the target address is marked in this bitmap. If the target is not valid, the process is terminated.
In terms of interoperability:
- Code compiled with CFG enabled can be linked with libraries and object files that are not compiled with CFG. In this case, a CFG-aware linker can identify address-taken functions in the non-CFG libraries.
- Libraries compiled with CFG can linked into non-CFG programs. In this case, the CFG runtime checks in the libraries are not used (i.e. the mitigation is completely disabled).
CFG functionality is completely implemented in the LLVM backend and is supported for X86 (32-bit and 64-bit), ARM, and Aarch64 targets. The rustc flag adds the relevant LLVM module flags to enable the feature. This flag will be ignored for all non-Windows targets.
When to use Control Flow Guard
The primary motivation for enabling CFG in Rust is to enhance security when linking against non-Rust code, especially C/C++ code. To achieve full CFG protection, all indirect calls (including any from Rust code) must have the appropriate CFG checks, as added by this flag. CFG can also improve security for Rust code that uses the unsafe
keyword.
Another motivation behind CFG is to harden programs against return-oriented programming (ROP) attacks. CFG disallows an attacker from taking advantage of the program's own instructions while redirecting control flow in unexpected ways.
Overhead of Control Flow Guard
The CFG checks and metadata can potentially increase binary size and runtime overhead. The magnitude of any increase depends on the number and frequency of indirect calls. For example, enabling CFG for the Rust standard library increases binary size by approximately 0.14%. Enabling CFG in the SPEC CPU 2017 Integer Speed benchmark suite (compiled with Clang/LLVM) incurs approximate runtime overheads of between 0% and 8%, with a geometric mean of 2.9%.
Testing Control Flow Guard
The rustc flag -Z control-flow-guard=nochecks
instructs LLVM to emit the list of valid call targets without inserting runtime checks. This flag should only be used for testing purposes as it does not provide security enforcement.
Control Flow Guard in libraries
It is strongly recommended to also enable CFG checks for all linked libraries, including the standard library.
To enable CFG in the standard library, use the cargo -Z build-std
functionality to recompile the standard library with the same configuration options as the main program.
For example:
rustup toolchain install --force nightly
rustup component add rust-src
SET RUSTFLAGS=-Z control-flow-guard
cargo +nightly build -Z build-std --target x86_64-pc-windows-msvc
rustup toolchain install --force nightly
rustup component add rust-src
$Env:RUSTFLAGS = "-Z control-flow-guard"
cargo +nightly build -Z build-std --target x86_64-pc-windows-msvc
Alternatively, if you are building the standard library from source, you can set control-flow-guard = true
in the config.toml file.
coverage-options
This option controls details of the coverage instrumentation performed by
-C instrument-coverage
.
Multiple options can be passed, separated by commas. Valid options are:
block
,branch
,condition
,mcdc
: Sets the level of coverage instrumentation. Setting the level will override any previously-specified level.block
(default): Blocks in the control-flow graph will be instrumented for coverage.branch
: In addition to block coverage, also enables branch coverage instrumentation.condition
: In addition to branch coverage, also instruments some boolean expressions as branches, even if they are not directly used as branch conditions.mcdc
: In addition to condition coverage, also enables MC/DC instrumentation. (Branch coverage instrumentation may differ in some cases.)
debug-info-for-profiling
Introduction
Automatic Feedback Directed Optimization (AFDO) is a method for using sampling based profiles to guide optimizations. This is contrasted with other methods of FDO or profile-guided optimization (PGO) which use instrumented profiling.
Unlike PGO (controlled by the rustc
flags -Cprofile-generate
and
-Cprofile-use
), a binary being profiled does not perform significantly worse,
and thus it's possible to profile binaries used in real workflows and not
necessary to construct artificial workflows.
Use
In order to use AFDO, the target platform must be Linux running on an x86_64
architecture with the performance profiler perf
available. In addition, the
external tool create_llvm_prof
from this repository must be used.
Given a Rust file main.rs
, we can produce an optimized binary as follows:
rustc -O -Zdebug-info-for-profiling main.rs -o main
perf record -b ./main
create_llvm_prof --binary=main --out=code.prof
rustc -O -Zprofile-sample-use=code.prof main.rs -o main2
The perf
command produces a profile perf.data
, which is then used by the
create_llvm_prof
command to create code.prof
. This final profile is then
used by rustc
to guide optimizations in producing the binary main2
.
default-hidden-visibility
The tracking issue for this feature is: https://github.com/rust-lang/compiler-team/issues/656
This flag can be used to override the target's
default_hidden_visibility
setting.
Using -Zdefault_hidden_visibility=yes
is roughly equivalent to Clang's
-fvisibility=hidden
cmdline flag.
direct_access_external_data
The tracking issue for this feature is: https://github.com/rust-lang/compiler-team/issues/707
Option -Z direct-access-external-data
controls how to access symbols of
external data.
Supported values for this option are:
yes
- Don't use GOT indirection to reference external data symbols.no
- Use GOT indirection to reference external data symbols.
If the option is not explicitly specified, different targets have different default values.
dump-mono-stats
The -Z dump-mono-stats
compiler flag generates a file with a list of the monomorphized items in the current crate.
It is useful for investigating compile times.
It accepts an optional directory where the file will be located. If no directory is specified, the file will be placed in the current directory.
See also -Z dump-mono-stats-format
and -Z print-mono-items
. Unlike print-mono-items
,
dump-mono-stats
aggregates monomorphized items by definition and includes a size estimate of how
large the item is when codegened.
See https://rustc-dev-guide.rust-lang.org/backend/monomorph.html for an overview of monomorphized items.
dump-mono-stats-format
The -Z dump-mono-stats-format
compiler flag controls what file format to use for -Z dump-mono-stats
.
The default is markdown; currently JSON is also supported. JSON can be useful for programmatically manipulating the results (e.g. to find the item that took the longest to compile).
dwarf-version
This option controls the version of DWARF that the compiler emits, on platforms that use DWARF to encode debug information. It takes one of the following values:
2
: DWARF version 2 (the default on certain platforms, like macOS).4
: DWARF version 4 (the default on certain platforms, like Linux).5
: DWARF version 5.
dylib-lto
This option enables using LTO for the dylib
crate type. This is currently only used for compiling
rustc
itself (more specifically, the librustc_driver
dylib).
embed-source
This flag controls whether the compiler embeds the program source code text into the object debug information section. It takes one of the following values:
y
,yes
,on
ortrue
: put source code in debug info.n
,no
,off
,false
or no value: omit source code from debug info (the default).
This flag is ignored in configurations that don't emit DWARF debug information
and is ignored on non-LLVM backends. -Z embed-source
requires DWARFv5. Use
-Z dwarf-version=5
to control the compiler's DWARF target version and -g
to
enable debug info generation.
emit-stack-sizes
The tracking issue for this feature is: #54192
The rustc flag -Z emit-stack-sizes
makes LLVM emit stack size metadata.
NOTE: This LLVM feature only supports the ELF object format as of LLVM 8.0. Using this flag with targets that use other object formats (e.g. macOS and Windows) will result in it being ignored.
Consider this crate:
#![crate_type = "lib"]
use std::ptr;
pub fn foo() {
// this function doesn't use the stack
}
pub fn bar() {
let xs = [0u32; 2];
// force LLVM to allocate `xs` on the stack
unsafe { ptr::read_volatile(&xs.as_ptr()); }
}
Using the -Z emit-stack-sizes
flag produces extra linker sections in the
output object file.
$ rustc -C opt-level=3 --emit=obj foo.rs
$ size -A foo.o
foo.o :
section size addr
.text 0 0
.text._ZN3foo3foo17he211d7b4a3a0c16eE 1 0
.text._ZN3foo3bar17h1acb594305f70c2eE 22 0
.note.GNU-stack 0 0
.eh_frame 72 0
Total 95
$ rustc -C opt-level=3 --emit=obj -Z emit-stack-sizes foo.rs
$ size -A foo.o
foo.o :
section size addr
.text 0 0
.text._ZN3foo3foo17he211d7b4a3a0c16eE 1 0
.stack_sizes 9 0
.text._ZN3foo3bar17h1acb594305f70c2eE 22 0
.stack_sizes 9 0
.note.GNU-stack 0 0
.eh_frame 72 0
Total 113
As of LLVM 7.0 the data will be written into a section named .stack_sizes
and
the format is "an array of pairs of function symbol values (pointer size) and
stack sizes (unsigned LEB128)".
$ objdump -d foo.o
foo.o: file format elf64-x86-64
Disassembly of section .text._ZN3foo3foo17he211d7b4a3a0c16eE:
0000000000000000 <_ZN3foo3foo17he211d7b4a3a0c16eE>:
0: c3 retq
Disassembly of section .text._ZN3foo3bar17h1acb594305f70c2eE:
0000000000000000 <_ZN3foo3bar17h1acb594305f70c2eE>:
0: 48 83 ec 10 sub $0x10,%rsp
4: 48 8d 44 24 08 lea 0x8(%rsp),%rax
9: 48 89 04 24 mov %rax,(%rsp)
d: 48 8b 04 24 mov (%rsp),%rax
11: 48 83 c4 10 add $0x10,%rsp
15: c3 retq
$ objdump -s -j .stack_sizes foo.o
foo.o: file format elf64-x86-64
Contents of section .stack_sizes:
0000 00000000 00000000 00 .........
Contents of section .stack_sizes:
0000 00000000 00000000 10 .........
It's important to note that linkers will discard this linker section by default. To preserve the section you can use a linker script like the one shown below.
/* file: keep-stack-sizes.x */
SECTIONS
{
/* `INFO` makes the section not allocatable so it won't be loaded into memory */
.stack_sizes (INFO) :
{
KEEP(*(.stack_sizes));
}
}
The linker script must be passed to the linker using a rustc flag like -C link-arg
.
// file: src/main.rs
use std::ptr;
#[inline(never)]
fn main() {
let xs = [0u32; 2];
// force LLVM to allocate `xs` on the stack
unsafe { ptr::read_volatile(&xs.as_ptr()); }
}
$ RUSTFLAGS="-Z emit-stack-sizes" cargo build --release
$ size -A target/release/hello | grep stack_sizes || echo section was not found
section was not found
$ RUSTFLAGS="-Z emit-stack-sizes" cargo rustc --release -- \
-C link-arg=-Wl,-Tkeep-stack-sizes.x \
-C link-arg=-N
$ size -A target/release/hello | grep stack_sizes
.stack_sizes 90 176272
$ # non-allocatable section (flags don't contain the "A" (alloc) flag)
$ readelf -S target/release/hello
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
(..)
[1031] .stack_sizes PROGBITS 000000000002b090 0002b0f0
000000000000005a 0000000000000000 L 5 0 1
$ objdump -s -j .stack_sizes target/release/hello
target/release/hello: file format elf64-x86-64
Contents of section .stack_sizes:
2b090 c0040000 00000000 08f00400 00000000 ................
2b0a0 00080005 00000000 00000810 05000000 ................
2b0b0 00000000 20050000 00000000 10400500 .... ........@..
2b0c0 00000000 00087005 00000000 00000080 ......p.........
2b0d0 05000000 00000000 90050000 00000000 ................
2b0e0 00a00500 00000000 0000 ..........
Author note: I'm not entirely sure why, in this case,
-N
is required in addition to-Tkeep-stack-sizes.x
. For example, it's not required when producing statically linked files for the ARM Cortex-M architecture.
env-set
The tracking issue for this feature is: #118372.
This option flag allows to specify environment variables value at compile time to be
used by env!
and option_env!
macros. It also impacts tracked_env::var
function
from the proc_macro
crate.
This information will be stored in the dep-info files. For more information about dep-info files, take a look here.
When retrieving an environment variable value, the one specified by --env-set
will take
precedence. For example, if you want have PATH=a
in your environment and pass:
rustc --env-set PATH=env
Then you will have:
#![allow(unused)] fn main() { assert_eq!(env!("PATH"), "env"); }
It will trigger a new compilation if any of the --env-set
argument value is different.
So if you first passed:
--env-set A=B --env X=12
and then on next compilation:
--env-set A=B
X
value is different (not set) so the code will be re-compiled.
Please note that on Windows, environment variables are case insensitive but case
preserving whereas rustc
's environment variables are case sensitive. For example,
having Path
in your environment (case insensitive) is different than using
rustc --env-set Path=...
(case sensitive).
export-executable-symbols
The tracking issue for this feature is: #84161.
The -Zexport-executable-symbols
compiler flag makes rustc
export symbols from executables. The resulting binary is runnable, but can also be used as a dynamic library. This is useful for interoperating with programs written in other languages, in particular languages with a runtime like Java or Lua.
For example on windows:
#[no_mangle] fn my_function() -> usize { return 42; } fn main() { println!("Hello, world!"); }
A standard cargo build
will produce a .exe
without an export directory. When the export-executable-symbols
flag is added
export RUSTFLAGS="-Zexport-executable-symbols"
cargo build
the binary has an export directory with the functions:
The Export Tables (interpreted .edata section contents)
...
[Ordinal/Name Pointer] Table
[ 0] my_function
[ 1] main
(the output of objdump -x
on the binary)
Please note that the #[no_mangle]
attribute is required. Without it, the symbol is not exported.
The equivalent of this flag in C and C++ compilers is the __declspec(dllexport)
annotation or the -rdynamic
linker flag.
--extern
Options
- Tracking issue for
--extern
crate modifiers: #98405 - Tracking issue for
noprelude
: #98398 - Tracking issue for
priv
: #98399 - Tracking issue for
nounused
: #98400 - Tracking issue for
force
: #111302
The behavior of the --extern
flag can be modified with noprelude
, priv
or nounused
options.
This is unstable feature, so you have to provide -Zunstable-options
to enable it.
Examples
Use your own build of the core
crate.
rustc main.rs -Z unstable-options --extern noprelude:core=libcore.rlib
To use multiple options, separate them with a comma:
rustc main.rs -Z unstable-options --extern noprelude,priv,nounused:mydep=mydep.rlib
Options
noprelude
: Do not add the crate to the external prelude. If used, it will need to be imported usingextern crate
. This is used by the build-std project to simulate compatibility with sysroot-only crates.priv
: Mark the crate as a private dependency for theexported_private_dependencies
lint.nounused
: Suppressunused-crate-dependencies
warnings for the crate.force
: Resolve the crate as if it is used, even if it is not used. This can be used to satisfy compilation session requirements like the presence of an allocator or panic handler.
external-clangrt
This option controls whether the compiler links in its own runtime library for sanitizers. Passing this flag makes the compiler not link its own library. For more information, see the section in the sanitizers doc on working with other languages.
fixed-x18
This option prevents the compiler from using the x18 register. It is only
supported on aarch64
.
From the ABI spec:
X18 is the platform register and is reserved for the use of platform ABIs. This is an additional temporary register on platforms that don't assign a special meaning to it.
This flag only has an effect when the x18 register would otherwise be considered a temporary register. When the flag is applied, x18 is always a reserved register.
This flag is intended for use with the shadow call stack sanitizer. Generally, when that sanitizer is enabled, the x18 register is used to store a pointer to the shadow stack. Enabling this flag prevents the compiler from overwriting the shadow stack pointer with temporary data, which is necessary for the sanitizer to work correctly.
Currently, the -Zsanitizer=shadow-call-stack
flag is only supported on
platforms that always treat x18 as a reserved register, and the -Zfixed-x18
flag is not required to use the sanitizer on such platforms. However, the
sanitizer may be supported on targets where this is not the case in the future.
One way to do so now on Nightly compilers is to explicitly supply this -Zfixed-x18
flag with aarch64
targets, so that the sanitizer is available for instrumentation
on targets like aarch64-unknown-none
, for instance. However, discretion is still
required to make sure that the runtime support is in place for this sanitizer
to be effective.
It is undefined behavior for -Zsanitizer=shadow-call-stack
code to call into
code where x18 is a temporary register. On the other hand, when you are not
using the shadow call stack sanitizer, compilation units compiled with and
without the -Zfixed-x18
flag are compatible with each other.
fmt-debug
The tracking issue for this feature is: #129709.
Option -Z fmt-debug=val
controls verbosity of derived Debug
implementations
and debug formatting in format strings ({:?}
).
-
full
—#[derive(Debug)]
prints types recursively. This is the default behavior. -
shallow
—#[derive(Debug)]
prints only the type name, or name of a variant of a fieldless enums. Details of theDebug
implementation are not stable and may change in the future. Behavior of customfmt::Debug
implementations is not affected. -
none
—#[derive(Debug)]
does not print anything at all.{:?}
in formatting strings has no effect. This option may reduce size of binaries, and remove occurrences of type names in the binary that are not removed by striping symbols. However, it may also causepanic!
andassert!
messages to be incomplete.
function-return
The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/116853.
Option -Zfunction-return
controls how function returns are converted.
It is equivalent to Clang's and GCC's -mfunction-return
. The Linux kernel
uses it for RETHUNK builds. For details, see LLVM commit 2240d72f15f3 ("[X86]
initial -mfunction-return=thunk-extern support") which introduces the feature.
Supported values for this option are:
keep
: do not convert function returns.thunk-extern
: convert function returns (ret
) to jumps (jmp
) to an external symbol called__x86_return_thunk
.
Like in Clang, GCC's values thunk
and thunk-inline
are not supported.
Only x86 and non-large code models are supported.
instrument-xray
The tracking issue for this feature is: #102921.
Enable generation of NOP sleds for XRay function tracing instrumentation. For more information on XRay, read LLVM documentation, and/or the XRay whitepaper.
Set the -Z instrument-xray
compiler flag in order to enable XRay instrumentation.
-Z instrument-xray
– use the default settings-Z instrument-xray=skip-exit
– configure a custom setting-Z instrument-xray=ignore-loops,instruction-threshold=300
– multiple settings separated by commas
Supported options:
always
– force instrumentation of all functionsnever
– do no instrument any functionsignore-loops
– ignore presence of loops, instrument functions based only on instruction countinstruction-threshold=10
– set a different instruction threshold for instrumentationskip-entry
– do no instrument function entryskip-exit
– do no instrument function exit
The default settings are:
- instrument both entry & exit from functions
- instrument functions with at least 200 instructions, or containing a non-trivial loop
Note that -Z instrument-xray
only enables generation of NOP sleds
which on their own don't do anything useful.
In order to actually trace the functions,
you will need to link a separate runtime library of your choice,
such as Clang's XRay Runtime Library.
link-native-libraries
This option allows ignoring libraries specified in #[link]
attributes instead of passing them to the linker.
This can be useful in build systems that manage native libraries themselves and pass them manually,
e.g. with -Clink-arg
.
yes
- Pass native libraries to the linker. Default.no
- Don't pass native libraries to the linker.
linker-features
The -Zlinker-features
compiler flag allows enabling or disabling specific features used during
linking, and is intended to be stabilized under the codegen options as -Clinker-features
.
These feature flags are a flexible extension mechanism that is complementary to linker flavors, designed to avoid the combinatorial explosion of having to create a new set of flavors for each linker feature we'd want to use.
For example, this design allows:
- default feature sets for principal flavors, or for specific targets.
- flavor-specific features: for example, clang offers automatic cross-linking with
--target
, which gcc-style compilers don't support. The flavor is still a C/C++ compiler, and we don't want to multiply the number of flavors for this use-case. Instead, we can have a single+target
feature. - umbrella features: for example, if clang accumulates more features in the future than just the
+target
above. That could be modeled as+clang
. - niche features for resolving specific issues: for example, on Apple targets the linker flag
implementing the
as-needed
native link modifier (#99424) is only possible on sufficiently recent linker versions. - still allows for discovery and automation, for example via feature detection. This can be useful in exotic environments/build systems.
The flag accepts a comma-separated list of features, individually enabled (+features
) or disabled
(-features
), though currently only one is exposed on the CLI:
lld
: to toggle using the lld linker, either the system-installed binary, or the self-containedrust-lld
linker.
As described above, this list is intended to grow in the future.
One of the most common uses of this flag will be to toggle self-contained linking with rust-lld
on
and off: -Clinker-features=+lld -Clink-self-contained=+linker
will use the toolchain's rust-lld
as the linker. Inversely, -Clinker-features=-lld
would opt out of that, if the current target had
self-contained linking enabled by default.
lint-llvm-ir
This flag will add LintPass
to the start of the pipeline.
You can use it to check for common errors in the LLVM IR generated by rustc
.
You can add -Cllvm-args=-lint-abort-on-error
to abort the process if errors were found.
llvm-module-flag
This flag allows adding a key/value to the !llvm.module.flags
metadata in the
LLVM-IR for a compiled Rust module. The syntax is
-Z llvm_module_flag=<name>:<type>:<value>:<behavior>
Currently only u32 values are supported but the type is required to be specified
for forward compatibility. The behavior
element must match one of the named
LLVM metadata behaviors
location-detail
The tracking issue for this feature is: #70580.
Option -Z location-detail=val
controls what location details are tracked when
using caller_location
. This allows users to control what location details
are printed as part of panic messages, by allowing them to exclude any combination
of filenames, line numbers, and column numbers. This option is intended to provide
users with a way to mitigate the size impact of #[track_caller]
.
This option supports a comma separated list of location details to be included. Valid options within this list are:
file
- the filename of the panic will be included in the panic outputline
- the source line of the panic will be included in the panic outputcolumn
- the source column of the panic will be included in the panic output
Any combination of these three options are supported. Alternatively, you can pass
none
to this option, which results in no location details being tracked.
If this option is not specified, all three are included by default.
An example of a panic output when using -Z location-detail=line
:
panicked at 'Process blink had a fault', <redacted>:323:0
The code size savings from this option are two-fold. First, the &'static str
values
for each path to a file containing a panic are removed from the binary. For projects
with deep directory structures and many files with panics, this can add up. This category
of savings can only be realized by excluding filenames from the panic output. Second,
savings can be realized by allowing multiple panics to be fused into a single panicking
branch. It is often the case that within a single file, multiple panics with the same
panic message exist -- e.g. two calls to Option::unwrap()
in a single line, or
two calls to Result::expect()
on adjacent lines. If column and line information
are included in the Location
struct passed to the panic handler, these branches cannot
be fused, as the output is different depending on which panic occurs. However if line
and column information is identical for all panics, these branches can be fused, which
can lead to substantial code size savings, especially for small embedded binaries with
many panics.
The savings from this option are amplified when combined with the use of -Zbuild-std
, as
otherwise paths for panics within the standard library are still included in your binary.
move_size_limit
The -Zmove-size-limit=N
compiler flag enables large_assignments
lints which
will warn when moving objects whose size exceeds N
bytes.
Lint warns only about moves in functions that participate in code generation.
Consequently it will be ineffective for compiler invocation that emit
metadata only, i.e., cargo check
like workflows.
no-jump-tables
The tracking issue for this feature is #116592
This option enables the -fno-jump-tables
flag for LLVM, which makes the
codegen backend avoid generating jump tables when lowering switches.
This option adds the LLVM no-jump-tables=true
attribute to every function.
The option can be used to help provide protection against jump-oriented-programming (JOP) attacks, such as with the linux kernel's IBT.
RUSTFLAGS="-Zno-jump-tables" cargo +nightly build -Z build-std
no-parallel-llvm
This flag disables parallelization of codegen and linking, while otherwise preserving behavior with regard to codegen units and LTO.
This flag is not useful for regular users, but it can be useful for debugging the backend. Codegen issues commonly only manifest under specific circumstances, e.g. if multiple codegen units are used and ThinLTO is enabled. Serialization of these threaded configurations makes the use of LLVM debugging facilities easier, by avoiding the interleaving of output.
no-unique-section-names
This flag currently applies only to ELF-based targets using the LLVM codegen backend. It prevents the generation of unique ELF section names for each separate code and data item when -Z function-sections
is also in use, which is the default for most targets. This option can reduce the size of object files, and depending on the linker, the final ELF binary as well.
For example, a function func
will by default generate a code section called .text.func
. Normally this is fine because the linker will merge all those .text.*
sections into a single one in the binary. However, starting with LLVM 12, the backend will also generate unique section names for exception handling, so you would see a section name of .gcc_except_table.func
in the object file and potentially in the final ELF binary, which could add significant bloat to programs that contain many functions.
This flag instructs LLVM to use the same .text
and .gcc_except_table
section name for each function, and it is analogous to Clang's -fno-unique-section-names
option.
on-broken-pipe
The tracking issue for this feature is: #97889
Note: The ui for this feature was previously an attribute named #[unix_sigpipe = "..."]
.
Overview
The -Zon-broken-pipe=...
compiler flag can be used to specify how libstd shall setup SIGPIPE
on Unix platforms before invoking fn main()
. This flag is ignored on non-Unix targets. The flag can be used with three different values or be omitted entirely. It affects SIGPIPE
before fn main()
and before children get exec()
'ed:
Compiler flag | SIGPIPE before fn main() | SIGPIPE before child exec() |
---|---|---|
not used | SIG_IGN | SIG_DFL |
-Zon-broken-pipe=kill | SIG_DFL | not touched |
-Zon-broken-pipe=error | SIG_IGN | not touched |
-Zon-broken-pipe=inherit | not touched | not touched |
-Zon-broken-pipe
not used
If -Zon-broken-pipe
is not used, libstd will behave in the manner it has since 2014, before Rust 1.0. SIGPIPE
will be set to SIG_IGN
before fn main()
and result in EPIPE
errors which are converted to std::io::ErrorKind::BrokenPipe
.
When spawning child processes, SIGPIPE
will be set to SIG_DFL
before doing the underlying exec()
syscall.
-Zon-broken-pipe=kill
Set the SIGPIPE
handler to SIG_DFL
before invoking fn main()
. This will result in your program getting killed if it tries to write to a closed pipe. This is normally what you want if your program produces textual output.
When spawning child processes, SIGPIPE
will not be touched. This normally means child processes inherit SIG_DFL
for SIGPIPE
.
Example
fn main() { loop { println!("hello world"); } }
$ rustc -Zon-broken-pipe=kill main.rs
$ ./main | head -n1
hello world
-Zon-broken-pipe=error
Set the SIGPIPE
handler to SIG_IGN
before invoking fn main()
. This will result in ErrorKind::BrokenPipe
errors if you program tries to write to a closed pipe. This is normally what you want if you for example write socket servers, socket clients, or pipe peers.
When spawning child processes, SIGPIPE
will not be touched. This normally means child processes inherit SIG_IGN
for SIGPIPE
.
Example
fn main() { loop { println!("hello world"); } }
$ rustc -Zon-broken-pipe=error main.rs
$ ./main | head -n1
hello world
thread 'main' panicked at library/std/src/io/stdio.rs:1118:9:
failed printing to stdout: Broken pipe (os error 32)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-Zon-broken-pipe=inherit
Leave SIGPIPE
untouched before entering fn main()
. Unless the parent process has changed the default SIGPIPE
handler from SIG_DFL
to something else, this will behave the same as -Zon-broken-pipe=kill
.
When spawning child processes, SIGPIPE
will not be touched. This normally means child processes inherit SIG_DFL
for SIGPIPE
.
patchable-function-entry
The -Z patchable-function-entry=total_nops,prefix_nops
or -Z patchable-function-entry=total_nops
compiler flag enables nop padding of function entries with 'total_nops' nops, with
an offset for the entry of the function at 'prefix_nops' nops. In the second form,
'prefix_nops' defaults to 0.
As an illustrative example, -Z patchable-function-entry=3,2
would produce:
nop
nop
function_label:
nop
//Actual function code begins here
This flag is used for hotpatching, especially in the Linux kernel. The flag
arguments are modeled after the -fpatchable-function-entry
flag as defined
for both Clang
and gcc
and is intended to provide the same effect.
print=check-cfg
The tracking issue for this feature is: #125704.
This option of the --print
flag print the list of expected cfgs.
This is related to the --check-cfg
flag which allows specifying arbitrary expected
names and values.
This print option works similarly to --print=cfg
(modulo check-cfg specifics):
- check_cfg syntax: output of --print=check-cfg
cfg(windows)
:windows
cfg(feature, values("foo", "bar"))
:feature="foo"
andfeature="bar"
cfg(feature, values(none(), ""))
:feature
andfeature=""
cfg(feature, values(any()))
:feature=any()
cfg(feature, values())
:feature=
cfg(any())
:any()
- nothing:
any()=any()
To be used like this:
rustc --print=check-cfg -Zunstable-options lib.rs
profile
The tracking issue for this feature is: #42524.
This feature allows the generation of code coverage reports.
Set the -Zprofile
compiler flag in order to enable gcov profiling.
For example:
cargo new testgcov --bin
cd testgcov
export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
export CARGO_INCREMENTAL=0
cargo build
cargo run
Once you've built and run your program, files with the gcno
(after build) and gcda
(after execution) extensions will be created.
You can parse them with llvm-cov gcov or grcov.
Please note that RUSTFLAGS
by default applies to everything that cargo builds and runs during a build!
When the --target
flag is explicitly passed to cargo, the RUSTFLAGS
no longer apply to build scripts and procedural macros.
For more fine-grained control consider passing a RUSTC_WRAPPER
program to cargo that only adds the profiling flags to
rustc for the specific crates you want to profile.
profile-sample-use
-Zprofile-sample-use=code.prof
directs rustc
to use the profile
code.prof
as a source for Automatic Feedback Directed Optimization (AFDO).
See the documentation of -Zdebug-info-for-profiling
for more information
on using AFDO.
remap-cwd-prefix
The tracking issue for this feature is: #87325.
This flag will rewrite absolute paths under the current working directory, replacing the current working directory prefix with a specified value.
The given value may be absolute or relative, or empty. This switch takes
precedence over --remap-path-prefix
in case they would both match a given
path.
This flag helps to produce deterministic output, by removing the current working directory from build output, while allowing the command line to be universally reproducible, such that the same execution will work on all machines, regardless of build environment.
Example
# This would produce an absolute path to main.rs in build outputs of
# "./main.rs".
rustc -Z remap-cwd-prefix=. main.rs
remap-path-scope
The tracking issue for this feature is: #111540.
When the --remap-path-prefix
option is passed to rustc, source path prefixes in all output will be affected by default.
The --remap-path-scope
argument can be used in conjunction with --remap-path-prefix
to determine paths in which output context should be affected.
This flag accepts a comma-separated list of values and may be specified multiple times, in which case the scopes are aggregated together. The valid scopes are:
macro
- apply remappings to the expansion ofstd::file!()
macro. This is where paths in embedded panic messages come fromdiagnostics
- apply remappings to printed compiler diagnosticsdebuginfo
- apply remappings to debug informationsobject
- apply remappings to all paths in compiled executables or libraries, but not elsewhere. Currently an alias formacro,debuginfo
.all
- an alias for all of the above, also equivalent to supplying only--remap-path-prefix
without--remap-path-scope
.
Example
# This would produce an absolute path to main.rs in build outputs of
# "./main.rs".
rustc --remap-path-prefix=$(PWD)=/remapped -Zremap-path-scope=object main.rs
report-time
The tracking issue for this feature is: #64888
The report-time
feature adds a possibility to report execution time of the
tests generated via libtest
.
This is unstable feature, so you have to provide -Zunstable-options
to get
this feature working.
Sample usage command:
./test_executable -Zunstable-options --report-time
Available options:
--report-time
Show execution time of each test.
Threshold values for colorized output can be
configured via
`RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION`
and
`RUST_TEST_TIME_DOCTEST` environment variables.
Expected format of environment variable is
`VARIABLE=WARN_TIME,CRITICAL_TIME`.
Not available for --format=terse
--ensure-time
Treat excess of the test execution time limit as
error.
Threshold values for this option can be configured via
`RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION`
and
`RUST_TEST_TIME_DOCTEST` environment variables.
Expected format of environment variable is
`VARIABLE=WARN_TIME,CRITICAL_TIME`.
`CRITICAL_TIME` here means the limit that should not be
exceeded by test.
Example of the environment variable format:
RUST_TEST_TIME_UNIT=100,200
where 100 stands for warn time, and 200 stands for critical time.
Examples
cargo test --tests -- -Zunstable-options --report-time
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running target/debug/deps/example-27fb188025bec02c
running 3 tests
test tests::unit_test_quick ... ok <0.000s>
test tests::unit_test_warn ... ok <0.055s>
test tests::unit_test_critical ... ok <0.110s>
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Running target/debug/deps/tests-cedb06f6526d15d9
running 3 tests
test unit_test_quick ... ok <0.000s>
test unit_test_warn ... ok <0.550s>
test unit_test_critical ... ok <1.100s>
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
sanitizer
Sanitizers are tools that help detect and prevent various types of bugs and vulnerabilities in software. They are available in compilers and work by instrumenting the code to add additional runtime checks. While they provide powerful tools for identifying bugs or security issues, it's important to note that using sanitizers can introduce runtime overhead and might not catch all possible issues. Therefore, they are typically used alongside other best practices in software development, such as testing and fuzzing, to ensure the highest level of software quality and security.
The tracking issues for this feature are:
This feature allows for use of one of following sanitizers:
-
Those intended for testing or fuzzing (but not production use):
- AddressSanitizer a fast memory error detector.
- HWAddressSanitizer a memory error detector similar to AddressSanitizer, but based on partial hardware assistance.
- LeakSanitizer a run-time memory leak detector.
- MemorySanitizer a detector of uninitialized reads.
- ThreadSanitizer a fast data race detector.
-
Those that apart from testing, may be used in production:
- ControlFlowIntegrity LLVM Control Flow Integrity (CFI) provides forward-edge control flow protection.
- DataFlowSanitizer a generic dynamic data flow analysis framework.
- KernelControlFlowIntegrity LLVM Kernel Control Flow Integrity (KCFI) provides forward-edge control flow protection for operating systems kernels.
- MemTagSanitizer fast memory error detector based on Armv8.5-A Memory Tagging Extension.
- SafeStack provides backward-edge control flow protection by separating the stack into safe and unsafe regions.
- ShadowCallStack provides backward-edge control flow protection (aarch64 only).
To enable a sanitizer compile with -Zsanitizer=address
, -Zsanitizer=cfi
,
-Zsanitizer=dataflow
,-Zsanitizer=hwaddress
, -Zsanitizer=leak
,
-Zsanitizer=memory
, -Zsanitizer=memtag
, -Zsanitizer=shadow-call-stack
, or
-Zsanitizer=thread
. You might also need the --target
and build-std
flags.
If you're working with other languages that are also instrumented with sanitizers,
you might need the external-clangrt
flag. See the section on
working with other languages.
Example:
$ RUSTFLAGS=-Zsanitizer=address cargo build -Zbuild-std --target x86_64-unknown-linux-gnu
Additional options for sanitizers can be passed to LLVM command line argument
processor via LLVM arguments using llvm-args
codegen option (e.g.,
-Cllvm-args=-dfsan-combine-pointer-labels-on-load=false
). See the sanitizer
documentation for more information about additional options.
AddressSanitizer
AddressSanitizer is a memory error detector. It can detect the following types of bugs:
- Out of bound accesses to heap, stack and globals
- Use after free
- Use after return (runtime flag
ASAN_OPTIONS=detect_stack_use_after_return=1
) - Use after scope
- Double-free, invalid free
- Memory leaks
The memory leak detection is enabled by default on Linux, and can be enabled
with runtime flag ASAN_OPTIONS=detect_leaks=1
on macOS.
AddressSanitizer is supported on the following targets:
aarch64-apple-darwin
aarch64-unknown-fuchsia
aarch64-unknown-linux-gnu
x86_64-apple-darwin
x86_64-unknown-fuchsia
x86_64-unknown-freebsd
x86_64-unknown-linux-gnu
AddressSanitizer works with non-instrumented code although it will impede its ability to detect some bugs. It is not expected to produce false positive reports.
See the Clang AddressSanitizer documentation for more details.
Examples
Stack buffer overflow:
fn main() { let xs = [0, 1, 2, 3]; let _y = unsafe { *xs.as_ptr().offset(4) }; }
$ export RUSTFLAGS=-Zsanitizer=address RUSTDOCFLAGS=-Zsanitizer=address
$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu
==37882==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe400e6250 at pc 0x5609a841fb20 bp 0x7ffe400e6210 sp 0x7ffe400e6208
READ of size 4 at 0x7ffe400e6250 thread T0
#0 0x5609a841fb1f in example::main::h628ffc6626ed85b2 /.../src/main.rs:3:23
...
Address 0x7ffe400e6250 is located in stack of thread T0 at offset 48 in frame
#0 0x5609a841f8af in example::main::h628ffc6626ed85b2 /.../src/main.rs:1
This frame has 1 object(s):
[32, 48) 'xs' (line 2) <== Memory access at offset 48 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /.../src/main.rs:3:23 in example::main::h628ffc6626ed85b2
Shadow bytes around the buggy address:
0x100048014bf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100048014c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100048014c10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100048014c20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100048014c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100048014c40: 00 00 00 00 f1 f1 f1 f1 00 00[f3]f3 00 00 00 00
0x100048014c50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100048014c60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100048014c70: f1 f1 f1 f1 00 00 f3 f3 00 00 00 00 00 00 00 00
0x100048014c80: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
0x100048014c90: 00 00 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==37882==ABORTING
Use of a stack object after its scope has already ended:
static mut P: *mut usize = std::ptr::null_mut(); fn main() { unsafe { { let mut x = 0; P = &mut x; } std::ptr::write_volatile(P, 123); } }
$ export RUSTFLAGS=-Zsanitizer=address RUSTDOCFLAGS=-Zsanitizer=address
$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu
=================================================================
==39249==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffc7ed3e1a0 at pc 0x55c98b262a8e bp 0x7ffc7ed3e050 sp 0x7ffc7ed3e048
WRITE of size 8 at 0x7ffc7ed3e1a0 thread T0
#0 0x55c98b262a8d in core::ptr::write_volatile::he21f1df5a82f329a /.../src/rust/src/libcore/ptr/mod.rs:1048:5
#1 0x55c98b262cd2 in example::main::h628ffc6626ed85b2 /.../src/main.rs:9:9
...
Address 0x7ffc7ed3e1a0 is located in stack of thread T0 at offset 32 in frame
#0 0x55c98b262bdf in example::main::h628ffc6626ed85b2 /.../src/main.rs:3
This frame has 1 object(s):
[32, 40) 'x' (line 6) <== Memory access at offset 32 is inside this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-scope /.../src/rust/src/libcore/ptr/mod.rs:1048:5 in core::ptr::write_volatile::he21f1df5a82f329a
Shadow bytes around the buggy address:
0x10000fd9fbe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10000fd9fbf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10000fd9fc00: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
0x10000fd9fc10: f8 f8 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
0x10000fd9fc20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10000fd9fc30: f1 f1 f1 f1[f8]f3 f3 f3 00 00 00 00 00 00 00 00
0x10000fd9fc40: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
0x10000fd9fc50: 00 00 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
0x10000fd9fc60: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 f3 f3
0x10000fd9fc70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10000fd9fc80: 00 00 00 00 f1 f1 f1 f1 00 00 f3 f3 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==39249==ABORTING
ControlFlowIntegrity
The LLVM CFI support in the Rust compiler provides forward-edge control flow protection for both Rust-compiled code only and for C or C++ and Rust -compiled code mixed-language binaries, also known as “mixed binaries” (i.e., for when C or C++ and Rust -compiled code share the same virtual address space), by aggregating function pointers in groups identified by their return and parameter types.
LLVM CFI can be enabled with -Zsanitizer=cfi
and requires LTO (i.e.,
-Clinker-plugin-lto
or -Clto
). Cross-language LLVM CFI can be enabled with
-Zsanitizer=cfi
, and requires the -Zsanitizer-cfi-normalize-integers
option
to be used with Clang -fsanitize-cfi-icall-experimental-normalize-integers
option for cross-language LLVM CFI support, and proper (i.e., non-rustc) LTO
(i.e., -Clinker-plugin-lto
).
It is recommended to rebuild the standard library with CFI enabled by using the
Cargo build-std feature (i.e., -Zbuild-std
) when enabling CFI.
See the Clang ControlFlowIntegrity documentation for more details.
Example 1: Redirecting control flow using an indirect branch/call to an invalid destination
#![feature(naked_functions)]
use std::arch::asm;
use std::mem;
fn add_one(x: i32) -> i32 {
x + 1
}
#[naked]
pub extern "C" fn add_two(x: i32) {
// x + 2 preceded by a landing pad/nop block
unsafe {
asm!(
"
nop
nop
nop
nop
nop
nop
nop
nop
nop
lea eax, [rdi+2]
ret
",
options(noreturn)
);
}
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: fn(i32) -> i32 = unsafe {
// Offset 0 is a valid branch/call destination (i.e., the function entry
// point), but offsets 1-8 within the landing pad/nop block are invalid
// branch/call destinations (i.e., within the body of the function).
mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5))
};
let next_answer = do_twice(f, 5);
println!("The next answer is: {}", next_answer);
}
Fig. 1. Redirecting control flow using an indirect branch/call to an invalid destination (i.e., within the body of the function).
$ cargo run --release
Compiling rust-cfi-1 v0.1.0 (/home/rcvalle/rust-cfi-1)
Finished release [optimized] target(s) in 0.42s
Running `target/release/rust-cfi-1`
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
Fig. 2. Build and execution of Fig. 1 with LLVM CFI disabled.
$ RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi" cargo run -Zbuild-std -Zbuild-std-features --release --target x86_64-unknown-linux-gnu
...
Compiling rust-cfi-1 v0.1.0 (/home/rcvalle/rust-cfi-1)
Finished release [optimized] target(s) in 1m 08s
Running `target/x86_64-unknown-linux-gnu/release/rust-cfi-1`
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
Fig. 3. Build and execution of Fig. 1 with LLVM CFI enabled.
When LLVM CFI is enabled, if there are any attempts to change/hijack control flow using an indirect branch/call to an invalid destination, the execution is terminated (see Fig. 3).
Example 2: Redirecting control flow using an indirect branch/call to a function with a different number of parameters
use std::mem; fn add_one(x: i32) -> i32 { x + 1 } fn add_two(x: i32, _y: i32) -> i32 { x + 2 } fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { f(arg) + f(arg) } fn main() { let answer = do_twice(add_one, 5); println!("The answer is: {}", answer); println!("With CFI enabled, you should not see the next answer"); let f: fn(i32) -> i32 = unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; let next_answer = do_twice(f, 5); println!("The next answer is: {}", next_answer); }
Fig. 4. Redirecting control flow using an indirect branch/call to a function with a different number of parameters than arguments intended/passed in the call/branch site.
$ cargo run --release
Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2)
Finished release [optimized] target(s) in 0.43s
Running `target/release/rust-cfi-2`
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
Fig. 5. Build and execution of Fig. 4 with LLVM CFI disabled.
$ RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi" cargo run -Zbuild-std -Zbuild-std-features --release --target x86_64-unknown-linux-gnu
...
Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2)
Finished release [optimized] target(s) in 1m 08s
Running `target/x86_64-unknown-linux-gnu/release/rust-cfi-2`
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
Fig. 6. Build and execution of Fig. 4 with LLVM CFI enabled.
When LLVM CFI is enabled, if there are any attempts to change/hijack control flow using an indirect branch/call to a function with different number of parameters than arguments intended/passed in the call/branch site, the execution is also terminated (see Fig. 6).
Example 3: Redirecting control flow using an indirect branch/call to a function with different return and parameter types
use std::mem; fn add_one(x: i32) -> i32 { x + 1 } fn add_two(x: i64) -> i64 { x + 2 } fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { f(arg) + f(arg) } fn main() { let answer = do_twice(add_one, 5); println!("The answer is: {}", answer); println!("With CFI enabled, you should not see the next answer"); let f: fn(i32) -> i32 = unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; let next_answer = do_twice(f, 5); println!("The next answer is: {}", next_answer); }
Fig. 7. Redirecting control flow using an indirect branch/call to a function with different return and parameter types than the return type expected and arguments intended/passed at the call/branch site.
$ cargo run --release
Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3)
Finished release [optimized] target(s) in 0.44s
Running `target/release/rust-cfi-3`
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
Fig. 8. Build and execution of Fig. 7 with LLVM CFI disabled.
$ RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi" cargo run -Zbuild-std -Zbuild-std-features --release --target x86_64-unknown-linux-gnu
...
Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3)
Finished release [optimized] target(s) in 1m 07s
Running `target/x86_64-unknown-linux-gnu/release/rust-cfi-3`
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
Fig. 9. Build and execution of Fig. 7 with LLVM CFI enabled.
When LLVM CFI is enabled, if there are any attempts to change/hijack control flow using an indirect branch/call to a function with different return and parameter types than the return type expected and arguments intended/passed in the call/branch site, the execution is also terminated (see Fig. 9).
Example 4: Redirecting control flow using an indirect branch/call to a function with different return and parameter types across the FFI boundary
int
do_twice(int (*fn)(int), int arg)
{
return fn(arg) + fn(arg);
}
Fig. 10. Example C library.
use std::mem;
#[link(name = "foo")]
extern "C" {
fn do_twice(f: unsafe extern "C" fn(i32) -> i32, arg: i32) -> i32;
}
unsafe extern "C" fn add_one(x: i32) -> i32 {
x + 1
}
unsafe extern "C" fn add_two(x: i64) -> i64 {
x + 2
}
fn main() {
let answer = unsafe { do_twice(add_one, 5) };
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: unsafe extern "C" fn(i32) -> i32 = unsafe {
mem::transmute::<*const u8, unsafe extern "C" fn(i32) -> i32>(add_two as *const u8)
};
let next_answer = unsafe { do_twice(f, 5) };
println!("The next answer is: {}", next_answer);
}
Fig. 11. Redirecting control flow using an indirect branch/call to a function with different return and parameter types than the return type expected and arguments intended/passed in the call/branch site, across the FFI boundary.
$ make
mkdir -p target/release
clang -I. -Isrc -Wall -c src/foo.c -o target/release/libfoo.o
llvm-ar rcs target/release/libfoo.a target/release/libfoo.o
RUSTFLAGS="-L./target/release -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build --release
Compiling rust-cfi-4 v0.1.0 (/home/rcvalle/rust-cfi-4)
Finished release [optimized] target(s) in 0.49s
$ ./target/release/rust-cfi-4
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
Fig. 12. Build and execution of Figs. 10–11 with LLVM CFI disabled.
$ make
mkdir -p target/release
clang -I. -Isrc -Wall -flto -fsanitize=cfi -fsanitize-cfi-icall-experimental-normalize-integers -fvisibility=hidden -c -emit-llvm src/foo.c -o target/release/libfoo.bc
llvm-ar rcs target/release/libfoo.a target/release/libfoo.bc
RUSTFLAGS="-L./target/release -Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers" cargo build -Zbuild-std -Zbuild-std-features --release --target x86_64-unknown-linux-gnu
...
Compiling rust-cfi-4 v0.1.0 (/home/rcvalle/rust-cfi-4)
Finished release [optimized] target(s) in 1m 06s
$ ./target/x86_64-unknown-linux-gnu/release/rust-cfi-4
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
Fig. 13. Build and execution of FIgs. 10–11 with LLVM CFI enabled.
When LLVM CFI is enabled, if there are any attempts to redirect control flow using an indirect branch/call to a function with different return and parameter types than the return type expected and arguments intended/passed in the call/branch site, even across the FFI boundary and for extern "C" function types indirectly called (i.e., callbacks/function pointers) across the FFI boundary, the execution is also terminated (see Fig. 13).
HWAddressSanitizer
HWAddressSanitizer is a newer variant of AddressSanitizer that consumes much less memory.
HWAddressSanitizer is supported on the following targets:
aarch64-linux-android
aarch64-unknown-linux-gnu
HWAddressSanitizer requires tagged-globals
target feature to instrument
globals. To enable this target feature compile with -C target-feature=+tagged-globals
See the Clang HWAddressSanitizer documentation for more details.
Example
Heap buffer overflow:
fn main() { let xs = vec![0, 1, 2, 3]; let _y = unsafe { *xs.as_ptr().offset(4) }; }
$ rustc main.rs -Zsanitizer=hwaddress -C target-feature=+tagged-globals -C
linker=aarch64-linux-gnu-gcc -C link-arg=-fuse-ld=lld --target
aarch64-unknown-linux-gnu
$ ./main
==241==ERROR: HWAddressSanitizer: tag-mismatch on address 0xefdeffff0050 at pc 0xaaaae0ae4a98
READ of size 4 at 0xefdeffff0050 tags: 2c/00 (ptr/mem) in thread T0
#0 0xaaaae0ae4a94 (/.../main+0x54a94)
...
[0xefdeffff0040,0xefdeffff0060) is a small allocated heap chunk; size: 32 offset: 16
0xefdeffff0050 is located 0 bytes to the right of 16-byte region [0xefdeffff0040,0xefdeffff0050)
allocated here:
#0 0xaaaae0acb80c (/.../main+0x3b80c)
...
Thread: T0 0xeffe00002000 stack: [0xffffc28ad000,0xffffc30ad000) sz: 8388608 tls: [0xffffaa10a020,0xffffaa10a7d0)
Memory tags around the buggy address (one tag corresponds to 16 bytes):
0xfefcefffef80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefcefffef90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefcefffefa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefcefffefb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefcefffefc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefcefffefd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefcefffefe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefcefffeff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0xfefceffff000: d7 d7 05 00 2c [00] 00 00 00 00 00 00 00 00 00 00
0xfefceffff010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefceffff020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefceffff030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefceffff040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefceffff050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefceffff060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefceffff070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xfefceffff080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Tags for short granules around the buggy address (one tag corresponds to 16 bytes):
0xfefcefffeff0: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
=>0xfefceffff000: .. .. 8c .. .. [..] .. .. .. .. .. .. .. .. .. ..
0xfefceffff010: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags
Registers where the failure occurred (pc 0xaaaae0ae4a98):
x0 2c00efdeffff0050 x1 0000000000000004 x2 0000000000000004 x3 0000000000000000
x4 0000fffefc30ac37 x5 000000000000005d x6 00000ffffc30ac37 x7 0000efff00000000
x8 2c00efdeffff0050 x9 0200efff00000000 x10 0000000000000000 x11 0200efff00000000
x12 0200effe00000310 x13 0200effe00000310 x14 0000000000000008 x15 5d00ffffc30ac360
x16 0000aaaae0ad062c x17 0000000000000003 x18 0000000000000001 x19 0000ffffc30ac658
x20 4e00ffffc30ac6e0 x21 0000aaaae0ac5e10 x22 0000000000000000 x23 0000000000000000
x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000
x28 0000000000000000 x29 0000ffffc30ac5a0 x30 0000aaaae0ae4a98
SUMMARY: HWAddressSanitizer: tag-mismatch (/.../main+0x54a94)
KernelControlFlowIntegrity
The LLVM Kernel Control Flow Integrity (CFI) support to the Rust compiler initially provides forward-edge control flow protection for operating systems kernels for Rust-compiled code only by aggregating function pointers in groups identified by their return and parameter types. (See LLVM commit cff5bef "KCFI sanitizer".)
Forward-edge control flow protection for C or C++ and Rust -compiled code "mixed binaries" (i.e., for when C or C++ and Rust -compiled code share the same virtual address space) will be provided in later work by defining and using compatible type identifiers (see Type metadata in the design document in the tracking issue #89653).
LLVM KCFI can be enabled with -Zsanitizer=kcfi
.
LLVM KCFI is supported on the following targets:
aarch64-linux-android
aarch64-unknown-linux-gnu
x86_64-linux-android
x86_64-unknown-linux-gnu
See the Clang KernelControlFlowIntegrity documentation for more details.
DataFlowSanitizer
DataFlowSanitizer is a generalised dynamic data flow analysis.
Unlike other Sanitizer tools, this tool is not designed to detect a specific class of bugs on its own. Instead, it provides a generic dynamic data flow analysis framework to be used by clients to help detect application-specific issues within their own code.
DataFlowSanitizer is supported on the following targets:
x86_64-unknown-linux-gnu
See the Clang DataFlowSanitizer documentation for more details.
KernelAddressSanitizer
KernelAddressSanitizer (KASAN) is a freestanding version of AddressSanitizer which is suitable for detecting memory errors in programs which do not have a runtime environment, such as operating system kernels. KernelAddressSanitizer requires manual implementation of the underlying functions used for tracking KernelAddressSanitizer state.
KernelAddressSanitizer is supported on the following targets:
aarch64-unknown-none
riscv64gc-unknown-none-elf
riscv64imac-unknown-none-elf
x86_64-unknown-none
See the Linux Kernel's KernelAddressSanitizer documentation for more details.
LeakSanitizer
LeakSanitizer is run-time memory leak detector.
LeakSanitizer is supported on the following targets:
aarch64-apple-darwin
aarch64-unknown-linux-gnu
x86_64-apple-darwin
x86_64-unknown-linux-gnu
See the Clang LeakSanitizer documentation for more details.
MemorySanitizer
MemorySanitizer is detector of uninitialized reads.
MemorySanitizer is supported on the following targets:
aarch64-unknown-linux-gnu
x86_64-unknown-freebsd
x86_64-unknown-linux-gnu
MemorySanitizer requires all program code to be instrumented. C/C++ dependencies
need to be recompiled using Clang with -fsanitize=memory
option. Failing to
achieve that will result in false positive reports.
See the Clang MemorySanitizer documentation for more details.
Example
Detecting the use of uninitialized memory. The -Zbuild-std
flag rebuilds and
instruments the standard library, and is strictly necessary for the correct
operation of the tool. The -Zsanitizer-memory-track-origins
enables tracking
of the origins of uninitialized memory:
use std::mem::MaybeUninit; fn main() { unsafe { let a = MaybeUninit::<[usize; 4]>::uninit(); let a = a.assume_init(); println!("{}", a[2]); } }
$ export \
RUSTFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins' \
RUSTDOCFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins'
$ cargo clean
$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu
==9416==WARNING: MemorySanitizer: use-of-uninitialized-value
#0 0x560c04f7488a in core::fmt::num::imp::fmt_u64::haa293b0b098501ca $RUST/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/src/rust/src/libcore/fmt/num.rs:202:16
...
Uninitialized value was stored to memory at
#0 0x560c04ae898a in __msan_memcpy.part.0 $RUST/src/llvm-project/compiler-rt/lib/msan/msan_interceptors.cc:1558:3
#1 0x560c04b2bf88 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:6:16
Uninitialized value was created by an allocation of 'a' in the stack frame of function '_ZN6memory4main17hd2333c1899d997f5E'
#0 0x560c04b2bc50 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:3
MemTagSanitizer
MemTagSanitizer detects a similar class of errors as AddressSanitizer and HardwareAddressSanitizer, but with lower overhead suitable for use as hardening for production binaries.
MemTagSanitizer is supported on the following targets:
aarch64-linux-android
aarch64-unknown-linux-gnu
MemTagSanitizer requires hardware support and the mte
target feature.
To enable this target feature compile with -C target-feature="+mte"
.
See the LLVM MemTagSanitizer documentation for more details.
SafeStack
SafeStack provides backward edge control flow protection by separating the stack into data which is only accessed safely (the safe stack) and all other data (the unsafe stack).
SafeStack can be enabled with the -Zsanitizer=safestack
option and is supported on the following targets:
x86_64-unknown-linux-gnu
See the Clang SafeStack documentation for more details.
ShadowCallStack
ShadowCallStack provides backward edge control flow protection by storing a function's return address in a separately allocated 'shadow call stack'
and loading the return address from that shadow call stack.
AArch64 and RISC-V both have a platform register defined in their ABIs, which is x18
and x3
/gp
respectively, that can optionally be reserved for this purpose.
Software support from the operating system and runtime may be required depending on the target platform which is detailed in the remaining section.
See the Clang ShadowCallStack documentation for more details.
ShadowCallStack can be enabled with -Zsanitizer=shadow-call-stack
option and is supported on the following targets:
AArch64 family
ShadowCallStack requires the use of the ABI defined platform register, x18
, which is required for code generation purposes.
When x18
is not reserved, and is instead used as a scratch register subsequently, enabling ShadowCallStack would lead to undefined behaviour
due to corruption of return address or invalid memory access when the instrumentation restores return register to the link register lr
from the
already clobbered x18
register.
In other words, code that is calling into or called by functions instrumented with ShadowCallStack must reserve the x18
register or preserve its value.
aarch64-linux-android
and aarch64-unknown-fuchsia
/aarch64-fuchsia
This target already reserves the x18
register.
A runtime must be provided by the application or operating system.
If bionic
is used on this target, the software support is provided.
Otherwise, a runtime needs to prepare a memory region and points x18
to the region which serves as the shadow call stack.
aarch64-unknown-none
In addition to support from a runtime by the application or operating system, the -Zfixed-x18
flag is also mandatory.
RISC-V 64 family
ShadowCallStack uses either the gp
register for software shadow stack, also known as x3
, or the ssp
register if Zicfiss
extension is available.
gp
/x3
is currently always reserved and available for ShadowCallStack instrumentation, and ssp
in case of Zicfiss
is only accessible through its dedicated shadow stack instructions.
Support from the runtime and operating system is required when gp
/x3
is used for software shadow stack.
A runtime must prepare a memory region and point gp
/x3
to the region before executing the code.
The following targets support ShadowCallStack.
riscv64imac-unknown-none-elf
riscv64gc-unknown-none-elf
riscv64gc-unknown-fuchsia
ThreadSanitizer
ThreadSanitizer is a data race detection tool. It is supported on the following targets:
aarch64-apple-darwin
aarch64-unknown-linux-gnu
x86_64-apple-darwin
x86_64-unknown-freebsd
x86_64-unknown-linux-gnu
To work correctly ThreadSanitizer needs to be "aware" of all synchronization
operations in a program. It generally achieves that through combination of
library interception (for example synchronization performed through
pthread_mutex_lock
/ pthread_mutex_unlock
) and compile time instrumentation
(e.g. atomic operations). Using it without instrumenting all the program code
can lead to false positive reports.
ThreadSanitizer does not support atomic fences std::sync::atomic::fence
,
nor synchronization performed using inline assembly code.
See the Clang ThreadSanitizer documentation for more details.
Example
static mut A: usize = 0; fn main() { let t = std::thread::spawn(|| { unsafe { A += 1 }; }); unsafe { A += 1 }; t.join().unwrap(); }
$ export RUSTFLAGS=-Zsanitizer=thread RUSTDOCFLAGS=-Zsanitizer=thread
$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu
==================
WARNING: ThreadSanitizer: data race (pid=10574)
Read of size 8 at 0x5632dfe3d030 by thread T1:
#0 example::main::_$u7b$$u7b$closure$u7d$$u7d$::h23f64b0b2f8c9484 ../src/main.rs:5:18 (example+0x86cec)
...
Previous write of size 8 at 0x5632dfe3d030 by main thread:
#0 example::main::h628ffc6626ed85b2 /.../src/main.rs:7:14 (example+0x868c8)
...
#11 main <null> (example+0x86a1a)
Location is global 'example::A::h43ac149ddf992709' of size 8 at 0x5632dfe3d030 (example+0x000000bd9030)
Instrumentation of external dependencies and std
The sanitizers to varying degrees work correctly with partially instrumented code. On the one extreme is LeakSanitizer that doesn't use any compile time instrumentation, on the other is MemorySanitizer that requires that all program code to be instrumented (failing to achieve that will inevitably result in false positives).
It is strongly recommended to combine sanitizers with recompiled and
instrumented standard library, for example using cargo -Zbuild-std
functionality.
Working with other languages
Sanitizers rely on compiler runtime libraries to function properly. Rust links in its own compiler runtime which might conflict with runtimes required by languages such as C++. Since Rust's runtime doesn't always contain the symbols required by C++ instrumented code, you might need to skip linking it so another runtime can be linked instead.
A separate unstable option -Zexternal-clangrt
can be used to make rustc skip
linking the compiler runtime for the sanitizer. This will require you to link
in an external runtime, such as from clang instead.
Build scripts and procedural macros
Use of sanitizers together with build scripts and procedural macros is technically possible, but in almost all cases it would be best avoided. This is especially true for procedural macros which would require an instrumented version of rustc.
In more practical terms when using cargo always remember to pass --target
flag, so that rustflags will not be applied to build scripts and procedural
macros.
Symbolizing the Reports
Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in PATH
.
Additional Information
- Sanitizers project page
- AddressSanitizer in Clang
- ControlFlowIntegrity in Clang
- DataFlowSanitizer in Clang
- HWAddressSanitizer in Clang
- Linux Kernel's KernelAddressSanitizer documentation
- LeakSanitizer in Clang
- MemorySanitizer in Clang
- MemTagSanitizer in LLVM
- ThreadSanitizer in Clang
self-profile
The -Zself-profile
compiler flag enables rustc's internal profiler.
When enabled, the compiler will output three binary files in the specified directory (or the current working directory if no directory is specified).
These files can be analyzed by using the tools in the measureme
repository.
To control the data recorded in the trace files, use the -Zself-profile-events
flag.
For example:
First, run a compilation session and provide the -Zself-profile
flag:
$ rustc --crate-name foo -Zself-profile
This will generate three files in the working directory such as:
foo-1234.events
foo-1234.string_data
foo-1234.string_index
Where foo
is the name of the crate and 1234
is the process id of the rustc process.
To get a summary of where the compiler is spending its time:
$ ../measureme/target/release/summarize summarize foo-1234
To generate a flamegraph of the same data:
$ ../measureme/target/release/inferno foo-1234
To dump the event data in a Chromium-profiler compatible format:
$ ../measureme/target/release/crox foo-1234
For more information, consult the measureme
documentation.
self-profile-events
The -Zself-profile-events
compiler flag controls what events are recorded by the self-profiler when it is enabled via the -Zself-profile
flag.
This flag takes a comma delimited list of event types to record.
For example:
$ rustc -Zself-profile -Zself-profile-events=default,args
Event types
-
query-provider
- Traces each query used internally by the compiler.
-
generic-activity
- Traces other parts of the compiler not covered by the query system.
-
query-cache-hit
- Adds tracing information that records when the in-memory query cache is "hit" and does not need to re-execute a query which has been cached.
- Disabled by default because this significantly increases the trace file size.
-
query-blocked
- Tracks time that a query tries to run but is blocked waiting on another thread executing the same query to finish executing.
- Query blocking only occurs when the compiler is built with parallel mode support.
-
incr-cache-load
- Tracks time that is spent loading and deserializing query results from the incremental compilation on-disk cache.
-
query-keys
- Adds a serialized representation of each query's query key to the tracing data.
- Disabled by default because this significantly increases the trace file size.
-
function-args
- Adds additional tracing data to some
generic-activity
events. - Disabled by default for parity with
query-keys
.
- Adds additional tracing data to some
-
llvm
- Adds tracing information about LLVM passes and codegeneration.
- Disabled by default because this significantly increases the trace file size.
Event synonyms
-
none
- Disables all events. Equivalent to the self-profiler being disabled.
-
default
- The default set of events which stikes a balance between providing detailed tracing data and adding additional overhead to the compilation.
-
args
- Equivalent to
query-keys
andfunction-args
.
- Equivalent to
-
all
- Enables all events.
Examples
Enable the profiler and capture the default set of events (both invocations are equivalent):
$ rustc -Zself-profile
$ rustc -Zself-profile -Zself-profile-events=default
Enable the profiler and capture the default events and their arguments:
$ rustc -Zself-profile -Zself-profile-events=default,args
shell-argfiles
The -Zshell-argfiles
compiler flag allows argfiles to be parsed using POSIX
"shell-style" quoting. When enabled, the compiler will use shlex
to parse the
arguments from argfiles specified with @shell:<path>
.
Because this feature controls the parsing of input arguments, the
-Zshell-argfiles
flag must be present before the argument specifying the
shell-style argument file.
src-hash-algorithm
The tracking issue for this feature is: #70401.
The -Z src-hash-algorithm
compiler flag controls which algorithm is used when hashing each source file. The hash is stored in the debug info and can be used by a debugger to verify the source code matches the executable.
Supported hash algorithms are: md5
, sha1
, and sha256
. Note that not all hash algorithms are supported by all debug info formats.
By default, the compiler chooses the hash algorithm based on the target specification.
temps-dir
The -Ztemps-dir
compiler flag specifies the directory to write the
intermediate files in. If not set, the output directory is used. This option is
useful if you are running more than one instance of rustc
(e.g. with different
--crate-type
settings), and you need to make sure they are not overwriting
each other's intermediate files. No files are kept unless -C save-temps=yes
is
also set.
tiny-const-eval-limit
The -Ztiny-const-eval-limit
compiler flag sets a tiny, non-configurable limit for const eval.
This flag should only be used by const eval tests in the rustc test suite.
tls_model
The tracking issue for this feature is: None.
Option -Z tls-model
controls TLS model used to
generate code for accessing #[thread_local]
static
items.
Supported values for this option are:
global-dynamic
- General Dynamic TLS Model (alternatively called Global Dynamic) is the most general option usable in all circumstances, even if the TLS data is defined in a shared library loaded at runtime and is accessed from code outside of that library. This is the default for most targets.local-dynamic
- model usable if the TLS data is only accessed from the shared library or executable it is defined in. The TLS data may be in a library loaded after startup (viadlopen
).initial-exec
- model usable if the TLS data is defined in the executable or in a shared library loaded at program startup. The TLS data must not be in a library loaded after startup (viadlopen
).local-exec
- model usable only if the TLS data is defined directly in the executable, but not in a shared library, and is accessed only from that executable.emulated
- Uses thread-specific data keys to implement emulated TLS. It is like using a general-dynamic TLS model for all modes.
rustc
and LLVM may use a more optimized model than specified if they know that we are producing
an executable rather than a library, or that the static
item is private enough.
ub-checks
The tracking issue for this feature is: #123499.
The -Zub-checks
compiler flag enables additional runtime checks that detect some causes of Undefined Behavior at runtime.
By default, -Zub-checks
flag inherits the value of -Cdebug-assertions
.
All checks are generated on a best-effort basis; even if we have a check implemented for some cause of Undefined Behavior, it may be possible for the check to not fire.
If a dependency is compiled with -Zub-checks=no
but the final binary or library is compiled with -Zub-checks=yes
, UB checks reached by the dependency are likely to be optimized out.
When -Zub-checks
detects UB, a non-unwinding panic is produced.
That means that we will not unwind the stack and will not call any Drop
impls, but we will execute the configured panic hook.
We expect that unsafe code has been written which relies on code not unwinding which may have UB checks inserted.
Ergo, an unwinding panic could easily turn works-as-intended UB into a much bigger problem.
Calling the panic hook theoretically has the same implications, but we expect that the standard library panic hook will be stateless enough to be always called, and that if a user has configured a panic hook that the hook may be very helpful to debugging the detected UB.
unsound-mir-opts
The -Zunsound-mir-opts
compiler flag enables MIR optimization passes which can cause unsound behavior.
This flag should only be used by MIR optimization tests in the rustc test suite.
verbose-asm
The tracking issue for this feature is: #126802.
This enables passing -Zverbose-asm
to get contextual comments added by LLVM.
Sample code:
#![allow(unused)] fn main() { #[no_mangle] pub fn foo(a: i32, b: i32) -> i32 { a + b } }
Default output:
foo:
push rax
add edi, esi
mov dword ptr [rsp + 4], edi
seto al
jo .LBB0_2
mov eax, dword ptr [rsp + 4]
pop rcx
ret
.LBB0_2:
lea rdi, [rip + .L__unnamed_1]
mov rax, qword ptr [rip + core::panicking::panic_const::panic_const_add_overflow::h9c85248fe0d735b2@GOTPCREL]
call rax
.L__unnamed_2:
.ascii "/app/example.rs"
.L__unnamed_1:
.quad .L__unnamed_2
.asciz "\017\000\000\000\000\000\000\000\004\000\000\000\005\000\000"
With -Zverbose-asm
:
foo: # @foo
# %bb.0:
push rax
add edi, esi
mov dword ptr [rsp + 4], edi # 4-byte Spill
seto al
jo .LBB0_2
# %bb.1:
mov eax, dword ptr [rsp + 4] # 4-byte Reload
pop rcx
ret
.LBB0_2:
lea rdi, [rip + .L__unnamed_1]
mov rax, qword ptr [rip + core::panicking::panic_const::panic_const_add_overflow::h9c85248fe0d735b2@GOTPCREL]
call rax
# -- End function
.L__unnamed_2:
.ascii "/app/example.rs"
.L__unnamed_1:
.quad .L__unnamed_2
.asciz "\017\000\000\000\000\000\000\000\004\000\000\000\005\000\000"
# DW_AT_external
virtual-function-elimination
This option controls whether LLVM runs the Virtual Function Elimination (VFE)
optimization. This optimization in only available with LTO, so this flag can
only be passed if -Clto
is also passed.
VFE makes it possible to remove functions from vtables that are never dynamically called by the rest of the code. Without this flag, LLVM makes the really conservative assumption, that if any function in a vtable is called, no function that is referenced by this vtable can be removed. With this flag additional information are given to LLVM, so that it can determine which functions are actually called and remove the unused functions.
Limitations
At the time of writing this flag may remove vtable functions too eagerly. One such example is in this code:
#![allow(unused)] fn main() { trait Foo { fn foo(&self) { println!("foo") } } impl Foo for usize {} pub struct FooBox(Box<dyn Foo>); pub fn make_foo() -> FooBox { FooBox(Box::new(0)) } #[inline] pub fn f(a: FooBox) { a.0.foo() } }
In the above code the Foo
trait is private, so an assumption is made that its
functions can only be seen/called from the current crate and can therefore get
optimized out, if unused. However, with make_foo
you can produce a wrapped
dyn Foo
type outside of the current crate, which can then be used in f
. Due
to inlining of f
, Foo::foo
can then be called from a foreign crate. This can
lead to miscompilations.
wasm-c-abi
This option controls whether Rust uses the spec-compliant C ABI when compiling
for the wasm32-unknown-unknown
target.
This makes it possible to be ABI-compatible with all other spec-compliant Wasm
like Rusts wasm32-wasi
.
This compiler flag is perma-unstable, as it will be enabled by default in the future with no option to fall back to the old non-spec-compliant ABI.
Language features
aarch64_unstable_target_feature
The tracking issue for this feature is: #44839
aarch64_ver_target_feature
The tracking issue for this feature is: #44839
abi_avr_interrupt
The tracking issue for this feature is: #69664
abi_c_cmse_nonsecure_call
The tracking issue for this feature is: #81391
The TrustZone-M
feature is available
for targets with the Armv8-M architecture profile (thumbv8m
in their target
name).
LLVM, the Rust compiler and the linker are providing
support for the
TrustZone-M feature.
One of the things provided, with this unstable feature, is the
C-cmse-nonsecure-call
function ABI. This ABI is used on function pointers to
non-secure code to mark a non-secure function call (see section
5.5 for details).
With this ABI, the compiler will do the following to perform the call:
- save registers needed after the call to Secure memory
- clear all registers that might contain confidential information
- clear the Least Significant Bit of the function address
- branches using the BLXNS instruction
To avoid using the non-secure stack, the compiler will constrain the number and type of parameters/return value.
The extern "C-cmse-nonsecure-call"
ABI is otherwise equivalent to the
extern "C"
ABI.
#![no_std]
#![feature(abi_c_cmse_nonsecure_call)]
#[no_mangle]
pub fn call_nonsecure_function(addr: usize) -> u32 {
let non_secure_function =
unsafe { core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn() -> u32>(addr) };
non_secure_function()
}
$ rustc --emit asm --crate-type lib --target thumbv8m.main-none-eabi function.rs
call_nonsecure_function:
.fnstart
.save {r7, lr}
push {r7, lr}
.setfp r7, sp
mov r7, sp
.pad #16
sub sp, #16
str r0, [sp, #12]
ldr r0, [sp, #12]
str r0, [sp, #8]
b .LBB0_1
.LBB0_1:
ldr r0, [sp, #8]
push.w {r4, r5, r6, r7, r8, r9, r10, r11}
bic r0, r0, #1
mov r1, r0
mov r2, r0
mov r3, r0
mov r4, r0
mov r5, r0
mov r6, r0
mov r7, r0
mov r8, r0
mov r9, r0
mov r10, r0
mov r11, r0
mov r12, r0
msr apsr_nzcvq, r0
blxns r0
pop.w {r4, r5, r6, r7, r8, r9, r10, r11}
str r0, [sp, #4]
b .LBB0_2
.LBB0_2:
ldr r0, [sp, #4]
add sp, #16
pop {r7, pc}
abi_msp430_interrupt
The tracking issue for this feature is: #38487
In the MSP430 architecture, interrupt handlers have a special calling
convention. You can use the "msp430-interrupt"
ABI to make the compiler apply
the right calling convention to the interrupt handlers you define.
#![feature(abi_msp430_interrupt)]
#![no_std]
// Place the interrupt handler at the appropriate memory address
// (Alternatively, you can use `#[used]` and remove `pub` and `#[no_mangle]`)
#[link_section = "__interrupt_vector_10"]
#[no_mangle]
pub static TIM0_VECTOR: extern "msp430-interrupt" fn() = tim0;
// The interrupt handler
extern "msp430-interrupt" fn tim0() {
// ..
}
$ msp430-elf-objdump -CD ./target/msp430/release/app
Disassembly of section __interrupt_vector_10:
0000fff2 <TIM0_VECTOR>:
fff2: 00 c0 interrupt service routine at 0xc000
Disassembly of section .text:
0000c000 <int::tim0>:
c000: 00 13 reti
abi_ptx
The tracking issue for this feature is: #38788
When emitting PTX code, all vanilla Rust functions (fn
) get translated to
"device" functions. These functions are not callable from the host via the
CUDA API so a crate with only device functions is not too useful!
OTOH, "global" functions can be called by the host; you can think of them
as the real public API of your crate. To produce a global function use the
"ptx-kernel"
ABI.
#![feature(abi_ptx)]
#![no_std]
pub unsafe extern "ptx-kernel" fn global_function() {
device_function();
}
pub fn device_function() {
// ..
}
$ xargo rustc --target nvptx64-nvidia-cuda --release -- --emit=asm
$ cat $(find -name '*.s')
//
// Generated by LLVM NVPTX Back-End
//
.version 3.2
.target sm_20
.address_size 64
// .globl _ZN6kernel15global_function17h46111ebe6516b382E
.visible .entry _ZN6kernel15global_function17h46111ebe6516b382E()
{
ret;
}
// .globl _ZN6kernel15device_function17hd6a0e4993bbf3f78E
.visible .func _ZN6kernel15device_function17hd6a0e4993bbf3f78E()
{
ret;
}
abi_riscv_interrupt
The tracking issue for this feature is: #111889
abi_unadjusted
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
abi_vectorcall
The tracking issue for this feature is: #124485
Adds support for the Windows "vectorcall"
ABI, the equivalent of __vectorcall
in MSVC.
extern "vectorcall" {
fn add_f64s(x: f64, y: f64) -> f64;
}
fn main() {
println!("{}", add_f64s(2.0, 4.0));
}
abi_x86_interrupt
The tracking issue for this feature is: #40180
adt_const_params
The tracking issue for this feature is: #95174
Allows for using more complex types for const parameters, such as structs or enums.
#![allow(unused)] #![feature(adt_const_params)] #![allow(incomplete_features)] fn main() { use std::marker::ConstParamTy; #[derive(ConstParamTy, PartialEq, Eq)] enum Foo { A, B, C, } #[derive(ConstParamTy, PartialEq, Eq)] struct Bar { flag: bool, } fn is_foo_a_and_bar_true<const F: Foo, const B: Bar>() -> bool { match (F, B.flag) { (Foo::A, true) => true, _ => false, } } }
alloc_error_handler
The tracking issue for this feature is: #51540
allocator_internals
This feature does not have a tracking issue, it is an unstable implementation
detail of the global_allocator
feature not intended for use outside the
compiler.
allow_internal_unsafe
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
allow_internal_unstable
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
anonymous_lifetime_in_impl_trait
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
arbitrary_self_types
The tracking issue for this feature is: #44874
arbitrary_self_types_pointers
The tracking issue for this feature is: #44874
arm_target_feature
The tracking issue for this feature is: #44839
asm_experimental_arch
The tracking issue for this feature is: #93335
This feature tracks asm!
and global_asm!
support for the following architectures:
- NVPTX
- PowerPC
- Hexagon
- MIPS32r2 and MIPS64r2
- wasm32
- BPF
- SPIR-V
- AVR
- MSP430
- M68k
- CSKY
- s390x
- Arm64EC
Register classes
Architecture | Register class | Registers | LLVM constraint code |
---|---|---|---|
MIPS | reg | $[2-25] | r |
MIPS | freg | $f[0-31] | f |
NVPTX | reg16 | None* | h |
NVPTX | reg32 | None* | r |
NVPTX | reg64 | None* | l |
Hexagon | reg | r[0-28] | r |
PowerPC | reg | r[0-31] | r |
PowerPC | reg_nonzero | r[1-31] | b |
PowerPC | freg | f[0-31] | f |
PowerPC | cr | cr[0-7] , cr | Only clobbers |
PowerPC | xer | xer | Only clobbers |
wasm32 | local | None* | r |
BPF | reg | r[0-10] | r |
BPF | wreg | w[0-10] | w |
AVR | reg | r[2-25] , XH , XL , ZH , ZL | r |
AVR | reg_upper | r[16-25] , XH , XL , ZH , ZL | d |
AVR | reg_pair | r3r2 .. r25r24 , X , Z | r |
AVR | reg_iw | r25r24 , X , Z | w |
AVR | reg_ptr | X , Z | e |
MSP430 | reg | r[0-15] | r |
M68k | reg | d[0-7] , a[0-7] | r |
M68k | reg_data | d[0-7] | d |
M68k | reg_addr | a[0-3] | a |
CSKY | reg | r[0-31] | r |
CSKY | freg | f[0-31] | f |
s390x | reg | r[0-10] , r[12-14] | r |
s390x | freg | f[0-15] | f |
Arm64EC | reg | x[0-12] , x[15-22] , x[25-27] , x30 | r |
Arm64EC | vreg | v[0-15] | w |
Arm64EC | vreg_low16 | v[0-15] | x |
Notes:
NVPTX doesn't have a fixed register set, so named registers are not supported.
WebAssembly doesn't have registers, so named registers are not supported.
Register class supported types
Architecture | Register class | Target feature | Allowed types |
---|---|---|---|
MIPS32 | reg | None | i8 , i16 , i32 , f32 |
MIPS32 | freg | None | f32 , f64 |
MIPS64 | reg | None | i8 , i16 , i32 , i64 , f32 , f64 |
MIPS64 | freg | None | f32 , f64 |
NVPTX | reg16 | None | i8 , i16 |
NVPTX | reg32 | None | i8 , i16 , i32 , f32 |
NVPTX | reg64 | None | i8 , i16 , i32 , f32 , i64 , f64 |
Hexagon | reg | None | i8 , i16 , i32 , f32 |
PowerPC | reg | None | i8 , i16 , i32 |
PowerPC | reg_nonzero | None | i8 , i16 , i32 |
PowerPC | freg | None | f32 , f64 |
PowerPC | cr | N/A | Only clobbers |
PowerPC | xer | N/A | Only clobbers |
wasm32 | local | None | i8 i16 i32 i64 f32 f64 |
BPF | reg | None | i8 i16 i32 i64 |
BPF | wreg | alu32 | i8 i16 i32 |
AVR | reg , reg_upper | None | i8 |
AVR | reg_pair , reg_iw , reg_ptr | None | i16 |
MSP430 | reg | None | i8 , i16 |
M68k | reg , reg_addr | None | i16 , i32 |
M68k | reg_data | None | i8 , i16 , i32 |
CSKY | reg | None | i8 , i16 , i32 |
CSKY | freg | None | f32 , |
s390x | reg , reg_addr | None | i8 , i16 , i32 , i64 |
s390x | freg | None | f32 , f64 |
Arm64EC | reg | None | i8 , i16 , i32 , f32 , i64 , f64 |
Arm64EC | vreg | None | i8 , i16 , i32 , f32 , i64 , f64 , i8x8 , i16x4 , i32x2 , i64x1 , f32x2 , f64x1 , i8x16 , i16x8 , i32x4 , i64x2 , f32x4 , f64x2 |
Register aliases
Architecture | Base register | Aliases |
---|---|---|
Hexagon | r29 | sp |
Hexagon | r30 | fr |
Hexagon | r31 | lr |
BPF | r[0-10] | w[0-10] |
AVR | XH | r27 |
AVR | XL | r26 |
AVR | ZH | r31 |
AVR | ZL | r30 |
MSP430 | r0 | pc |
MSP430 | r1 | sp |
MSP430 | r2 | sr |
MSP430 | r3 | cg |
MSP430 | r4 | fp |
M68k | a5 | bp |
M68k | a6 | fp |
M68k | a7 | sp , usp , ssp , isp |
CSKY | r[0-3] | a[0-3] |
CSKY | r[4-11] | l[0-7] |
CSKY | r[12-13] | t[0-1] |
CSKY | r14 | sp |
CSKY | r15 | lr |
CSKY | r[16-17] | l[8-9] |
CSKY | r[18-25] | t[2-9] |
CSKY | r28 | rgb |
CSKY | r29 | rtb |
CSKY | r30 | svbr |
CSKY | r31 | tls |
Arm64EC | x[0-30] | w[0-30] |
Arm64EC | x29 | fp |
Arm64EC | x30 | lr |
Arm64EC | sp | wsp |
Arm64EC | xzr | wzr |
Arm64EC | v[0-15] | b[0-15] , h[0-15] , s[0-15] , d[0-15] , q[0-15] |
Notes:
- TI does not mandate a frame pointer for MSP430, but toolchains are allowed to use one; LLVM uses
r4
.
Unsupported registers
Architecture | Unsupported register | Reason |
---|---|---|
All | sp , r15 (s390x) | The stack pointer must be restored to its original value at the end of an asm code block. |
All | fr (Hexagon), $fp (MIPS), Y (AVR), r4 (MSP430), a6 (M68k), r11 (s390x), x29 (Arm64EC) | The frame pointer cannot be used as an input or output. |
All | r19 (Hexagon), x19 (Arm64EC) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. |
MIPS | $0 or $zero | This is a constant zero register which can't be modified. |
MIPS | $1 or $at | Reserved for assembler. |
MIPS | $26 /$k0 , $27 /$k1 | OS-reserved registers. |
MIPS | $28 /$gp | Global pointer cannot be used as inputs or outputs. |
MIPS | $ra | Return address cannot be used as inputs or outputs. |
Hexagon | lr | This is the link register which cannot be used as an input or output. |
AVR | r0 , r1 , r1r0 | Due to an issue in LLVM, the r0 and r1 registers cannot be used as inputs or outputs. If modified, they must be restored to their original values before the end of the block. |
MSP430 | r0 , r2 , r3 | These are the program counter, status register, and constant generator respectively. Neither the status register nor constant generator can be written to. |
M68k | a4 , a5 | Used internally by LLVM for the base pointer and global base pointer. |
CSKY | r7 , r28 | Used internally by LLVM for the base pointer and global base pointer. |
CSKY | r8 | Used internally by LLVM for the frame pointer. |
CSKY | r14 | Used internally by LLVM for the stack pointer. |
CSKY | r15 | This is the link register. |
CSKY | r[26-30] | Reserved by its ABI. |
CSKY | r31 | This is the TLS register. |
Arm64EC | xzr | This is a constant zero register which can't be modified. |
Arm64EC | x18 | This is an OS-reserved register. |
Arm64EC | x13 , x14 , x23 , x24 , x28 , v[16-31] | These are AArch64 registers that are not supported for Arm64EC. |
Template modifiers
Architecture | Register class | Modifier | Example output | LLVM modifier |
---|---|---|---|---|
MIPS | reg | None | $2 | None |
MIPS | freg | None | $f0 | None |
NVPTX | reg16 | None | rs0 | None |
NVPTX | reg32 | None | r0 | None |
NVPTX | reg64 | None | rd0 | None |
Hexagon | reg | None | r0 | None |
PowerPC | reg | None | 0 | None |
PowerPC | reg_nonzero | None | 3 | None |
PowerPC | freg | None | 0 | None |
s390x | reg | None | %r0 | None |
s390x | reg_addr | None | %r1 | None |
s390x | freg | None | %f0 | None |
CSKY | reg | None | r0 | None |
CSKY | freg | None | f0 | None |
Arm64EC | reg | None | x0 | x |
Arm64EC | reg | w | w0 | w |
Arm64EC | reg | x | x0 | x |
Arm64EC | vreg | None | v0 | None |
Arm64EC | vreg | v | v0 | None |
Arm64EC | vreg | b | b0 | b |
Arm64EC | vreg | h | h0 | h |
Arm64EC | vreg | s | s0 | s |
Arm64EC | vreg | d | d0 | d |
Arm64EC | vreg | q | q0 | q |
Flags covered by preserves_flags
These flags registers must be restored upon exiting the asm block if the preserves_flags
option is set:
- AVR
- The status register
SREG
.
- The status register
- MSP430
- The status register
r2
.
- The status register
- M68k
- The condition code register
ccr
.
- The condition code register
- s390x
- The condition code register
cc
.
- The condition code register
- Arm64EC
- Condition flags (
NZCV
register). - Floating-point status (
FPSR
register).
- Condition flags (
asm_goto
The tracking issue for this feature is: #119364
This feature adds a label <block>
operand type to asm!
.
Example:
unsafe {
asm!(
"jmp {}",
label {
println!("Jumped from asm!");
}
);
}
The block must have unit type or diverge.
When label <block>
is used together with noreturn
option, it means that the
assembly will not fallthrough. It's allowed to jump to a label within the
assembly. In this case, the entire asm!
expression will have an unit type as
opposed to diverging, if not all label blocks diverge. The asm!
expression
still diverges if noreturn
option is used and all label blocks diverge.
asm_unwind
The tracking issue for this feature is: #93334
This feature adds a may_unwind
option to asm!
which allows an asm
block to unwind stack and be part of the stack unwinding process. This option is only supported by the LLVM backend right now.
associated_const_equality
The tracking issue for this feature is: #92827
associated_type_defaults
The tracking issue for this feature is: #29661
async_closure
The tracking issue for this feature is: #62290
async_fn_track_caller
The tracking issue for this feature is: #110011
async_for_loop
The tracking issue for this feature is: #118898
auto_traits
The tracking issue for this feature is #13231
The auto_traits
feature gate allows you to define auto traits.
Auto traits, like Send
or Sync
in the standard library, are marker traits
that are automatically implemented for every type, unless the type, or a type it contains,
has explicitly opted out via a negative impl. (Negative impls are separately controlled
by the negative_impls
feature.)
impl !Trait for Type {}
Example:
#![feature(negative_impls)] #![feature(auto_traits)] auto trait Valid {} struct True; struct False; impl !Valid for False {} struct MaybeValid<T>(T); fn must_be_valid<T: Valid>(_t: T) { } fn main() { // works must_be_valid( MaybeValid(True) ); // compiler error - trait bound not satisfied // must_be_valid( MaybeValid(False) ); }
Automatic trait implementations
When a type is declared as an auto trait
, we will automatically
create impls for every struct/enum/union, unless an explicit impl is
provided. These automatic impls contain a where clause for each field
of the form T: AutoTrait
, where T
is the type of the field and
AutoTrait
is the auto trait in question. As an example, consider the
struct List
and the auto trait Send
:
#![allow(unused)] fn main() { struct List<T> { data: T, next: Option<Box<List<T>>>, } }
Presuming that there is no explicit impl of Send
for List
, the
compiler will supply an automatic impl of the form:
#![allow(unused)] fn main() { struct List<T> { data: T, next: Option<Box<List<T>>>, } unsafe impl<T> Send for List<T> where T: Send, // from the field `data` Option<Box<List<T>>>: Send, // from the field `next` { } }
Explicit impls may be either positive or negative. They take the form:
impl<...> AutoTrait for StructName<..> { }
impl<...> !AutoTrait for StructName<..> { }
Coinduction: Auto traits permit cyclic matching
Unlike ordinary trait matching, auto traits are coinductive. This
means, in short, that cycles which occur in trait matching are
considered ok. As an example, consider the recursive struct List
introduced in the previous section. In attempting to determine whether
List: Send
, we would wind up in a cycle: to apply the impl, we must
show that Option<Box<List>>: Send
, which will in turn require
Box<List>: Send
and then finally List: Send
again. Under ordinary
trait matching, this cycle would be an error, but for an auto trait it
is considered a successful match.
Items
Auto traits cannot have any trait items, such as methods or associated types. This ensures that we can generate default implementations.
Supertraits
Auto traits cannot have supertraits. This is for soundness reasons, as the interaction of coinduction with implied bounds is difficult to reconcile.
avx512_target_feature
The tracking issue for this feature is: #44839
box_patterns
The tracking issue for this feature is: #29641
Box patterns let you match on Box<T>
s:
#![feature(box_patterns)] fn main() { let b = Some(Box::new(5)); match b { Some(box n) if n < 0 => { println!("Box contains negative number {n}"); }, Some(box n) if n >= 0 => { println!("Box contains non-negative number {n}"); }, None => { println!("No box"); }, _ => unreachable!() } }
bpf_target_feature
The tracking issue for this feature is: #44839
builtin_syntax
The tracking issue for this feature is: #110680
c_variadic
The tracking issue for this feature is: #44930
The c_variadic
language feature enables C-variadic functions to be
defined in Rust. They may be called both from within Rust and via FFI.
Examples
#![allow(unused)] #![feature(c_variadic)] fn main() { pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize { let mut sum = 0; for _ in 0..n { sum += args.arg::<usize>(); } sum } }
cfg_overflow_checks
The tracking issue for this feature is: #111466
cfg_relocation_model
The tracking issue for this feature is: #114929
cfg_sanitize
The tracking issue for this feature is: #39699
The cfg_sanitize
feature makes it possible to execute different code
depending on whether a particular sanitizer is enabled or not.
Examples
#![allow(unused)] #![feature(cfg_sanitize)] fn main() { #[cfg(sanitize = "thread")] fn a() { // ... } #[cfg(not(sanitize = "thread"))] fn a() { // ... } fn b() { if cfg!(sanitize = "leak") { // ... } else { // ... } } }
cfg_sanitizer_cfi
The tracking issue for this feature is: #89653
cfg_target_compact
The tracking issue for this feature is: #96901
cfg_target_has_atomic
The tracking issue for this feature is: #94039
cfg_target_has_atomic_equal_alignment
The tracking issue for this feature is: #93822
cfg_target_thread_local
The tracking issue for this feature is: #29594
cfg_ub_checks
The tracking issue for this feature is: #123499
cfg_version
The tracking issue for this feature is: #64796
The cfg_version
feature makes it possible to execute different code
depending on the compiler version. It will return true if the compiler
version is greater than or equal to the specified version.
Examples
#![allow(unused)] #![feature(cfg_version)] fn main() { #[cfg(version("1.42"))] // 1.42 and above fn a() { // ... } #[cfg(not(version("1.42")))] // 1.41 and below fn a() { // ... } fn b() { if cfg!(version("1.42")) { // ... } else { // ... } } }
cfi_encoding
The tracking issue for this feature is: #89653
The cfi_encoding
feature allows the user to define a CFI encoding for a type.
It allows the user to use a different names for types that otherwise would be
required to have the same name as used in externally defined C functions.
Examples
#![allow(unused)] #![feature(cfi_encoding, extern_types)] fn main() { #[cfi_encoding = "3Foo"] pub struct Type1(i32); extern { #[cfi_encoding = "3Bar"] type Type2; } }
closure_lifetime_binder
The tracking issue for this feature is: #97362
closure_track_caller
The tracking issue for this feature is: #87417
Allows using the #[track_caller]
attribute on closures and coroutines.
Calls made to the closure or coroutine will have caller information
available through std::panic::Location::caller()
, just like using
#[track_caller]
on a function.
cmse_nonsecure_entry
The tracking issue for this feature is: #75835
The TrustZone-M
feature is available
for targets with the Armv8-M architecture profile (thumbv8m
in their target
name).
LLVM, the Rust compiler and the linker are providing
support for the
TrustZone-M feature.
One of the things provided, with this unstable feature, is the
cmse_nonsecure_entry
attribute. This attribute marks a Secure function as an
entry function (see section
5.4 for details).
With this attribute, the compiler will do the following:
- add a special symbol on the function which is the
__acle_se_
prefix and the standard function name - constrain the number of parameters to avoid using the Non-Secure stack
- before returning from the function, clear registers that might contain Secure information
- use the
BXNS
instruction to return
Because the stack can not be used to pass parameters, there will be compilation errors if:
- the total size of all parameters is too big (for example more than four 32 bits integers)
- the entry function is not using a C ABI
The special symbol __acle_se_
will be used by the linker to generate a secure
gateway veneer.
#![feature(cmse_nonsecure_entry)]
#[no_mangle]
#[cmse_nonsecure_entry]
pub extern "C" fn entry_function(input: u32) -> u32 {
input + 6
}
$ rustc --emit obj --crate-type lib --target thumbv8m.main-none-eabi function.rs
$ arm-none-eabi-objdump -D function.o
00000000 <entry_function>:
0: b580 push {r7, lr}
2: 466f mov r7, sp
4: b082 sub sp, #8
6: 9001 str r0, [sp, #4]
8: 1d81 adds r1, r0, #6
a: 460a mov r2, r1
c: 4281 cmp r1, r0
e: 9200 str r2, [sp, #0]
10: d30b bcc.n 2a <entry_function+0x2a>
12: e7ff b.n 14 <entry_function+0x14>
14: 9800 ldr r0, [sp, #0]
16: b002 add sp, #8
18: e8bd 4080 ldmia.w sp!, {r7, lr}
1c: 4671 mov r1, lr
1e: 4672 mov r2, lr
20: 4673 mov r3, lr
22: 46f4 mov ip, lr
24: f38e 8800 msr CPSR_f, lr
28: 4774 bxns lr
2a: f240 0000 movw r0, #0
2e: f2c0 0000 movt r0, #0
32: f240 0200 movw r2, #0
36: f2c0 0200 movt r2, #0
3a: 211c movs r1, #28
3c: f7ff fffe bl 0 <_ZN4core9panicking5panic17h5c028258ca2fb3f5E>
40: defe udf #254 ; 0xfe
compiler_builtins
This feature is internal to the Rust compiler and is not intended for general use.
const_arg_path
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_async_blocks
The tracking issue for this feature is: #85368
const_closures
The tracking issue for this feature is: #106003
const_extern_fn
The tracking issue for this feature is: #64926
const_for
The tracking issue for this feature is: #87575
const_mut_refs
The tracking issue for this feature is: #57349
const_precise_live_drops
The tracking issue for this feature is: #73255
const_refs_to_cell
The tracking issue for this feature is: #80384
const_refs_to_static
The tracking issue for this feature is: #119618
const_trait_impl
The tracking issue for this feature is: #67792
const_try
The tracking issue for this feature is: #74935
coroutine_clone
The tracking issue for this feature is: #95360
coroutines
The tracking issue for this feature is: #43122
The coroutines
feature gate in Rust allows you to define coroutine or
coroutine literals. A coroutine is a "resumable function" that syntactically
resembles a closure but compiles to much different semantics in the compiler
itself. The primary feature of a coroutine is that it can be suspended during
execution to be resumed at a later date. Coroutines use the yield
keyword to
"return", and then the caller can resume
a coroutine to resume execution just
after the yield
keyword.
Coroutines are an extra-unstable feature in the compiler right now. Added in RFC 2033 they're mostly intended right now as a information/constraint gathering phase. The intent is that experimentation can happen on the nightly compiler before actual stabilization. A further RFC will be required to stabilize coroutines and will likely contain at least a few small tweaks to the overall design.
A syntactical example of a coroutine is:
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; fn main() { let mut coroutine = #[coroutine] || { yield 1; return "foo" }; match Pin::new(&mut coroutine).resume(()) { CoroutineState::Yielded(1) => {} _ => panic!("unexpected value from resume"), } match Pin::new(&mut coroutine).resume(()) { CoroutineState::Complete("foo") => {} _ => panic!("unexpected value from resume"), } }
Coroutines are closure-like literals which are annotated with #[coroutine]
and can contain a yield
statement. The
yield
statement takes an optional expression of a value to yield out of the
coroutine. All coroutine literals implement the Coroutine
trait in the
std::ops
module. The Coroutine
trait has one main method, resume
, which
resumes execution of the coroutine at the previous suspension point.
An example of the control flow of coroutines is that the following example prints all numbers in order:
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] use std::ops::Coroutine; use std::pin::Pin; fn main() { let mut coroutine = #[coroutine] || { println!("2"); yield; println!("4"); }; println!("1"); Pin::new(&mut coroutine).resume(()); println!("3"); Pin::new(&mut coroutine).resume(()); println!("5"); }
At this time the main use case of coroutines is an implementation
primitive for async
/await
and gen
syntax, but coroutines
will likely be extended to other primitives in the future.
Feedback on the design and usage is always appreciated!
The Coroutine
trait
The Coroutine
trait in std::ops
currently looks like:
#![allow(unused)] fn main() { #![feature(arbitrary_self_types, coroutine_trait)] use std::ops::CoroutineState; use std::pin::Pin; pub trait Coroutine<R = ()> { type Yield; type Return; fn resume(self: Pin<&mut Self>, resume: R) -> CoroutineState<Self::Yield, Self::Return>; } }
The Coroutine::Yield
type is the type of values that can be yielded with the
yield
statement. The Coroutine::Return
type is the returned type of the
coroutine. This is typically the last expression in a coroutine's definition or
any value passed to return
in a coroutine. The resume
function is the entry
point for executing the Coroutine
itself.
The return value of resume
, CoroutineState
, looks like:
#![allow(unused)] fn main() { pub enum CoroutineState<Y, R> { Yielded(Y), Complete(R), } }
The Yielded
variant indicates that the coroutine can later be resumed. This
corresponds to a yield
point in a coroutine. The Complete
variant indicates
that the coroutine is complete and cannot be resumed again. Calling resume
after a coroutine has returned Complete
will likely result in a panic of the
program.
Closure-like semantics
The closure-like syntax for coroutines alludes to the fact that they also have closure-like semantics. Namely:
-
When created, a coroutine executes no code. A closure literal does not actually execute any of the closure's code on construction, and similarly a coroutine literal does not execute any code inside the coroutine when constructed.
-
Coroutines can capture outer variables by reference or by move, and this can be tweaked with the
move
keyword at the beginning of the closure. Like closures all coroutines will have an implicit environment which is inferred by the compiler. Outer variables can be moved into a coroutine for use as the coroutine progresses. -
Coroutine literals produce a value with a unique type which implements the
std::ops::Coroutine
trait. This allows actual execution of the coroutine through theCoroutine::resume
method as well as also naming it in return types and such. -
Traits like
Send
andSync
are automatically implemented for aCoroutine
depending on the captured variables of the environment. Unlike closures, coroutines also depend on variables live across suspension points. This means that although the ambient environment may beSend
orSync
, the coroutine itself may not be due to internal variables live acrossyield
points being not-Send
or not-Sync
. Note that coroutines do not implement traits likeCopy
orClone
automatically. -
Whenever a coroutine is dropped it will drop all captured environment variables.
Coroutines as state machines
In the compiler, coroutines are currently compiled as state machines. Each
yield
expression will correspond to a different state that stores all live
variables over that suspension point. Resumption of a coroutine will dispatch on
the current state and then execute internally until a yield
is reached, at
which point all state is saved off in the coroutine and a value is returned.
Let's take a look at an example to see what's going on here:
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] use std::ops::Coroutine; use std::pin::Pin; fn main() { let ret = "foo"; let mut coroutine = #[coroutine] move || { yield 1; return ret }; Pin::new(&mut coroutine).resume(()); Pin::new(&mut coroutine).resume(()); }
This coroutine literal will compile down to something similar to:
#![feature(arbitrary_self_types, coroutine_trait)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; fn main() { let ret = "foo"; let mut coroutine = { enum __Coroutine { Start(&'static str), Yield1(&'static str), Done, } impl Coroutine for __Coroutine { type Yield = i32; type Return = &'static str; fn resume(mut self: Pin<&mut Self>, resume: ()) -> CoroutineState<i32, &'static str> { use std::mem; match mem::replace(&mut *self, __Coroutine::Done) { __Coroutine::Start(s) => { *self = __Coroutine::Yield1(s); CoroutineState::Yielded(1) } __Coroutine::Yield1(s) => { *self = __Coroutine::Done; CoroutineState::Complete(s) } __Coroutine::Done => { panic!("coroutine resumed after completion") } } } } __Coroutine::Start(ret) }; Pin::new(&mut coroutine).resume(()); Pin::new(&mut coroutine).resume(()); }
Notably here we can see that the compiler is generating a fresh type,
__Coroutine
in this case. This type has a number of states (represented here
as an enum
) corresponding to each of the conceptual states of the coroutine.
At the beginning we're closing over our outer variable foo
and then that
variable is also live over the yield
point, so it's stored in both states.
When the coroutine starts it'll immediately yield 1, but it saves off its state
just before it does so indicating that it has reached the yield point. Upon
resuming again we'll execute the return ret
which returns the Complete
state.
Here we can also note that the Done
state, if resumed, panics immediately as
it's invalid to resume a completed coroutine. It's also worth noting that this
is just a rough desugaring, not a normative specification for what the compiler
does.
coverage_attribute
The tracking issue for this feature is: #84605
The coverage
attribute can be used to selectively disable coverage
instrumentation in an annotated function. This might be useful to:
- Avoid instrumentation overhead in a performance critical function
- Avoid generating coverage for a function that is not meant to be executed, but still target 100% coverage for the rest of the program.
Example
#![allow(unused)] #![feature(coverage_attribute)] fn main() { // `foo()` will get coverage instrumentation (by default) fn foo() { // ... } #[coverage(off)] fn bar() { // ... } }
csky_target_feature
The tracking issue for this feature is: #44839
custom_inner_attributes
The tracking issue for this feature is: #54726
custom_mir
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
custom_test_frameworks
The tracking issue for this feature is: #50297
The custom_test_frameworks
feature allows the use of #[test_case]
and #![test_runner]
.
Any function, const, or static can be annotated with #[test_case]
causing it to be aggregated (like #[test]
)
and be passed to the test runner determined by the #![test_runner]
crate attribute.
#![allow(unused)] #![feature(custom_test_frameworks)] #![test_runner(my_runner)] fn main() { fn my_runner(tests: &[&i32]) { for t in tests { if **t == 0 { println!("PASSED"); } else { println!("FAILED"); } } } #[test_case] const WILL_PASS: i32 = 0; #[test_case] const WILL_FAIL: i32 = 4; }
decl_macro
The tracking issue for this feature is: #39412
deprecated_safe
The tracking issue for this feature is: #94978
deprecated_suggestion
The tracking issue for this feature is: #94785
deref_patterns
The tracking issue for this feature is: #87121
derive_smart_pointer
The tracking issue for this feature is: #123430
do_not_recommend
The tracking issue for this feature is: #51992
doc_auto_cfg
The tracking issue for this feature is: #43781
doc_cfg
The tracking issue for this feature is: #43781
The doc_cfg
feature allows an API be documented as only available in some specific platforms.
This attribute has two effects:
-
In the annotated item's documentation, there will be a message saying "Available on (platform) only".
-
The item's doc-tests will only run on the specific platform.
In addition to allowing the use of the #[doc(cfg)]
attribute, this feature enables the use of a
special conditional compilation flag, #[cfg(doc)]
, set whenever building documentation on your
crate.
This feature was introduced as part of PR #43348 to allow the platform-specific parts of the standard library be documented.
#![allow(unused)] #![feature(doc_cfg)] fn main() { #[cfg(any(windows, doc))] #[doc(cfg(windows))] /// The application's icon in the notification area (a.k.a. system tray). /// /// # Examples /// /// ```no_run /// extern crate my_awesome_ui_library; /// use my_awesome_ui_library::current_app; /// use my_awesome_ui_library::windows::notification; /// /// let icon = current_app().get::<notification::Icon>(); /// icon.show(); /// icon.show_message("Hello"); /// ``` pub struct Icon { // ... } }
doc_cfg_hide
The tracking issue for this feature is: #43781
doc_masked
The tracking issue for this feature is: #44027
The doc_masked
feature allows a crate to exclude types from a given crate from appearing in lists
of trait implementations. The specifics of the feature are as follows:
-
When rustdoc encounters an
extern crate
statement annotated with a#[doc(masked)]
attribute, it marks the crate as being masked. -
When listing traits a given type implements, rustdoc ensures that traits from masked crates are not emitted into the documentation.
-
When listing types that implement a given trait, rustdoc ensures that types from masked crates are not emitted into the documentation.
This feature was introduced in PR #44026 to ensure that compiler-internal and implementation-specific types and traits were not included in the standard library's documentation. Such types would introduce broken links into the documentation.
doc_notable_trait
The tracking issue for this feature is: #45040
The doc_notable_trait
feature allows the use of the #[doc(notable_trait)]
attribute, which will display the trait in a "Notable traits" dialog for
functions returning types that implement the trait. For example, this attribute
is applied to the Iterator
, Future
, io::Read
, and io::Write
traits in
the standard library.
You can do this on your own traits like so:
#![feature(doc_notable_trait)]
#[doc(notable_trait)]
pub trait MyTrait {}
pub struct MyStruct;
impl MyTrait for MyStruct {}
/// The docs for this function will have a button that displays a dialog about
/// `MyStruct` implementing `MyTrait`.
pub fn my_fn() -> MyStruct { MyStruct }
This feature was originally implemented in PR #45039.
See also its documentation in the rustdoc book.
dropck_eyepatch
The tracking issue for this feature is: #34761
dyn_star
The tracking issue for this feature is: #102425
effects
The tracking issue for this feature is: #102090
ermsb_target_feature
The tracking issue for this feature is: #44839
exhaustive_patterns
The tracking issue for this feature is: #51085
explicit_tail_calls
The tracking issue for this feature is: #112788
expr_fragment_specifier_2024
The tracking issue for this feature is: #123742
extended_varargs_abi_support
The tracking issue for this feature is: #100189
This feature adds the possibility of using sysv64
, win64
or efiapi
calling
conventions on functions with varargs.
extern_types
The tracking issue for this feature is: #43467
f128
The tracking issue for this feature is: #116909
Enable the f128
type for IEEE 128-bit floating numbers (quad precision).
f16
The tracking issue for this feature is: #116909
Enable the f16
type for IEEE 16-bit floating numbers (half precision).
ffi_const
The tracking issue for this feature is: #58328
The #[ffi_const]
attribute applies clang's const
attribute to foreign
functions declarations.
That is, #[ffi_const]
functions shall have no effects except for its return
value, which can only depend on the values of the function parameters, and is
not affected by changes to the observable state of the program.
Applying the #[ffi_const]
attribute to a function that violates these
requirements is undefined behaviour.
This attribute enables Rust to perform common optimizations, like sub-expression
elimination, and it can avoid emitting some calls in repeated invocations of the
function with the same argument values regardless of other operations being
performed in between these functions calls (as opposed to #[ffi_pure]
functions).
Pitfalls
A #[ffi_const]
function can only read global memory that would not affect
its return value for the whole execution of the program (e.g. immutable global
memory). #[ffi_const]
functions are referentially-transparent and therefore
more strict than #[ffi_pure]
functions.
A common pitfall involves applying the #[ffi_const]
attribute to a
function that reads memory through pointer arguments which do not necessarily
point to immutable global memory.
A #[ffi_const]
function that returns unit has no effect on the abstract
machine's state, and a #[ffi_const]
function cannot be #[ffi_pure]
.
A #[ffi_const]
function must not diverge, neither via a side effect (e.g. a
call to abort
) nor by infinite loops.
When translating C headers to Rust FFI, it is worth verifying for which targets
the const
attribute is enabled in those headers, and using the appropriate
cfg
macros in the Rust side to match those definitions. While the semantics of
const
are implemented identically by many C and C++ compilers, e.g., clang,
GCC, ARM C/C++ compiler, IBM ILE C/C++, etc. they are not necessarily
implemented in this way on all of them. It is therefore also worth verifying
that the semantics of the C toolchain used to compile the binary being linked
against are compatible with those of the #[ffi_const]
.
ffi_pure
The tracking issue for this feature is: #58329
The #[ffi_pure]
attribute applies clang's pure
attribute to foreign
functions declarations.
That is, #[ffi_pure]
functions shall have no effects except for its return
value, which shall not change across two consecutive function calls with
the same parameters.
Applying the #[ffi_pure]
attribute to a function that violates these
requirements is undefined behavior.
This attribute enables Rust to perform common optimizations, like sub-expression
elimination and loop optimizations. Some common examples of pure functions are
strlen
or memcmp
.
These optimizations are only applicable when the compiler can prove that no
program state observable by the #[ffi_pure]
function has changed between calls
of the function, which could alter the result. See also the #[ffi_const]
attribute, which provides stronger guarantees regarding the allowable behavior
of a function, enabling further optimization.
Pitfalls
A #[ffi_pure]
function can read global memory through the function
parameters (e.g. pointers), globals, etc. #[ffi_pure]
functions are not
referentially-transparent, and are therefore more relaxed than #[ffi_const]
functions.
However, accessing global memory through volatile or atomic reads can violate the requirement that two consecutive function calls shall return the same value.
A pure
function that returns unit has no effect on the abstract machine's
state.
A #[ffi_pure]
function must not diverge, neither via a side effect (e.g. a
call to abort
) nor by infinite loops.
When translating C headers to Rust FFI, it is worth verifying for which targets
the pure
attribute is enabled in those headers, and using the appropriate
cfg
macros in the Rust side to match those definitions. While the semantics of
pure
are implemented identically by many C and C++ compilers, e.g., clang,
GCC, ARM C/C++ compiler, IBM ILE C/C++, etc. they are not necessarily
implemented in this way on all of them. It is therefore also worth verifying
that the semantics of the C toolchain used to compile the binary being linked
against are compatible with those of the #[ffi_pure]
.
fmt_debug
The tracking issue for this feature is: #129709
fn_align
The tracking issue for this feature is: #82232
fn_delegation
The tracking issue for this feature is: #118212
freeze_impls
The tracking issue for this feature is: #121675
fundamental
The tracking issue for this feature is: #29635
gen_blocks
The tracking issue for this feature is: #117078
generic_arg_infer
The tracking issue for this feature is: #85077
generic_assert
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
generic_associated_types_extended
The tracking issue for this feature is: #95451
generic_const_exprs
The tracking issue for this feature is: #76560
generic_const_items
The tracking issue for this feature is: #113521
global_registration
The tracking issue for this feature is: #125119
half_open_range_patterns_in_slices
The tracking issue for this feature is: #67264
It is a future part of the exclusive_range_pattern
feature,
tracked at #37854.
This feature allow using top-level half-open range patterns in slices.
#![feature(half_open_range_patterns_in_slices)] fn main() { let xs = [13, 1, 5, 2, 3, 1, 21, 8]; let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs else { return; }; }
Note that this feature is not required if the patterns are wrapped between parenthesis.
fn main() { let xs = [13, 1]; let [(a @ 3..), c] = xs else { return; }; }
hexagon_target_feature
The tracking issue for this feature is: #44839
if_let_guard
The tracking issue for this feature is: #51114
impl_trait_in_assoc_type
The tracking issue for this feature is: #63063
impl_trait_in_fn_trait_return
The tracking issue for this feature is: #99697
inherent_associated_types
The tracking issue for this feature is: #8995
inline_const_pat
The tracking issue for this feature is: #76001
This feature allows you to use inline constant expressions in pattern position:
#![allow(unused)] #![feature(inline_const_pat)] fn main() { const fn one() -> i32 { 1 } let some_int = 3; match some_int { const { 1 + 2 } => println!("Matched 1 + 2"), const { one() } => println!("Matched const fn returning 1"), _ => println!("Didn't match anything :("), } }
intra-doc-pointers
The tracking issue for this feature is: #80896
Rustdoc does not currently allow disambiguating between *const
and *mut
, and
raw pointers in intra-doc links are unstable until it does.
#![allow(unused)] #![feature(intra_doc_pointers)] fn main() { //! [pointer::add] }
intrinsics
The tracking issue for this feature is: None.
Intrinsics are rarely intended to be stable directly, but are usually exported in some sort of stable manner. Prefer using the stable interfaces to the intrinsic directly when you can.
Intrinsics with fallback logic
Many intrinsics can be written in pure rust, albeit inefficiently or without supporting
some features that only exist on some backends. Backends can simply not implement those
intrinsics without causing any code miscompilations or failures to compile.
All intrinsic fallback bodies are automatically made cross-crate inlineable (like #[inline]
)
by the codegen backend, but not the MIR inliner.
#![allow(unused)] #![feature(rustc_attrs)] #![allow(internal_features)] fn main() { #[rustc_intrinsic] const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} }
Since these are just regular functions, it is perfectly ok to create the intrinsic twice:
#![allow(unused)] #![feature(rustc_attrs)] #![allow(internal_features)] fn main() { #[rustc_intrinsic] const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} mod foo { #[rustc_intrinsic] const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) { panic!("noisy const dealloc") } } }
The behaviour on backends that override the intrinsic is exactly the same. On other backends, the intrinsic behaviour depends on which implementation is called, just like with any regular function.
Intrinsics lowered to MIR instructions
Various intrinsics have native MIR operations that they correspond to. Instead of requiring
backends to implement both the intrinsic and the MIR operation, the lower_intrinsics
pass
will convert the calls to the MIR operation. Backends do not need to know about these intrinsics
at all. These intrinsics only make sense without a body, and can either be declared as a "rust-intrinsic"
or as a #[rustc_intrinsic]
. The body is never used, as calls to the intrinsic do not exist
anymore after MIR analyses.
Intrinsics without fallback logic
These must be implemented by all backends.
#[rustc_intrinsic]
declarations
These are written like intrinsics with fallback bodies, but the body is irrelevant.
Use loop {}
for the body or call the intrinsic recursively and add
#[rustc_intrinsic_must_be_overridden]
to the function to ensure that backends don't
invoke the body.
Legacy extern ABI based intrinsics
These are imported as if they were FFI functions, with the special
rust-intrinsic
ABI. For example, if one was in a freestanding
context, but wished to be able to transmute
between types, and
perform efficient pointer arithmetic, one would import those functions
via a declaration like
#![feature(intrinsics)] #![allow(internal_features)] fn main() {} extern "rust-intrinsic" { fn transmute<T, U>(x: T) -> U; fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; }
As with any other FFI functions, these are by default always unsafe
to call.
You can add #[rustc_safe_intrinsic]
to the intrinsic to make it safe to call.
lahfsahf_target_feature
The tracking issue for this feature is: #44839
lang_items
The tracking issue for this feature is: None.
The rustc
compiler has certain pluggable operations, that is,
functionality that isn't hard-coded into the language, but is
implemented in libraries, with a special marker to tell the compiler
it exists. The marker is the attribute #[lang = "..."]
and there are
various different values of ...
, i.e. various different 'lang
items'. Most of them can only be defined once.
Lang items are loaded lazily by the compiler; e.g. if one never uses Box
then there is no need to define a function for exchange_malloc
.
rustc
will emit an error when an item is needed but not found in the current
crate or any that it depends on.
Some features provided by lang items:
- overloadable operators via traits: the traits corresponding to the
==
,<
, dereferencing (*
) and+
(etc.) operators are all marked with lang items; those specific four areeq
,partial_ord
,deref
/deref_mut
, andadd
respectively. - panicking: the
panic
andpanic_impl
lang items, among others. - stack unwinding: the lang item
eh_personality
is a function used by the failure mechanisms of the compiler. This is often mapped to GCC's personality function (see thestd
implementation for more information), but programs which don't trigger a panic can be assured that this function is never called. Additionally, aeh_catch_typeinfo
static is needed for certain targets which implement Rust panics on top of C++ exceptions. - the traits in
core::marker
used to indicate types of various kinds; e.g. lang itemssized
,sync
andcopy
. - memory allocation, see below.
Most lang items are defined by core
, but if you're trying to build
an executable without the std
crate, you might run into the need
for lang item definitions.
Example: Implementing a Box
Box
pointers require two lang items: one for the type itself and one for
allocation. A freestanding program that uses the Box
sugar for dynamic
allocations via malloc
and free
:
#![feature(lang_items, start, core_intrinsics, rustc_private, panic_unwind, rustc_attrs)]
#![allow(internal_features)]
#![no_std]
extern crate libc;
extern crate unwind;
use core::ffi::c_void;
use core::intrinsics;
use core::panic::PanicInfo;
use core::ptr::NonNull;
pub struct Global; // the global allocator
struct Unique<T>(NonNull<T>);
#[lang = "owned_box"]
pub struct Box<T, A = Global>(Unique<T>, A);
impl<T> Box<T> {
pub fn new(x: T) -> Self {
#[rustc_box]
Box::new(x)
}
}
impl<T, A> Drop for Box<T, A> {
fn drop(&mut self) {
unsafe {
libc::free(self.0.0.as_ptr() as *mut c_void);
}
}
}
#[lang = "exchange_malloc"]
unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
let p = libc::malloc(size) as *mut u8;
// Check if `malloc` failed:
if p.is_null() {
intrinsics::abort();
}
p
}
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
let _x = Box::new(1);
0
}
#[lang = "eh_personality"]
fn rust_eh_personality() {}
#[panic_handler]
fn panic_handler(_info: &PanicInfo) -> ! { intrinsics::abort() }
Note the use of abort
: the exchange_malloc
lang item is assumed to
return a valid pointer, and so needs to do the check internally.
List of all language items
An up-to-date list of all language items can be found here in the compiler code.
large_assignments
The tracking issue for this feature is: #83518
lazy_type_alias
The tracking issue for this feature is: #112792
let_chains
The tracking issue for this feature is: #53667
lifetime_capture_rules_2024
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
link_arg_attribute
The tracking issue for this feature is: #99427
The link_arg_attribute
feature allows passing arguments into the linker
from inside of the source code. Order is preserved for link attributes as
they were defined on a single extern block:
#![allow(unused)] #![feature(link_arg_attribute)] fn main() { #[link(kind = "link-arg", name = "--start-group")] #[link(kind = "static", name = "c")] #[link(kind = "static", name = "gcc")] #[link(kind = "link-arg", name = "--end-group")] extern "C" {} }
link_cfg
This feature is internal to the Rust compiler and is not intended for general use.
link_llvm_intrinsics
The tracking issue for this feature is: #29602
linkage
The tracking issue for this feature is: #29603
loongarch_target_feature
The tracking issue for this feature is: #44839
macro_metavar_expr
The tracking issue for this feature is: #83527
macro_metavar_expr_concat
The tracking issue for this feature is: #124225
marker_trait_attr
The tracking issue for this feature is: #29864
Normally, Rust keeps you from adding trait implementations that could overlap with each other, as it would be ambiguous which to use. This feature, however, carves out an exception to that rule: a trait can opt-in to having overlapping implementations, at the cost that those implementations are not allowed to override anything (and thus the trait itself cannot have any associated items, as they're pointless when they'd need to do the same thing for every type anyway).
#![allow(unused)] #![feature(marker_trait_attr)] fn main() { #[marker] trait CheapToClone: Clone {} impl<T: Copy> CheapToClone for T {} // These could potentially overlap with the blanket implementation above, // so are only allowed because CheapToClone is a marker trait. impl<T: CheapToClone, U: CheapToClone> CheapToClone for (T, U) {} impl<T: CheapToClone> CheapToClone for std::ops::Range<T> {} fn cheap_clone<T: CheapToClone>(t: T) -> T { t.clone() } }
This is expected to replace the unstable overlapping_marker_traits
feature, which applied to all empty traits (without needing an opt-in).
min_specialization
The tracking issue for this feature is: #31844
mips_target_feature
The tracking issue for this feature is: #44839
more_maybe_bounds
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
more_qualified_paths
The more_qualified_paths
feature can be used in order to enable the
use of qualified paths in patterns.
Example
#![feature(more_qualified_paths)] fn main() { // destructure through a qualified path let <Foo as A>::Assoc { br } = StructStruct { br: 2 }; } struct StructStruct { br: i8, } struct Foo; trait A { type Assoc; } impl A for Foo { type Assoc = StructStruct; }
multiple_supertrait_upcastable
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
must_not_suspend
The tracking issue for this feature is: #83310
mut_ref
The tracking issue for this feature is: #123076
naked_functions
The tracking issue for this feature is: #90957
native_link_modifiers_as_needed
The tracking issue for this feature is: #81490
The native_link_modifiers_as_needed
feature allows you to use the as-needed
modifier.
as-needed
is only compatible with the dynamic
and framework
linking kinds. Using any other kind will result in a compiler error.
+as-needed
means that the library will be actually linked only if it satisfies some undefined symbols at the point at which it is specified on the command line, making it similar to static libraries in this regard.
This modifier translates to --as-needed
for ld-like linkers, and to -dead_strip_dylibs
/ -needed_library
/ -needed_framework
for ld64.
The modifier does nothing for linkers that don't support it (e.g. link.exe
).
The default for this modifier is unclear, some targets currently specify it as +as-needed
, some do not. We may want to try making +as-needed
a default for all targets.
needs_panic_runtime
The tracking issue for this feature is: #32837
negative_bounds
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
negative_impls
The tracking issue for this feature is #68318.
With the feature gate negative_impls
, you can write negative impls as well as positive ones:
#![allow(unused)] #![feature(negative_impls)] fn main() { trait DerefMut { } impl<T: ?Sized> !DerefMut for &T { } }
Negative impls indicate a semver guarantee that the given trait will not be implemented for the given types. Negative impls play an additional purpose for auto traits, described below.
Negative impls have the following characteristics:
- They do not have any items.
- They must obey the orphan rules as if they were a positive impl.
- They cannot "overlap" with any positive impls.
Semver interaction
It is a breaking change to remove a negative impl. Negative impls are a commitment not to implement the given trait for the named types.
Orphan and overlap rules
Negative impls must obey the same orphan rules as a positive impl. This implies you cannot add a negative impl for types defined in upstream crates and so forth.
Similarly, negative impls cannot overlap with positive impls, again using the same "overlap" check that we ordinarily use to determine if two impls overlap. (Note that positive impls typically cannot overlap with one another either, except as permitted by specialization.)
Interaction with auto traits
Declaring a negative impl impl !SomeAutoTrait for SomeType
for an
auto-trait serves two purposes:
- as with any trait, it declares that
SomeType
will never implementSomeAutoTrait
; - it disables the automatic
SomeType: SomeAutoTrait
impl that would otherwise have been generated.
Note that, at present, there is no way to indicate that a given type
does not implement an auto trait but that it may do so in the
future. For ordinary types, this is done by simply not declaring any
impl at all, but that is not an option for auto traits. A workaround
is that one could embed a marker type as one of the fields, where the
marker type is !AutoTrait
.
Immediate uses
Negative impls are used to declare that &T: !DerefMut
and &mut T: !Clone
, as required to fix the soundness of Pin
described in #66544.
This serves two purposes:
- For proving the correctness of unsafe code, we can use that impl as evidence that no
DerefMut
orClone
impl exists. - It prevents downstream crates from creating such impls.
never_patterns
The tracking issue for this feature is: #118155
never_type
The tracking issue for this feature is: #35121
never_type_fallback
The tracking issue for this feature is: #65992
no_core
The tracking issue for this feature is: #29639
no_sanitize
The tracking issue for this feature is: #39699
The no_sanitize
attribute can be used to selectively disable sanitizer
instrumentation in an annotated function. This might be useful to: avoid
instrumentation overhead in a performance critical function, or avoid
instrumenting code that contains constructs unsupported by given sanitizer.
The precise effect of this annotation depends on particular sanitizer in use.
For example, with no_sanitize(thread)
, the thread sanitizer will no longer
instrument non-atomic store / load operations, but it will instrument atomic
operations to avoid reporting false positives and provide meaning full stack
traces.
Examples
#![allow(unused)] #![feature(no_sanitize)] fn main() { #[no_sanitize(address)] fn foo() { // ... } }
non_exhaustive_omitted_patterns_lint
The tracking issue for this feature is: #89554
non_lifetime_binders
The tracking issue for this feature is: #108185
object_safe_for_dispatch
The tracking issue for this feature is: #43561
offset_of_enum
The tracking issue for this feature is: #120141
offset_of_slice
The tracking issue for this feature is: #126151
omit_gdb_pretty_printer_section
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
optimize_attribute
The tracking issue for this feature is: #54882
panic_runtime
The tracking issue for this feature is: #32837
patchable_function_entry
The tracking issue for this feature is: #123115
pattern_complexity
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
pattern_types
The tracking issue for this feature is: #123646
postfix-match
postfix-match
adds the feature for matching upon values postfix
the expressions that generate the values.
#![allow(unused)] #![feature(postfix_match)] fn main() { enum Foo { Bar, Baz } fn get_foo() -> Foo { Foo::Bar } get_foo().match { Foo::Bar => {}, Foo::Baz => panic!(), } }
powerpc_target_feature
The tracking issue for this feature is: #44839
prelude_import
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
prfchw_target_feature
The tracking issue for this feature is: #44839
proc_macro_hygiene
The tracking issue for this feature is: #54727
profiler_runtime
The tracking issue for this feature is: #42524.
ref_pat_eat_one_layer_2024
The tracking issue for this feature is: #123076
ref_pat_eat_one_layer_2024_structural
The tracking issue for this feature is: #123076
register_tool
The tracking issue for this feature is: #66079
repr_simd
The tracking issue for this feature is: #27731
repr128
The tracking issue for this feature is: #56071
The repr128
feature adds support for #[repr(u128)]
on enum
s.
#![allow(unused)] #![feature(repr128)] fn main() { #[repr(u128)] enum Foo { Bar(u64), } }
result_ffi_guarantees
The tracking issue for this feature is: #110503
This feature adds the possibility of using Result<T, E>
in FFI if T's niche
value can be used to describe E or vise-versa.
See RFC 3391 for more information.
return_type_notation
The tracking issue for this feature is: #109417
riscv_target_feature
The tracking issue for this feature is: #44839
rtm_target_feature
The tracking issue for this feature is: #44839
rust_cold_cc
The tracking issue for this feature is: #97544
rustc_allow_const_fn_unstable
The tracking issue for this feature is: #69399
rustc_attrs
This feature has no tracking issue, and is therefore internal to the compiler, not being intended for general use.
Note: rustc_attrs
enables many rustc-internal attributes and this page
only discuss a few of them.
The rustc_attrs
feature allows debugging rustc type layouts by using
#[rustc_layout(...)]
to debug layout at compile time (it even works
with cargo check
) as an alternative to rustc -Z print-type-sizes
that is way more verbose.
Options provided by #[rustc_layout(...)]
are debug
, size
, align
,
abi
. Note that it only works on sized types without generics.
Examples
#![allow(unused)] #![feature(rustc_attrs)] fn main() { #[rustc_layout(abi, size)] pub enum X { Y(u8, u8, u8), Z(isize), } }
When that is compiled, the compiler will error with something like
error: abi: Aggregate { sized: true }
--> src/lib.rs:4:1
|
4 | / pub enum T {
5 | | Y(u8, u8, u8),
6 | | Z(isize),
7 | | }
| |_^
error: size: Size { raw: 16 }
--> src/lib.rs:4:1
|
4 | / pub enum T {
5 | | Y(u8, u8, u8),
6 | | Z(isize),
7 | | }
| |_^
error: aborting due to 2 previous errors
rustc_private
The tracking issue for this feature is: #27812
This feature allows access to unstable internal compiler crates.
Additionally it changes the linking behavior of crates which have this feature enabled. It will prevent linking to a dylib if there's a static variant of it already statically linked into another dylib dependency. This is required to successfully link to rustc_driver
.
rustdoc_internals
The tracking issue for this feature is: #90418
rustdoc_missing_doc_code_examples
The tracking issue for this feature is: #101730
s390x_target_feature
The tracking issue for this feature is: #44839
sha512_sm_x86
The tracking issue for this feature is: #126624
shorter_tail_lifetimes
The tracking issue for this feature is: #123739
simd_ffi
The tracking issue for this feature is: #27731
specialization
The tracking issue for this feature is: #31844
sse4a_target_feature
The tracking issue for this feature is: #44839
staged_api
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
start
The tracking issue for this feature is: #29633
Allows you to mark a function as the entry point of the executable, which is
necessary in #![no_std]
environments.
The function marked #[start]
is passed the command line parameters in the same
format as the C main function (aside from the integer types being used).
It has to be non-generic and have the following signature:
let _:
fn(isize, *const *const u8) -> isize
;
This feature should not be confused with the start
lang item which is
defined by the std
crate and is written #[lang = "start"]
.
Usage together with the std
crate
#[start]
can be used in combination with the std
crate, in which case the
normal main
function (which would get called from the std
crate) won't be
used as an entry point.
The initialization code in std
will be skipped this way.
Example:
#![allow(unused)] #![feature(start)] fn main() { #[start] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } }
Unwinding the stack past the #[start]
function is currently considered
Undefined Behavior (for any unwinding implementation):
#![feature(start)]
#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
std::panic::catch_unwind(|| {
panic!(); // panic safely gets caught or safely aborts execution
});
panic!(); // UB!
0
}
stmt_expr_attributes
The tracking issue for this feature is: #15701
strict_provenance
The tracking issue for this feature is: #95228
The strict_provenance
feature allows to enable the fuzzy_provenance_casts
and lossy_provenance_casts
lints.
These lint on casts between integers and pointers, that are recommended against or invalid in the strict provenance model.
The same feature gate is also used for the experimental strict provenance API in std
(actually core
).
Example
#![feature(strict_provenance)] #![warn(fuzzy_provenance_casts)] fn main() { let _dangling = 16_usize as *const u8; //~^ WARNING: strict provenance disallows casting integer `usize` to pointer `*const u8` }
string_deref_patterns
The tracking issue for this feature is: #87121
This feature permits pattern matching String
to &str
through its Deref
implementation.
#![allow(unused)] #![feature(string_deref_patterns)] fn main() { pub enum Value { String(String), Number(u32), } pub fn is_it_the_answer(value: Value) -> bool { match value { Value::String("42") => true, Value::Number(42) => true, _ => false, } } }
Without this feature other constructs such as match guards have to be used.
#![allow(unused)] fn main() { pub enum Value { String(String), Number(u32), } pub fn is_it_the_answer(value: Value) -> bool { match value { Value::String(s) if s == "42" => true, Value::Number(42) => true, _ => false, } } }
structural_match
The tracking issue for this feature is: #31434
target_feature_11
The tracking issue for this feature is: #69098
tbm_target_feature
The tracking issue for this feature is: #44839
test_unstable_lint
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
thread_local
The tracking issue for this feature is: #29594
trait_alias
The tracking issue for this feature is: #41517
The trait_alias
feature adds support for trait aliases. These allow aliases
to be created for one or more traits (currently just a single regular trait plus
any number of auto-traits), and used wherever traits would normally be used as
either bounds or trait objects.
#![feature(trait_alias)] trait Foo = std::fmt::Debug + Send; trait Bar = Foo + Sync; // Use trait alias as bound on type parameter. fn foo<T: Foo>(v: &T) { println!("{:?}", v); } pub fn main() { foo(&1); // Use trait alias for trait objects. let a: &Bar = &123; println!("{:?}", a); let b = Box::new(456) as Box<dyn Foo>; println!("{:?}", b); }
trait_upcasting
The tracking issue for this feature is: #65991
The trait_upcasting
feature adds support for trait upcasting coercion. This allows a
trait object of type dyn Bar
to be cast to a trait object of type dyn Foo
so long as Bar: Foo
.
#![allow(unused)] #![feature(trait_upcasting)] #![allow(incomplete_features)] fn main() { trait Foo {} trait Bar: Foo {} impl Foo for i32 {} impl<T: Foo + ?Sized> Bar for T {} let bar: &dyn Bar = &123; let foo: &dyn Foo = bar; }
transmute_generic_consts
The tracking issue for this feature is: #109929
transparent_unions
The tracking issue for this feature is #60405
The transparent_unions
feature allows you mark union
s as
#[repr(transparent)]
. A union
may be #[repr(transparent)]
in exactly the
same conditions in which a struct
may be #[repr(transparent)]
(generally,
this means the union
must have exactly one non-zero-sized field). Some
concrete illustrations follow.
#![allow(unused)] #![feature(transparent_unions)] fn main() { // This union has the same representation as `f32`. #[repr(transparent)] union SingleFieldUnion { field: f32, } // This union has the same representation as `usize`. #[repr(transparent)] union MultiFieldUnion { field: usize, nothing: (), } }
For consistency with transparent struct
s, union
s must have exactly one
non-zero-sized field. If all fields are zero-sized, the union
must not be
#[repr(transparent)]
:
#![allow(unused)] #![feature(transparent_unions)] fn main() { // This (non-transparent) union is already valid in stable Rust: pub union GoodUnion { pub nothing: (), } // Error: transparent union needs exactly one non-zero-sized field, but has 0 // #[repr(transparent)] // pub union BadUnion { // pub nothing: (), // } }
The one exception is if the union
is generic over T
and has a field of type
T
, it may be #[repr(transparent)]
even if T
is a zero-sized type:
#![allow(unused)] #![feature(transparent_unions)] fn main() { // This union has the same representation as `T`. #[repr(transparent)] pub union GenericUnion<T: Copy> { // Unions with non-`Copy` fields are unstable. pub field: T, pub nothing: (), } // This is okay even though `()` is a zero-sized type. pub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () }; }
Like transparent struct
s, a transparent union
of type U
has the same
layout, size, and ABI as its single non-ZST field. If it is generic over a type
T
, and all its fields are ZSTs except for exactly one field of type T
, then
it has the same layout and ABI as T
(even if T
is a ZST when monomorphized).
Like transparent struct
s, transparent union
s are FFI-safe if and only if
their underlying representation type is also FFI-safe.
A union
may not be eligible for the same nonnull-style optimizations that a
struct
or enum
(with the same fields) are eligible for. Adding
#[repr(transparent)]
to union
does not change this. To give a more concrete
example, it is unspecified whether size_of::<T>()
is equal to
size_of::<Option<T>>()
, where T
is a union
(regardless of whether or not
it is transparent). The Rust compiler is free to perform this optimization if
possible, but is not required to, and different compiler versions may differ in
their application of these optimizations.
trivial_bounds
The tracking issue for this feature is: #48214
try_blocks
The tracking issue for this feature is: #31436
The try_blocks
feature adds support for try
blocks. A try
block creates a new scope one can use the ?
operator in.
#![allow(unused)] #![feature(try_blocks)] fn main() { use std::num::ParseIntError; let result: Result<i32, ParseIntError> = try { "1".parse::<i32>()? + "2".parse::<i32>()? + "3".parse::<i32>()? }; assert_eq!(result, Ok(6)); let result: Result<i32, ParseIntError> = try { "1".parse::<i32>()? + "foo".parse::<i32>()? + "3".parse::<i32>()? }; assert!(result.is_err()); }
type_alias_impl_trait
The tracking issue for this feature is: #63063
type_changing_struct_update
The tracking issue for this feature is: #86555
This implements RFC2528. When turned on, you can create instances of the same struct that have different generic type or lifetime parameters.
#![allow(unused_variables, dead_code)] #![feature(type_changing_struct_update)] fn main () { struct Foo<T, U> { field1: T, field2: U, } let base: Foo<String, i32> = Foo { field1: String::from("hello"), field2: 1234, }; let updated: Foo<f64, i32> = Foo { field1: 3.14, ..base }; }
unboxed_closures
The tracking issue for this feature is #29625
See Also: fn_traits
The unboxed_closures
feature allows you to write functions using the "rust-call"
ABI,
required for implementing the Fn*
family of traits. "rust-call"
functions must have
exactly one (non self) argument, a tuple representing the argument list.
#![feature(unboxed_closures)] extern "rust-call" fn add_args(args: (u32, u32)) -> u32 { args.0 + args.1 } fn main() {}
unnamed_fields
The tracking issue for this feature is: #49804
unsized_const_params
The tracking issue for this feature is: #95174
unsized_fn_params
The tracking issue for this feature is: #48055
unsized_locals
The tracking issue for this feature is: #48055
This implements RFC1909. When turned on, you can have unsized arguments and locals:
#![allow(incomplete_features)] #![feature(unsized_locals, unsized_fn_params)] use std::any::Any; fn main() { let x: Box<dyn Any> = Box::new(42); let x: dyn Any = *x; // ^ unsized local variable // ^^ unsized temporary foo(x); } fn foo(_: dyn Any) {} // ^^^^^^ unsized argument
The RFC still forbids the following unsized expressions:
#![feature(unsized_locals)] use std::any::Any; struct MyStruct<T: ?Sized> { content: T, } struct MyTupleStruct<T: ?Sized>(T); fn answer() -> Box<dyn Any> { Box::new(42) } fn main() { // You CANNOT have unsized statics. static X: dyn Any = *answer(); // ERROR const Y: dyn Any = *answer(); // ERROR // You CANNOT have struct initialized unsized. MyStruct { content: *answer() }; // ERROR MyTupleStruct(*answer()); // ERROR (42, *answer()); // ERROR // You CANNOT have unsized return types. fn my_function() -> dyn Any { *answer() } // ERROR // You CAN have unsized local variables... let mut x: dyn Any = *answer(); // OK // ...but you CANNOT reassign to them. x = *answer(); // ERROR // You CANNOT even initialize them separately. let y: dyn Any; // OK y = *answer(); // ERROR // Not mentioned in the RFC, but by-move captured variables are also Sized. let x: dyn Any = *answer(); (move || { // ERROR let y = x; })(); // You CAN create a closure with unsized arguments, // but you CANNOT call it. // This is an implementation detail and may be changed in the future. let f = |x: dyn Any| {}; f(*answer()); // ERROR }
By-value trait objects
With this feature, you can have by-value self
arguments without Self: Sized
bounds.
#![feature(unsized_fn_params)] trait Foo { fn foo(self) {} } impl<T: ?Sized> Foo for T {} fn main() { let slice: Box<[i32]> = Box::new([1, 2, 3]); <[i32] as Foo>::foo(*slice); }
And Foo
will also be object-safe.
#![feature(unsized_fn_params)] trait Foo { fn foo(self) {} } impl<T: ?Sized> Foo for T {} fn main () { let slice: Box<dyn Foo> = Box::new([1, 2, 3]); // doesn't compile yet <dyn Foo as Foo>::foo(*slice); }
One of the objectives of this feature is to allow Box<dyn FnOnce>
.
Variable length arrays
The RFC also describes an extension to the array literal syntax: [e; dyn n]
. In the syntax, n
isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of [T]
, instead of [T; n]
.
#![feature(unsized_locals)]
fn mergesort<T: Ord>(a: &mut [T]) {
let mut tmp = [T; dyn a.len()];
// ...
}
fn main() {
let mut a = [3, 1, 5, 6];
mergesort(&mut a);
assert_eq!(a, [1, 3, 5, 6]);
}
VLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like [e; dyn(1)]
would be ambiguous. One possible alternative proposed in the RFC is [e; n]
: if n
captures one or more local variables, then it is considered as [e; dyn n]
.
Advisory on stack usage
It's advised not to casually use the #![feature(unsized_locals)]
feature. Typical use-cases are:
- When you need a by-value trait objects.
- When you really need a fast allocation of small temporary arrays.
Another pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code
#![feature(unsized_locals)] fn main() { let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]); let _x = {{{{{{{{{{*x}}}}}}}}}}; }
and the code
#![feature(unsized_locals)] fn main() { for _ in 0..10 { let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]); let _x = *x; } }
will unnecessarily extend the stack frame.
unsized_tuple_coercion
The tracking issue for this feature is: #42877
This is a part of RFC0401. According to the RFC, there should be an implementation like this:
impl<..., T, U: ?Sized> Unsized<(..., U)> for (..., T) where T: Unsized<U> {}
This implementation is currently gated behind #[feature(unsized_tuple_coercion)]
to avoid insta-stability. Therefore you can use it like this:
#![feature(unsized_tuple_coercion)] fn main() { let x : ([i32; 3], [i32; 3]) = ([1, 2, 3], [4, 5, 6]); let y : &([i32; 3], [i32]) = &x; assert_eq!(y.1[0], 4); }
used_with_arg
The tracking issue for this feature is: #93798
wasm_target_feature
The tracking issue for this feature is: #44839
with_negative_coherence
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
x86_amx_intrinsics
The tracking issue for this feature is: #126622
xop_target_feature
The tracking issue for this feature is: #127208
yeet_expr
The tracking issue for this feature is: #96373
The yeet_expr
feature adds support for do yeet
expressions,
which can be used to early-exit from a function or try
block.
These are highly experimental, thus the placeholder syntax.
#![allow(unused)] #![feature(yeet_expr)] fn main() { fn foo() -> Result<String, i32> { do yeet 4; } assert_eq!(foo(), Err(4)); fn bar() -> Option<String> { do yeet; } assert_eq!(bar(), None); }
Library Features
acceptfilter
The tracking issue for this feature is: #121891
addr_parse_ascii
The tracking issue for this feature is: #101035
alloc_error_hook
The tracking issue for this feature is: #51245
alloc_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
alloc_layout_extra
The tracking issue for this feature is: #55724
allocator_api
The tracking issue for this feature is #32838
Sometimes you want the memory for one collection to use a different
allocator than the memory for another collection. In this case,
replacing the global allocator is not a workable option. Instead,
you need to pass in an instance of an AllocRef
to each collection
for which you want a custom allocator.
TBD
anonymous_pipe
The tracking issue for this feature is: #127154
array_chunks
The tracking issue for this feature is: #74985
array_into_iter_constructors
The tracking issue for this feature is: #91583
array_ptr_get
The tracking issue for this feature is: #119834
array_repeat
The tracking issue for this feature is: #126695
array_try_from_fn
The tracking issue for this feature is: #89379
array_try_map
The tracking issue for this feature is: #79711
array_windows
The tracking issue for this feature is: #75027
as_array_of_cells
The tracking issue for this feature is: #88248
ascii_char
The tracking issue for this feature is: #110998
ascii_char_variants
The tracking issue for this feature is: #110998
assert_matches
The tracking issue for this feature is: #82775
async_drop
The tracking issue for this feature is: #126482
async_fn_traits
See Also: fn_traits
The async_fn_traits
feature allows for implementation of the AsyncFn*
traits
for creating custom closure-like types that return futures.
The main difference to the Fn*
family of traits is that AsyncFn
can return a future
that borrows from itself (FnOnce::Output
has no lifetime parameters, while AsyncFnMut::CallRefFuture
does).
async_gen_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
async_iter_from_iter
The tracking issue for this feature is: #81798
async_iterator
The tracking issue for this feature is: #79024
atomic_from_mut
The tracking issue for this feature is: #76314
backtrace_frames
The tracking issue for this feature is: #79676
bigint_helper_methods
The tracking issue for this feature is: #85532
binary_heap_drain_sorted
The tracking issue for this feature is: #59278
binary_heap_into_iter_sorted
The tracking issue for this feature is: #59278
bound_as_ref
The tracking issue for this feature is: #80996
box_as_ptr
The tracking issue for this feature is: #129090
box_into_boxed_slice
The tracking issue for this feature is: #71582
box_into_inner
The tracking issue for this feature is: #80437
box_uninit_write
The tracking issue for this feature is: #129397
btree_cursors
The tracking issue for this feature is: #107540
btree_extract_if
The tracking issue for this feature is: #70530
btreemap_alloc
The tracking issue for this feature is: #32838
buf_read_has_data_left
The tracking issue for this feature is: #86423
bufread_skip_until
The tracking issue for this feature is: #111735
bufreader_peek
The tracking issue for this feature is: #128405
build_hasher_default_const_new
The tracking issue for this feature is: #123197
c_size_t
The tracking issue for this feature is: #88345
c_str_module
The tracking issue for this feature is: #112134
c_void_variant
This feature is internal to the Rust compiler and is not intended for general use.
can_vector
The tracking issue for this feature is: #69941
cell_leak
The tracking issue for this feature is: #69099
cell_update
The tracking issue for this feature is: #50186
cfg_accessible
The tracking issue for this feature is: #64797
cfg_eval
The tracking issue for this feature is: #82679
cfg_match
The tracking issue for this feature is: #115585
char_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
clone_to_uninit
The tracking issue for this feature is: #126799
cmp_minmax
The tracking issue for this feature is: #115939
coerce_unsized
The tracking issue for this feature is: #18598
concat_bytes
The tracking issue for this feature is: #87555
concat_idents
The tracking issue for this feature is: #29599
The concat_idents
feature adds a macro for concatenating multiple identifiers
into one identifier.
Examples
#![feature(concat_idents)] fn main() { fn foobar() -> u32 { 23 } let f = concat_idents!(foo, bar); assert_eq!(f(), 23); }
const_align_of_val
The tracking issue for this feature is: #46571
const_align_of_val_raw
The tracking issue for this feature is: #46571
const_align_offset
The tracking issue for this feature is: #90962
const_alloc_error
The tracking issue for this feature is: #92523
const_alloc_layout
The tracking issue for this feature is: #67521
const_arguments_as_str
The tracking issue for this feature is: #103900
const_array_from_ref
The tracking issue for this feature is: #90206
const_array_into_iter_constructors
The tracking issue for this feature is: #91583
const_atomic_from_ptr
The tracking issue for this feature is: #108652
const_bigint_helper_methods
The tracking issue for this feature is: #85532
const_binary_heap_new_in
The tracking issue for this feature is: #125961
const_black_box
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_box
The tracking issue for this feature is: #92521
const_btree_len
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_cell_into_inner
The tracking issue for this feature is: #78729
const_collections_with_hasher
The tracking issue for this feature is: #102575
const_cow_is_borrowed
The tracking issue for this feature is: #65143
const_eval_select
The tracking issue for this feature is: #124625
const_exact_div
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_float_classify
The tracking issue for this feature is: #72505
const_fmt_arguments_new
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_format_args
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_hash
The tracking issue for this feature is: #104061
const_heap
The tracking issue for this feature is: #79597
const_index_range_slice_index
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_intoiterator_identity
The tracking issue for this feature is: #90603
const_intrinsic_compare_bytes
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_intrinsic_copy
The tracking issue for this feature is: #80697
const_intrinsic_forget
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_intrinsic_raw_eq
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_ip
The tracking issue for this feature is: #76205
const_ipv4
The tracking issue for this feature is: #76205
const_ipv6
The tracking issue for this feature is: #76205
const_likely
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_maybe_uninit_array_assume_init
The tracking issue for this feature is: #96097
const_maybe_uninit_as_mut_ptr
The tracking issue for this feature is: #75251
const_maybe_uninit_assume_init
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_maybe_uninit_uninit_array
The tracking issue for this feature is: #96097
const_maybe_uninit_write
The tracking issue for this feature is: #63567
const_nonnull_new
The tracking issue for this feature is: #93235
const_num_midpoint
The tracking issue for this feature is: #110840
const_option
The tracking issue for this feature is: #67441
const_option_ext
The tracking issue for this feature is: #91930
const_pin
The tracking issue for this feature is: #76654
const_pointer_is_aligned
The tracking issue for this feature is: #104203
const_pref_align_of
The tracking issue for this feature is: #91971
const_ptr_as_ref
The tracking issue for this feature is: #91822
const_ptr_is_null
The tracking issue for this feature is: #74939
const_ptr_sub_ptr
The tracking issue for this feature is: #95892
const_ptr_write
The tracking issue for this feature is: #86302
const_range_bounds
The tracking issue for this feature is: #108082
const_raw_ptr_comparison
The tracking issue for this feature is: #53020
const_replace
The tracking issue for this feature is: #83164
const_result
The tracking issue for this feature is: #82814
const_size_of_val
The tracking issue for this feature is: #46571
const_size_of_val_raw
The tracking issue for this feature is: #46571
const_slice_first_last
The tracking issue for this feature is: #83570
const_slice_first_last_chunk
The tracking issue for this feature is: #111774
const_slice_flatten
The tracking issue for this feature is: #95629
const_slice_from_mut_ptr_range
The tracking issue for this feature is: #89792
const_slice_from_ptr_range
The tracking issue for this feature is: #89792
const_slice_from_raw_parts_mut
The tracking issue for this feature is: #67456
const_slice_from_ref
The tracking issue for this feature is: #90206
const_slice_index
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_slice_split_at_mut
The tracking issue for this feature is: #101804
const_str_as_mut
The tracking issue for this feature is: #130086
const_str_from_raw_parts_mut
The tracking issue for this feature is: #119206
const_str_from_utf8
The tracking issue for this feature is: #91006
const_str_from_utf8_unchecked_mut
The tracking issue for this feature is: #91005
const_strict_overflow_ops
The tracking issue for this feature is: #118260
const_swap
The tracking issue for this feature is: #83163
const_three_way_compare
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_type_id
The tracking issue for this feature is: #77125
const_type_name
The tracking issue for this feature is: #63084
const_typed_swap
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_ub_checks
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
const_unbounded_shifts
The tracking issue for this feature is: #129375
const_unicode_case_lookup
The tracking issue for this feature is: #101400
const_unsafecell_get_mut
The tracking issue for this feature is: #88836
container_error_extra
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
context_ext
The tracking issue for this feature is: #123392
control_flow_enum
The tracking issue for this feature is: #75744
convert_float_to_int
The tracking issue for this feature is: #67057
core_intrinsics
This feature is internal to the Rust compiler and is not intended for general use.
core_io_borrowed_buf
The tracking issue for this feature is: #117693
core_pattern_type
The tracking issue for this feature is: #123646
core_pattern_types
The tracking issue for this feature is: #123646
core_private_bignum
This feature is internal to the Rust compiler and is not intended for general use.
core_private_diy_float
This feature is internal to the Rust compiler and is not intended for general use.
coroutine_trait
The tracking issue for this feature is: #43122
cow_is_borrowed
The tracking issue for this feature is: #65143
cstr_bytes
The tracking issue for this feature is: #112115
cstr_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
cursor_split
The tracking issue for this feature is: #86369
deadline_api
The tracking issue for this feature is: #46316
debug_closure_helpers
The tracking issue for this feature is: #117729
debug_more_non_exhaustive
The tracking issue for this feature is: #127942
dec2flt
This feature is internal to the Rust compiler and is not intended for general use.
deref_pure_trait
The tracking issue for this feature is: #87121
derive_clone_copy
This feature is internal to the Rust compiler and is not intended for general use.
derive_const
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
derive_eq
This feature is internal to the Rust compiler and is not intended for general use.
dir_entry_ext2
The tracking issue for this feature is: #85573
discriminant_kind
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
dispatch_from_dyn
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
downcast_unchecked
The tracking issue for this feature is: #90850
drain_keep_rest
The tracking issue for this feature is: #101122
duration_constants
The tracking issue for this feature is: #57391
duration_constructors
The tracking issue for this feature is: #120301
Add the methods from_mins
, from_hours
and from_days
to Duration
.
duration_consts_float
The tracking issue for this feature is: #72440
duration_millis_float
The tracking issue for this feature is: #122451
duration_units
The tracking issue for this feature is: #120301
edition_panic
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
effect_types
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
entry_insert
The tracking issue for this feature is: #65225
error_generic_member_access
The tracking issue for this feature is: #99301
error_iter
The tracking issue for this feature is: #58520
error_reporter
The tracking issue for this feature is: #90172
error_type_id
The tracking issue for this feature is: #60784
exact_size_is_empty
The tracking issue for this feature is: #35428
exclusive_wrapper
The tracking issue for this feature is: #98407
exit_status_error
The tracking issue for this feature is: #84908
exitcode_exit_method
The tracking issue for this feature is: #97100
exposed_provenance
The tracking issue for this feature is: #95228
extend_one
The tracking issue for this feature is: #72631
extend_one_unchecked
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
extract_if
The tracking issue for this feature is: #43244
f128_const
The tracking issue for this feature is: #116909
f16_const
The tracking issue for this feature is: #116909
fd
This feature is internal to the Rust compiler and is not intended for general use.
fd_read
This feature is internal to the Rust compiler and is not intended for general use.
float_gamma
The tracking issue for this feature is: #99842
float_minimum_maximum
The tracking issue for this feature is: #91079
float_next_up_down
The tracking issue for this feature is: #91399
flt2dec
This feature is internal to the Rust compiler and is not intended for general use.
fmt_helpers_for_derive
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
fmt_internals
This feature is internal to the Rust compiler and is not intended for general use.
fn_ptr_trait
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
fn_traits
The tracking issue for this feature is #29625
See Also: unboxed_closures
The fn_traits
feature allows for implementation of the Fn*
traits
for creating custom closure-like types.
#![feature(unboxed_closures)] #![feature(fn_traits)] struct Adder { a: u32 } impl FnOnce<(u32, )> for Adder { type Output = u32; extern "rust-call" fn call_once(self, b: (u32, )) -> Self::Output { self.a + b.0 } } fn main() { let adder = Adder { a: 3 }; assert_eq!(adder(2), 5); }
forget_unsized
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
format_args_nl
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
freeze
The tracking issue for this feature is: #121675
future_join
The tracking issue for this feature is: #91642
gen_future
The tracking issue for this feature is: #50547
generic_assert_internals
The tracking issue for this feature is: #44838
get_many_mut
The tracking issue for this feature is: #104642
get_mut_unchecked
The tracking issue for this feature is: #63292
hash_extract_if
The tracking issue for this feature is: #59618
hash_raw_entry
The tracking issue for this feature is: #56167
hash_set_entry
The tracking issue for this feature is: #60896
hasher_prefixfree_extras
The tracking issue for this feature is: #96762
hashmap_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
hint_must_use
The tracking issue for this feature is: #94745
inplace_iteration
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
int_roundings
The tracking issue for this feature is: #88581
integer_atomics
The tracking issue for this feature is: #99069
integer_sign_cast
The tracking issue for this feature is: #125882
internal_impls_macro
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
internal_output_capture
This feature is internal to the Rust compiler and is not intended for general use.
io_error_more
The tracking issue for this feature is: #86442
io_error_uncategorized
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
ip
The tracking issue for this feature is: #27709
is_ascii_octdigit
The tracking issue for this feature is: #101288
is_riscv_feature_detected
The tracking issue for this feature is: #111192
is_val_statically_known
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
isqrt
The tracking issue for this feature is: #116226
iter_advance_by
The tracking issue for this feature is: #77404
iter_array_chunks
The tracking issue for this feature is: #100450
iter_chain
The tracking issue for this feature is: #125964
iter_collect_into
The tracking issue for this feature is: #94780
iter_from_coroutine
The tracking issue for this feature is: #43122
iter_intersperse
The tracking issue for this feature is: #79524
iter_is_partitioned
The tracking issue for this feature is: #62544
iter_map_windows
The tracking issue for this feature is: #87155
iter_next_chunk
The tracking issue for this feature is: #98326
iter_order_by
The tracking issue for this feature is: #64295
iter_partition_in_place
The tracking issue for this feature is: #62543
iterator_try_collect
The tracking issue for this feature is: #94047
iterator_try_reduce
The tracking issue for this feature is: #87053
junction_point
The tracking issue for this feature is: #121709
layout_for_ptr
The tracking issue for this feature is: #69835
lazy_cell_into_inner
The tracking issue for this feature is: #125623
liballoc_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
libstd_sys_internals
This feature is internal to the Rust compiler and is not intended for general use.
linked_list_cursors
The tracking issue for this feature is: #58533
linked_list_remove
The tracking issue for this feature is: #69210
linked_list_retain
The tracking issue for this feature is: #114135
linux_pidfd
The tracking issue for this feature is: #82971
local_waker
The tracking issue for this feature is: #118959
log_syntax
The tracking issue for this feature is: #29598
map_entry_replace
The tracking issue for this feature is: #44286
map_many_mut
The tracking issue for this feature is: #97601
map_try_insert
The tracking issue for this feature is: #82766
mapped_lock_guards
The tracking issue for this feature is: #117108
maybe_uninit_array_assume_init
The tracking issue for this feature is: #96097
maybe_uninit_as_bytes
The tracking issue for this feature is: #93092
maybe_uninit_fill
The tracking issue for this feature is: #117428
maybe_uninit_slice
The tracking issue for this feature is: #63569
maybe_uninit_uninit_array
The tracking issue for this feature is: #96097
maybe_uninit_uninit_array_transpose
The tracking issue for this feature is: #96097
maybe_uninit_write_slice
The tracking issue for this feature is: #79995
mem_copy_fn
The tracking issue for this feature is: #98262
more_float_constants
The tracking issue for this feature is: #103883
new_range_api
The tracking issue for this feature is: #125687
new_zeroed_alloc
The tracking issue for this feature is: #129396
non_zero_count_ones
The tracking issue for this feature is: #120287
nonzero_bitwise
The tracking issue for this feature is: #128281
nonzero_from_mut
The tracking issue for this feature is: #106290
nonzero_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
nonzero_ops
The tracking issue for this feature is: #84186
noop_waker
The tracking issue for this feature is: #98286
num_midpoint
The tracking issue for this feature is: #110840
numfmt
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
once_cell_get_mut
The tracking issue for this feature is: #121641
once_cell_try
The tracking issue for this feature is: #109737
once_cell_try_insert
The tracking issue for this feature is: #116693
once_wait
The tracking issue for this feature is: #127527
one_sided_range
The tracking issue for this feature is: #69780
option_get_or_insert_default
The tracking issue for this feature is: #82901
option_zip
The tracking issue for this feature is: #70086
os_str_display
The tracking issue for this feature is: #120048
os_str_slice
The tracking issue for this feature is: #118485
os_string_pathbuf_leak
The tracking issue for this feature is: #125965
panic_abort
The tracking issue for this feature is: #32837
panic_always_abort
The tracking issue for this feature is: #84438
panic_backtrace_config
The tracking issue for this feature is: #93346
panic_can_unwind
The tracking issue for this feature is: #92988
panic_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
panic_payload_as_str
The tracking issue for this feature is: #125175
panic_unwind
The tracking issue for this feature is: #32837
panic_update_hook
The tracking issue for this feature is: #92649
path_add_extension
The tracking issue for this feature is: #127292
path_file_prefix
The tracking issue for this feature is: #86319
pattern
The tracking issue for this feature is: #27721
peer_credentials_unix_socket
The tracking issue for this feature is: #42839
pin_coerce_unsized_trait
The tracking issue for this feature is: #123430
pin_deref_mut
The tracking issue for this feature is: #86918
pointer_is_aligned_to
The tracking issue for this feature is: #96284
pointer_like_trait
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
portable_simd
The tracking issue for this feature is: #86656
prelude_2024
The tracking issue for this feature is: #121042
print_internals
This feature is internal to the Rust compiler and is not intended for general use.
proc_macro_def_site
The tracking issue for this feature is: #54724
proc_macro_diagnostic
The tracking issue for this feature is: #54140
proc_macro_expand
The tracking issue for this feature is: #90765
proc_macro_internals
The tracking issue for this feature is: #27812
proc_macro_quote
The tracking issue for this feature is: #54722
proc_macro_span
The tracking issue for this feature is: #54725
proc_macro_tracked_env
The tracking issue for this feature is: #99515
process_exitcode_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
process_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
profiler_runtime_lib
This feature is internal to the Rust compiler and is not intended for general use.
ptr_alignment_type
The tracking issue for this feature is: #102070
ptr_as_ref_unchecked
The tracking issue for this feature is: #122034
ptr_as_uninit
The tracking issue for this feature is: #75402
ptr_fn_addr_eq
The tracking issue for this feature is: #129322
ptr_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
ptr_mask
The tracking issue for this feature is: #98290
ptr_metadata
The tracking issue for this feature is: #81513
ptr_sub_ptr
The tracking issue for this feature is: #95892
pub_crate_should_not_need_unstable_attr
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
raw_os_error_ty
The tracking issue for this feature is: #107792
raw_slice_split
The tracking issue for this feature is: #95595
raw_vec_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
read_buf
The tracking issue for this feature is: #78485
receiver_trait
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
reentrant_lock
The tracking issue for this feature is: #121440
restricted_std
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
result_flattening
The tracking issue for this feature is: #70142
round_char_boundary
The tracking issue for this feature is: #93743
rt
This feature is internal to the Rust compiler and is not intended for general use.
rustc_encodable_decodable
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
sealed
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
seek_stream_len
The tracking issue for this feature is: #59359
set_ptr_value
The tracking issue for this feature is: #75091
setgroups
The tracking issue for this feature is: #90747
sgx_platform
The tracking issue for this feature is: #56975
sized_type_properties
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
slice_as_chunks
The tracking issue for this feature is: #74985
slice_concat_ext
The tracking issue for this feature is: #27747
slice_concat_trait
The tracking issue for this feature is: #27747
slice_from_ptr_range
The tracking issue for this feature is: #89792
slice_index_methods
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
slice_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
slice_iter_mut_as_mut_slice
The tracking issue for this feature is: #93079
slice_partition_dedup
The tracking issue for this feature is: #54279
slice_pattern
The tracking issue for this feature is: #56345
slice_ptr_get
The tracking issue for this feature is: #74265
slice_range
The tracking issue for this feature is: #76393
slice_split_once
The tracking issue for this feature is: #112811
slice_swap_unchecked
The tracking issue for this feature is: #88539
slice_take
The tracking issue for this feature is: #62280
solid_ext
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
sort_floats
The tracking issue for this feature is: #93396
split_array
The tracking issue for this feature is: #90091
split_as_slice
The tracking issue for this feature is: #96137
std_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
stdarch_arm_feature_detection
The tracking issue for this feature is: #111190
stdarch_mips_feature_detection
The tracking issue for this feature is: #111188
stdarch_powerpc_feature_detection
The tracking issue for this feature is: #111191
stdio_makes_pipe
The tracking issue for this feature is: #98288
step_trait
The tracking issue for this feature is: #42168
str_from_raw_parts
The tracking issue for this feature is: #119206
str_from_utf16_endian
The tracking issue for this feature is: #116258
str_internals
This feature is internal to the Rust compiler and is not intended for general use.
str_lines_remainder
The tracking issue for this feature is: #77998
str_split_inclusive_remainder
The tracking issue for this feature is: #77998
str_split_remainder
The tracking issue for this feature is: #77998
str_split_whitespace_remainder
The tracking issue for this feature is: #77998
strict_overflow_ops
The tracking issue for this feature is: #118260
strict_provenance_atomic_ptr
The tracking issue for this feature is: #99108
string_extend_from_within
The tracking issue for this feature is: #103806
string_remove_matches
The tracking issue for this feature is: #72826
substr_range
The tracking issue for this feature is: #126769
sync_unsafe_cell
The tracking issue for this feature is: #95439
tcp_deferaccept
The tracking issue for this feature is: #119639
tcp_linger
The tracking issue for this feature is: #88494
tcp_quickack
The tracking issue for this feature is: #96256
tcplistener_into_incoming
The tracking issue for this feature is: #88373
test
The tracking issue for this feature is: None.
The internals of the test
crate are unstable, behind the test
flag. The
most widely used part of the test
crate are benchmark tests, which can test
the performance of your code. Let's make our src/lib.rs
look like this
(comments elided):
#![allow(unused)] #![feature(test)] fn main() { extern crate test; pub fn add_two(a: i32) -> i32 { a + 2 } #[cfg(test)] mod tests { use super::*; use test::Bencher; #[test] fn it_works() { assert_eq!(4, add_two(2)); } #[bench] fn bench_add_two(b: &mut Bencher) { b.iter(|| add_two(2)); } } }
Note the test
feature gate, which enables this unstable feature.
We've imported the test
crate, which contains our benchmarking support.
We have a new function as well, with the bench
attribute. Unlike regular
tests, which take no arguments, benchmark tests take a &mut Bencher
. This
Bencher
provides an iter
method, which takes a closure. This closure
contains the code we'd like to benchmark.
We can run benchmark tests with cargo bench
:
$ cargo bench
Compiling adder v0.0.1 (file:///home/steve/tmp/adder)
Running target/release/adder-91b3e234d4ed382a
running 2 tests
test tests::it_works ... ignored
test tests::bench_add_two ... bench: 1 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 1 ignored; 1 measured
Our non-benchmark test was ignored. You may have noticed that cargo bench
takes a bit longer than cargo test
. This is because Rust runs our benchmark
a number of times, and then takes the average. Because we're doing so little
work in this example, we have a 1 ns/iter (+/- 0)
, but this would show
the variance if there was one.
Advice on writing benchmarks:
- Move setup code outside the
iter
loop; only put the part you want to measure inside - Make the code do "the same thing" on each iteration; do not accumulate or change state
- Make the outer function idempotent too; the benchmark runner is likely to run it many times
- Make the inner
iter
loop short and fast so benchmark runs are fast and the calibrator can adjust the run-length at fine resolution - Make the code in the
iter
loop do something simple, to assist in pinpointing performance improvements (or regressions)
Gotcha: optimizations
There's another tricky part to writing benchmarks: benchmarks compiled with optimizations activated can be dramatically changed by the optimizer so that the benchmark is no longer benchmarking what one expects. For example, the compiler might recognize that some calculation has no external effects and remove it entirely.
#![allow(unused)] #![feature(test)] fn main() { extern crate test; use test::Bencher; #[bench] fn bench_xor_1000_ints(b: &mut Bencher) { b.iter(|| { (0..1000).fold(0, |old, new| old ^ new); }); } }
gives the following results
running 1 test
test bench_xor_1000_ints ... bench: 0 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
The benchmarking runner offers two ways to avoid this. Either, the closure that
the iter
method receives can return an arbitrary value which forces the
optimizer to consider the result used and ensures it cannot remove the
computation entirely. This could be done for the example above by adjusting the
b.iter
call to
#![allow(unused)] fn main() { struct X; impl X { fn iter<T, F>(&self, _: F) where F: FnMut() -> T {} } let b = X; b.iter(|| { // Note lack of `;` (could also use an explicit `return`). (0..1000).fold(0, |old, new| old ^ new) }); }
Or, the other option is to call the generic test::black_box
function, which
is an opaque "black box" to the optimizer and so forces it to consider any
argument as used.
#![feature(test)] extern crate test; fn main() { struct X; impl X { fn iter<T, F>(&self, _: F) where F: FnMut() -> T {} } let b = X; b.iter(|| { let n = test::black_box(1000); (0..n).fold(0, |a, b| a ^ b) }) }
Neither of these read or modify the value, and are very cheap for small values.
Larger values can be passed indirectly to reduce overhead (e.g.
black_box(&huge_struct)
).
Performing either of the above changes gives the following benchmarking results
running 1 test
test bench_xor_1000_ints ... bench: 131 ns/iter (+/- 3)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
However, the optimizer can still modify a testcase in an undesirable manner even when using either of the above.
thin_box
The tracking issue for this feature is: #92791
thread_id_value
The tracking issue for this feature is: #67939
thread_local_internals
This feature is internal to the Rust compiler and is not intended for general use.
thread_sleep_until
The tracking issue for this feature is: #113752
trace_macros
The tracking issue for this feature is #29598.
With trace_macros
you can trace the expansion of macros in your code.
Examples
#![feature(trace_macros)] fn main() { trace_macros!(true); println!("Hello, Rust!"); trace_macros!(false); }
The cargo build
output:
note: trace_macro
--> src/main.rs:5:5
|
5 | println!("Hello, Rust!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: expanding `println! { "Hello, Rust!" }`
= note: to `print ! ( concat ! ( "Hello, Rust!" , "\n" ) )`
= note: expanding `print! { concat ! ( "Hello, Rust!" , "\n" ) }`
= note: to `$crate :: io :: _print ( format_args ! ( concat ! ( "Hello, Rust!" , "\n" ) )
)`
Finished dev [unoptimized + debuginfo] target(s) in 0.60 secs
track_path
The tracking issue for this feature is: #99515
transmutability
The tracking issue for this feature is: #99571
trusted_fused
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
trusted_len
The tracking issue for this feature is: #37572
trusted_len_next_unchecked
The tracking issue for this feature is: #37572
trusted_random_access
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
trusted_step
The tracking issue for this feature is: #85731
try_find
The tracking issue for this feature is: #63178
try_reserve_kind
The tracking issue for this feature is: #48043
try_trait_v2
The tracking issue for this feature is: #84277
try_trait_v2_residual
The tracking issue for this feature is: #91285
try_trait_v2_yeet
The tracking issue for this feature is: #96374
try_with_capacity
The tracking issue for this feature is: #91913
tuple_trait
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
type_ascription
The tracking issue for this feature is: #23416
ub_checks
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
uefi_std
The tracking issue for this feature is: #100499
unbounded_shifts
The tracking issue for this feature is: #129375
unchecked_neg
The tracking issue for this feature is: #85122
unchecked_shifts
The tracking issue for this feature is: #85122
unicode_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
unique_rc_arc
The tracking issue for this feature is: #112566
unix_file_vectored_at
The tracking issue for this feature is: #89517
unix_set_mark
The tracking issue for this feature is: #96467
unix_socket_ancillary_data
The tracking issue for this feature is: #76915
unix_socket_peek
The tracking issue for this feature is: #76923
unsafe_cell_from_mut
The tracking issue for this feature is: #111645
unsafe_pin_internals
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
unsigned_is_multiple_of
The tracking issue for this feature is: #128101
unsigned_signed_diff
The tracking issue for this feature is: #126041
unsize
The tracking issue for this feature is: #18598
unwrap_infallible
The tracking issue for this feature is: #61695
update_panic_count
This feature is internal to the Rust compiler and is not intended for general use.
utf16_extra
The tracking issue for this feature is: #94919
utf16_extra_const
The tracking issue for this feature is: #94919
variant_count
The tracking issue for this feature is: #73662
vec_into_raw_parts
The tracking issue for this feature is: #65816
vec_pop_if
The tracking issue for this feature is: #122741
vec_push_within_capacity
The tracking issue for this feature is: #100486
vec_split_at_spare
The tracking issue for this feature is: #81944
wasi_ext
The tracking issue for this feature is: #71213
windows_by_handle
The tracking issue for this feature is: #63010
windows_c
This feature is internal to the Rust compiler and is not intended for general use.
windows_change_time
The tracking issue for this feature is: #121478
windows_handle
This feature is internal to the Rust compiler and is not intended for general use.
windows_net
This feature is internal to the Rust compiler and is not intended for general use.
windows_process_exit_code_from
The tracking issue for this feature is: #111688
windows_process_extensions_async_pipes
The tracking issue for this feature is: #98289
windows_process_extensions_force_quotes
The tracking issue for this feature is: #82227
windows_process_extensions_main_thread_handle
The tracking issue for this feature is: #96723
windows_process_extensions_raw_attribute
The tracking issue for this feature is: #114854
windows_process_extensions_show_window
The tracking issue for this feature is: #127544
windows_stdio
This feature is internal to the Rust compiler and is not intended for general use.
wrapping_int_impl
The tracking issue for this feature is: #32463
wrapping_next_power_of_two
The tracking issue for this feature is: #32463
write_all_vectored
The tracking issue for this feature is: #70436
yeet_desugar_details
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.