diff options
| author | bors <bors@rust-lang.org> | 2025-08-07 14:01:43 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2025-08-07 14:01:43 +0000 |
| commit | 321a89bec57b8ca723d1af8f784490b950458c6a (patch) | |
| tree | 8a0137b29007700853d5d19739718beda89a42be /src | |
| parent | cd434309efcf5dc68b253e5ef6ba40c1c43711c9 (diff) | |
| parent | bd8e7fdc543880073dcfc0fce9096b188f915f9a (diff) | |
| download | rust-321a89bec57b8ca723d1af8f784490b950458c6a.tar.gz rust-321a89bec57b8ca723d1af8f784490b950458c6a.zip | |
Auto merge of #145043 - Zalathar:rollup-3dbvdrm, r=Zalathar
Rollup of 19 pull requests Successful merges: - rust-lang/rust#137831 (Tweak auto trait errors) - rust-lang/rust#138689 (add nvptx_target_feature) - rust-lang/rust#140267 (implement continue_ok and break_ok for ControlFlow) - rust-lang/rust#143028 (emit `StorageLive` and schedule `StorageDead` for `let`-`else`'s bindings after matching) - rust-lang/rust#143764 (lower pattern bindings in the order they're written and base drop order on primary bindings' order) - rust-lang/rust#143808 (Port `#[should_panic]` to the new attribute parsing infrastructure ) - rust-lang/rust#143906 (Miri: non-deterministic floating point operations in `foreign_items`) - rust-lang/rust#143929 (Mark all deprecation lints in name resolution as deny-by-default and report-in-deps) - rust-lang/rust#144133 (Stabilize const TypeId::of) - rust-lang/rust#144369 (Upgrade semicolon_in_expressions_from_macros from warn to deny) - rust-lang/rust#144439 (Introduce ModernIdent type to unify macro 2.0 hygiene handling) - rust-lang/rust#144473 (Address libunwind.a inconsistency issues in the bootstrap program) - rust-lang/rust#144601 (Allow `cargo fix` to partially apply `mismatched_lifetime_syntaxes`) - rust-lang/rust#144650 (Additional tce tests) - rust-lang/rust#144659 (bootstrap: refactor mingw dist and fix gnullvm) - rust-lang/rust#144682 (Stabilize `strict_overflow_ops`) - rust-lang/rust#145026 (Update books) - rust-lang/rust#145033 (Reimplement `print_region` in `type_name.rs`.) - rust-lang/rust#145040 (rustc-dev-guide subtree update) Failed merges: - rust-lang/rust#143857 (Port #[macro_export] to the new attribute parsing infrastructure) r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'src')
| -rw-r--r-- | src/bootstrap/src/core/build_steps/compile.rs | 10 | ||||
| -rw-r--r-- | src/bootstrap/src/core/build_steps/dist.rs | 142 | ||||
| -rw-r--r-- | src/bootstrap/src/core/build_steps/llvm.rs | 8 | ||||
| m--------- | src/doc/book | 0 | ||||
| m--------- | src/doc/reference | 0 | ||||
| m--------- | src/doc/rust-by-example | 0 | ||||
| -rw-r--r-- | src/doc/rustc-dev-guide/rust-version | 2 | ||||
| -rw-r--r-- | src/doc/rustc-dev-guide/src/offload/installation.md | 2 | ||||
| -rw-r--r-- | src/doc/rustc-dev-guide/src/overview.md | 4 | ||||
| -rw-r--r-- | src/doc/rustc-dev-guide/src/tests/running.md | 23 | ||||
| -rw-r--r-- | src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md | 40 | ||||
| -rw-r--r-- | src/tools/miri/src/intrinsics/mod.rs | 196 | ||||
| -rw-r--r-- | src/tools/miri/src/lib.rs | 4 | ||||
| -rw-r--r-- | src/tools/miri/src/math.rs | 256 | ||||
| -rw-r--r-- | src/tools/miri/src/shims/foreign_items.rs | 217 | ||||
| -rw-r--r-- | src/tools/miri/tests/pass/float.rs | 154 | ||||
| -rw-r--r-- | src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs | 14 |
17 files changed, 677 insertions, 395 deletions
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 7039f31cdde..e1ee0773107 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2384,15 +2384,19 @@ pub fn run_cargo( let mut deps = Vec::new(); let mut toplevel = Vec::new(); let ok = stream_cargo(builder, cargo, tail_args, &mut |msg| { - let (filenames, crate_types) = match msg { + let (filenames_vec, crate_types) = match msg { CargoMessage::CompilerArtifact { filenames, target: CargoTarget { crate_types }, .. - } => (filenames, crate_types), + } => { + let mut f: Vec<String> = filenames.into_iter().map(|s| s.into_owned()).collect(); + f.sort(); // Sort the filenames + (f, crate_types) + } _ => return, }; - for filename in filenames { + for filename in filenames_vec { // Skip files like executables let mut keep = false; if filename.ends_with(".lib") diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 0465a071159..cbbfb6b6a11 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -174,36 +174,12 @@ fn find_files(files: &[&str], path: &[PathBuf]) -> Vec<PathBuf> { found } -fn make_win_dist( - rust_root: &Path, - plat_root: &Path, - target: TargetSelection, - builder: &Builder<'_>, -) { +fn make_win_dist(plat_root: &Path, target: TargetSelection, builder: &Builder<'_>) { if builder.config.dry_run() { return; } - //Ask gcc where it keeps its stuff - let mut cmd = command(builder.cc(target)); - cmd.arg("-print-search-dirs"); - let gcc_out = cmd.run_capture_stdout(builder).stdout(); - - let mut bin_path: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect(); - let mut lib_path = Vec::new(); - - for line in gcc_out.lines() { - let idx = line.find(':').unwrap(); - let key = &line[..idx]; - let trim_chars: &[_] = &[' ', '=']; - let value = env::split_paths(line[(idx + 1)..].trim_start_matches(trim_chars)); - - if key == "programs" { - bin_path.extend(value); - } else if key == "libraries" { - lib_path.extend(value); - } - } + let (bin_path, lib_path) = get_cc_search_dirs(target, builder); let compiler = if target == "i686-pc-windows-gnu" { "i686-w64-mingw32-gcc.exe" @@ -213,12 +189,6 @@ fn make_win_dist( "gcc.exe" }; let target_tools = [compiler, "ld.exe", "dlltool.exe", "libwinpthread-1.dll"]; - let mut rustc_dlls = vec!["libwinpthread-1.dll"]; - if target.starts_with("i686-") { - rustc_dlls.push("libgcc_s_dw2-1.dll"); - } else { - rustc_dlls.push("libgcc_s_seh-1.dll"); - } // Libraries necessary to link the windows-gnu toolchains. // System libraries will be preferred if they are available (see #67429). @@ -274,25 +244,8 @@ fn make_win_dist( //Find mingw artifacts we want to bundle let target_tools = find_files(&target_tools, &bin_path); - let rustc_dlls = find_files(&rustc_dlls, &bin_path); let target_libs = find_files(&target_libs, &lib_path); - // Copy runtime dlls next to rustc.exe - let rust_bin_dir = rust_root.join("bin/"); - fs::create_dir_all(&rust_bin_dir).expect("creating rust_bin_dir failed"); - for src in &rustc_dlls { - builder.copy_link_to_folder(src, &rust_bin_dir); - } - - if builder.config.lld_enabled { - // rust-lld.exe also needs runtime dlls - let rust_target_bin_dir = rust_root.join("lib/rustlib").join(target).join("bin"); - fs::create_dir_all(&rust_target_bin_dir).expect("creating rust_target_bin_dir failed"); - for src in &rustc_dlls { - builder.copy_link_to_folder(src, &rust_target_bin_dir); - } - } - //Copy platform tools to platform-specific bin directory let plat_target_bin_self_contained_dir = plat_root.join("lib/rustlib").join(target).join("bin/self-contained"); @@ -320,6 +273,82 @@ fn make_win_dist( } } +fn runtime_dll_dist(rust_root: &Path, target: TargetSelection, builder: &Builder<'_>) { + if builder.config.dry_run() { + return; + } + + let (bin_path, libs_path) = get_cc_search_dirs(target, builder); + + let mut rustc_dlls = vec![]; + // windows-gnu and windows-gnullvm require different runtime libs + if target.ends_with("windows-gnu") { + rustc_dlls.push("libwinpthread-1.dll"); + if target.starts_with("i686-") { + rustc_dlls.push("libgcc_s_dw2-1.dll"); + } else { + rustc_dlls.push("libgcc_s_seh-1.dll"); + } + } else if target.ends_with("windows-gnullvm") { + rustc_dlls.push("libunwind.dll"); + } else { + panic!("Vendoring of runtime DLLs for `{target}` is not supported`"); + } + // FIXME(#144656): Remove this whole `let ...` + let bin_path = if target.ends_with("windows-gnullvm") && builder.host_target != target { + bin_path + .into_iter() + .chain(libs_path.iter().map(|path| path.with_file_name("bin"))) + .collect() + } else { + bin_path + }; + let rustc_dlls = find_files(&rustc_dlls, &bin_path); + + // Copy runtime dlls next to rustc.exe + let rust_bin_dir = rust_root.join("bin/"); + fs::create_dir_all(&rust_bin_dir).expect("creating rust_bin_dir failed"); + for src in &rustc_dlls { + builder.copy_link_to_folder(src, &rust_bin_dir); + } + + if builder.config.lld_enabled { + // rust-lld.exe also needs runtime dlls + let rust_target_bin_dir = rust_root.join("lib/rustlib").join(target).join("bin"); + fs::create_dir_all(&rust_target_bin_dir).expect("creating rust_target_bin_dir failed"); + for src in &rustc_dlls { + builder.copy_link_to_folder(src, &rust_target_bin_dir); + } + } +} + +fn get_cc_search_dirs( + target: TargetSelection, + builder: &Builder<'_>, +) -> (Vec<PathBuf>, Vec<PathBuf>) { + //Ask gcc where it keeps its stuff + let mut cmd = command(builder.cc(target)); + cmd.arg("-print-search-dirs"); + let gcc_out = cmd.run_capture_stdout(builder).stdout(); + + let mut bin_path: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect(); + let mut lib_path = Vec::new(); + + for line in gcc_out.lines() { + let idx = line.find(':').unwrap(); + let key = &line[..idx]; + let trim_chars: &[_] = &[' ', '=']; + let value = env::split_paths(line[(idx + 1)..].trim_start_matches(trim_chars)); + + if key == "programs" { + bin_path.extend(value); + } else if key == "libraries" { + lib_path.extend(value); + } + } + (bin_path, lib_path) +} + #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct Mingw { pub host: TargetSelection, @@ -350,11 +379,7 @@ impl Step for Mingw { let mut tarball = Tarball::new(builder, "rust-mingw", &host.triple); tarball.set_product_name("Rust MinGW"); - // The first argument is a "temporary directory" which is just - // thrown away (this contains the runtime DLLs included in the rustc package - // above) and the second argument is where to place all the MinGW components - // (which is what we want). - make_win_dist(&tmpdir(builder), tarball.image_dir(), host, builder); + make_win_dist(tarball.image_dir(), host, builder); Some(tarball.generate()) } @@ -394,17 +419,14 @@ impl Step for Rustc { prepare_image(builder, compiler, tarball.image_dir()); // On MinGW we've got a few runtime DLL dependencies that we need to - // include. The first argument to this script is where to put these DLLs - // (the image we're creating), and the second argument is a junk directory - // to ignore all other MinGW stuff the script creates. - // + // include. // On 32-bit MinGW we're always including a DLL which needs some extra // licenses to distribute. On 64-bit MinGW we don't actually distribute // anything requiring us to distribute a license, but it's likely the // install will *also* include the rust-mingw package, which also needs // licenses, so to be safe we just include it here in all MinGW packages. - if host.ends_with("pc-windows-gnu") && builder.config.dist_include_mingw_linker { - make_win_dist(tarball.image_dir(), &tmpdir(builder), host, builder); + if host.contains("pc-windows-gnu") && builder.config.dist_include_mingw_linker { + runtime_dll_dist(tarball.image_dir(), host, builder); tarball.add_dir(builder.src.join("src/etc/third-party"), "share/doc"); } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index b2056f5cf37..721ba6ca459 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1528,8 +1528,12 @@ impl Step for Libunwind { // FIXME: https://github.com/alexcrichton/cc-rs/issues/545#issuecomment-679242845 let mut count = 0; - for entry in fs::read_dir(&out_dir).unwrap() { - let file = entry.unwrap().path().canonicalize().unwrap(); + let mut files = fs::read_dir(&out_dir) + .unwrap() + .map(|entry| entry.unwrap().path().canonicalize().unwrap()) + .collect::<Vec<_>>(); + files.sort(); + for file in files { if file.is_file() && file.extension() == Some(OsStr::new("o")) { // Object file name without the hash prefix is "Unwind-EHABI", "Unwind-seh" or "libunwind". let base_name = unhashed_basename(&file); diff --git a/src/doc/book b/src/doc/book -Subproject b2d1a0821e12a676b496d61891b8e3d374a8e83 +Subproject 3e9dc46aa563ca0c53ec826c41b05f10c591592 diff --git a/src/doc/reference b/src/doc/reference -Subproject 1f45bd41fa6c17b7c048ed6bfe5f168c4311206 +Subproject 1be151c051a082b542548c62cafbcb055fa8944 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example -Subproject e386be5f44af711854207c11fdd61bb576270b0 +Subproject bd1279cdc9865bfff605e741fb76a0b2f07314a diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index e9f1626f1fd..6ec700b9b4d 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -383b9c447b61641e1f1a3850253944a897a60827 +6bcdcc73bd11568fd85f5a38b58e1eda054ad1cd diff --git a/src/doc/rustc-dev-guide/src/offload/installation.md b/src/doc/rustc-dev-guide/src/offload/installation.md index 1e792de3c8c..b376e962ff6 100644 --- a/src/doc/rustc-dev-guide/src/offload/installation.md +++ b/src/doc/rustc-dev-guide/src/offload/installation.md @@ -8,7 +8,7 @@ First you need to clone and configure the Rust repository: ```bash git clone git@github.com:rust-lang/rust cd rust -./configure --enable-llvm-link-shared --release-channel=nightly --enable-llvm-assertions --enable-offload --enable-enzyme --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs +./configure --enable-llvm-link-shared --release-channel=nightly --enable-llvm-assertions --enable-llvm-offload --enable-llvm-enzyme --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs ``` Afterwards you can build rustc using: diff --git a/src/doc/rustc-dev-guide/src/overview.md b/src/doc/rustc-dev-guide/src/overview.md index 12b76828b5c..378d8c4453f 100644 --- a/src/doc/rustc-dev-guide/src/overview.md +++ b/src/doc/rustc-dev-guide/src/overview.md @@ -321,6 +321,10 @@ the name `'tcx`, which means that something is tied to the lifetime of the [`TyCtxt`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html +For more information about queries in the compiler, see [the queries chapter][queries]. + +[queries]: ./query.md + ### `ty::Ty` Types are really important in Rust, and they form the core of a lot of compiler diff --git a/src/doc/rustc-dev-guide/src/tests/running.md b/src/doc/rustc-dev-guide/src/tests/running.md index f6e313062cd..317b65f98cd 100644 --- a/src/doc/rustc-dev-guide/src/tests/running.md +++ b/src/doc/rustc-dev-guide/src/tests/running.md @@ -342,7 +342,6 @@ coordinate running tests (see [src/bootstrap/src/core/build_steps/test.rs]). > **TODO** > > - Is there any support for using an iOS emulator? -> - It's also unclear to me how the wasm or asm.js tests are run. [armhf-gnu]: https://github.com/rust-lang/rust/tree/master/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile [QEMU]: https://www.qemu.org/ @@ -350,6 +349,28 @@ coordinate running tests (see [src/bootstrap/src/core/build_steps/test.rs]). [remote-test-server]: https://github.com/rust-lang/rust/tree/master/src/tools/remote-test-server [src/bootstrap/src/core/build_steps/test.rs]: https://github.com/rust-lang/rust/blob/master/src/bootstrap/src/core/build_steps/test.rs +## Testing tests on wasi (wasm32-wasip1) + +Some tests are specific to wasm targets. +To run theste tests, you have to pass `--target wasm32-wasip1` to `x test`. +Additionally, you need the wasi sdk. +Follow the install instructions from the [wasi sdk repository] to get a sysroot on your computer. +On the [wasm32-wasip1 target support page] a minimum version is specified that your sdk must be able to build. +Some cmake commands that take a while and give a lot of very concerning c++ warnings... +Then, in `bootstrap.toml`, point to the sysroot like so: + +``` +[target.wasm32-wasip1] +wasi-root = "<wasi-sdk location>/build/sysroot/install/share/wasi-sysroot" +``` + +In my case I git-cloned it next to my rust folder, so it was `../wasi-sdk/build/....` +Now, tests should just run, you don't have to set up anything else. + +[wasi sdk repository]: https://github.com/WebAssembly/wasi-sdk +[wasm32-wasip1 target support page]: https://github.com/rust-lang/rust/blob/master/src/doc/rustc/src/platform-support/wasm32-wasip1.md#building-the-target. + + ## Running rustc_codegen_gcc tests First thing to know is that it only supports linux x86_64 at the moment. We will diff --git a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md index 106ec562bfc..36598982481 100644 --- a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md +++ b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md @@ -10,6 +10,46 @@ platform. [@RDambrosio016](https://github.com/RDambrosio016) [@kjetilkjeka](https://github.com/kjetilkjeka) +## Requirements + +This target is `no_std` and will typically be built with crate-type `cdylib` and `-C linker-flavor=llbc`, which generates PTX. +The necessary components for this workflow are: + +- `rustup toolchain add nightly` +- `rustup component add llvm-tools --toolchain nightly` +- `rustup component add llvm-bitcode-linker --toolchain nightly` + +There are two options for using the core library: + +- `rustup component add rust-src --toolchain nightly` and build using `-Z build-std=core`. +- `rustup target add nvptx64-nvidia-cuda --toolchain nightly` + +### Target and features + +It is generally necessary to specify the target, such as `-C target-cpu=sm_89`, because the default is very old. This implies two target features: `sm_89` and `ptx78` (and all preceding features within `sm_*` and `ptx*`). Rust will default to using the oldest PTX version that supports the target processor (see [this table](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#release-notes-ptx-release-history)), which maximizes driver compatibility. +One can use `-C target-feature=+ptx80` to choose a later PTX version without changing the target (the default in this case, `ptx78`, requires CUDA driver version 11.8, while `ptx80` would require driver version 12.0). +Later PTX versions may allow more efficient code generation. + +Although Rust follows LLVM in representing `ptx*` and `sm_*` as target features, they should be thought of as having crate granularity, set via (either via `-Ctarget-cpu` and optionally `-Ctarget-feature`). +While the compiler accepts `#[target_feature(enable = "ptx80", enable = "sm_89")]`, it is not supported, may not behave as intended, and may become erroneous in the future. + +## Building Rust kernels + +A `no_std` crate containing one or more functions with `extern "ptx-kernel"` can be compiled to PTX using a command like the following. + +```console +$ RUSTFLAGS='-Ctarget-cpu=sm_89' cargo +nightly rustc --target=nvptx64-nvidia-cuda -Zbuild-std=core --crate-type=cdylib -- -Clinker-flavor=llbc -Zunstable-options +``` + +Intrinsics in `core::arch::nvptx` may use `#[cfg(target_feature = "...")]`, thus it's necessary to use `-Zbuild-std=core` with appropriate `RUSTFLAGS`. The following components are needed for this workflow: + +```console +$ rustup component add rust-src --toolchain nightly +$ rustup component add llvm-tools --toolchain nightly +$ rustup component add llvm-bitcode-linker --toolchain nightly +``` + + <!-- FIXME: fill this out ## Requirements diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index b5e81460773..e0e09ac6835 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -3,20 +3,16 @@ mod atomic; mod simd; -use std::ops::Neg; - use rand::Rng; use rustc_abi::Size; -use rustc_apfloat::ieee::{IeeeFloat, Semantics}; use rustc_apfloat::{self, Float, Round}; use rustc_middle::mir; -use rustc_middle::ty::{self, FloatTy, ScalarInt}; +use rustc_middle::ty::{self, FloatTy}; use rustc_span::{Symbol, sym}; use self::atomic::EvalContextExt as _; use self::helpers::{ToHost, ToSoft}; use self::simd::EvalContextExt as _; -use crate::math::{IeeeExt, apply_random_float_error_ulp}; use crate::*; /// Check that the number of args is what we expect. @@ -209,7 +205,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [f] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; - let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { + let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { // Using host floats (but it's fine, these operations do not have // guaranteed precision). let host = f.to_host(); @@ -227,7 +223,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Apply a relative error of 4ULP to introduce some non-determinism // simulating imprecise implementations and optimizations. - let res = apply_random_float_error_ulp( + let res = math::apply_random_float_error_ulp( this, res, 2, // log2(4) @@ -235,7 +231,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Clamp the result to the guaranteed range of this function according to the C standard, // if any. - clamp_float_value(intrinsic_name, res) + math::clamp_float_value(intrinsic_name, res) }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; @@ -253,7 +249,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [f] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f64()?; - let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { + let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { // Using host floats (but it's fine, these operations do not have // guaranteed precision). let host = f.to_host(); @@ -271,7 +267,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Apply a relative error of 4ULP to introduce some non-determinism // simulating imprecise implementations and optimizations. - let res = apply_random_float_error_ulp( + let res = math::apply_random_float_error_ulp( this, res, 2, // log2(4) @@ -279,7 +275,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Clamp the result to the guaranteed range of this function according to the C standard, // if any. - clamp_float_value(intrinsic_name, res) + math::clamp_float_value(intrinsic_name, res) }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; @@ -330,16 +326,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f1 = this.read_scalar(f1)?.to_f32()?; let f2 = this.read_scalar(f2)?.to_f32()?; - let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f1.to_host().powf(f2.to_host()).to_soft(); + let res = + math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f1.to_host().powf(f2.to_host()).to_soft(); - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - apply_random_float_error_ulp( - this, res, 2, // log2(4) - ) - }); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + math::apply_random_float_error_ulp( + this, res, 2, // log2(4) + ) + }); let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } @@ -348,16 +345,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f1 = this.read_scalar(f1)?.to_f64()?; let f2 = this.read_scalar(f2)?.to_f64()?; - let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f1.to_host().powf(f2.to_host()).to_soft(); + let res = + math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f1.to_host().powf(f2.to_host()).to_soft(); - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - apply_random_float_error_ulp( - this, res, 2, // log2(4) - ) - }); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + math::apply_random_float_error_ulp( + this, res, 2, // log2(4) + ) + }); let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } @@ -367,13 +365,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f = this.read_scalar(f)?.to_f32()?; let i = this.read_scalar(i)?.to_i32()?; - let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| { + let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| { // Using host floats (but it's fine, this operation does not have guaranteed precision). let res = f.to_host().powi(i).to_soft(); // Apply a relative error of 4ULP to introduce some non-determinism // simulating imprecise implementations and optimizations. - apply_random_float_error_ulp( + math::apply_random_float_error_ulp( this, res, 2, // log2(4) ) }); @@ -385,13 +383,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let f = this.read_scalar(f)?.to_f64()?; let i = this.read_scalar(i)?.to_i32()?; - let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| { + let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| { // Using host floats (but it's fine, this operation does not have guaranteed precision). let res = f.to_host().powi(i).to_soft(); // Apply a relative error of 4ULP to introduce some non-determinism // simulating imprecise implementations and optimizations. - apply_random_float_error_ulp( + math::apply_random_float_error_ulp( this, res, 2, // log2(4) ) }); @@ -448,7 +446,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Apply a relative error of 4ULP to simulate non-deterministic precision loss // due to optimizations. - let res = apply_random_float_error_to_imm(this, res, 2 /* log2(4) */)?; + let res = math::apply_random_float_error_to_imm(this, res, 2 /* log2(4) */)?; this.write_immediate(*res, dest)?; } @@ -485,133 +483,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(EmulateItemResult::NeedsReturn) } } - -/// Applies a random ULP floating point error to `val` and returns the new value. -/// So if you want an X ULP error, `ulp_exponent` should be log2(X). -/// -/// Will fail if `val` is not a floating point number. -fn apply_random_float_error_to_imm<'tcx>( - ecx: &mut MiriInterpCx<'tcx>, - val: ImmTy<'tcx>, - ulp_exponent: u32, -) -> InterpResult<'tcx, ImmTy<'tcx>> { - let scalar = val.to_scalar_int()?; - let res: ScalarInt = match val.layout.ty.kind() { - ty::Float(FloatTy::F16) => - apply_random_float_error_ulp(ecx, scalar.to_f16(), ulp_exponent).into(), - ty::Float(FloatTy::F32) => - apply_random_float_error_ulp(ecx, scalar.to_f32(), ulp_exponent).into(), - ty::Float(FloatTy::F64) => - apply_random_float_error_ulp(ecx, scalar.to_f64(), ulp_exponent).into(), - ty::Float(FloatTy::F128) => - apply_random_float_error_ulp(ecx, scalar.to_f128(), ulp_exponent).into(), - _ => bug!("intrinsic called with non-float input type"), - }; - - interp_ok(ImmTy::from_scalar_int(res, val.layout)) -} - -/// For the intrinsics: -/// - sinf32, sinf64 -/// - cosf32, cosf64 -/// - expf32, expf64, exp2f32, exp2f64 -/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64 -/// - powf32, powf64 -/// -/// # Return -/// -/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard -/// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error -/// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying -/// implementation. Returns `None` if no specific value is guaranteed. -/// -/// # Note -/// -/// For `powf*` operations of the form: -/// -/// - `(SNaN)^(±0)` -/// - `1^(SNaN)` -/// -/// The result is implementation-defined: -/// - musl returns for both `1.0` -/// - glibc returns for both `NaN` -/// -/// This discrepancy exists because SNaN handling is not consistently defined across platforms, -/// and the C standard leaves behavior for SNaNs unspecified. -/// -/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically. -fn fixed_float_value<S: Semantics>( - ecx: &mut MiriInterpCx<'_>, - intrinsic_name: &str, - args: &[IeeeFloat<S>], -) -> Option<IeeeFloat<S>> { - let one = IeeeFloat::<S>::one(); - Some(match (intrinsic_name, args) { - // cos(+- 0) = 1 - ("cosf32" | "cosf64", [input]) if input.is_zero() => one, - - // e^0 = 1 - ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one, - - // (-1)^(±INF) = 1 - ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one, - - // 1^y = 1 for any y, even a NaN - ("powf32" | "powf64", [base, exp]) if *base == one => { - let rng = ecx.machine.rng.get_mut(); - // SNaN exponents get special treatment: they might return 1, or a NaN. - let return_nan = exp.is_signaling() && ecx.machine.float_nondet && rng.random(); - // Handle both the musl and glibc cases non-deterministically. - if return_nan { ecx.generate_nan(args) } else { one } - } - - // x^(±0) = 1 for any x, even a NaN - ("powf32" | "powf64", [base, exp]) if exp.is_zero() => { - let rng = ecx.machine.rng.get_mut(); - // SNaN bases get special treatment: they might return 1, or a NaN. - let return_nan = base.is_signaling() && ecx.machine.float_nondet && rng.random(); - // Handle both the musl and glibc cases non-deterministically. - if return_nan { ecx.generate_nan(args) } else { one } - } - - // There are a lot of cases for fixed outputs according to the C Standard, but these are - // mainly INF or zero which are not affected by the applied error. - _ => return None, - }) -} - -/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the -/// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`. -fn fixed_powi_float_value<S: Semantics>( - ecx: &mut MiriInterpCx<'_>, - base: IeeeFloat<S>, - exp: i32, -) -> Option<IeeeFloat<S>> { - Some(match exp { - 0 => { - let one = IeeeFloat::<S>::one(); - let rng = ecx.machine.rng.get_mut(); - let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling(); - // For SNaN treatment, we are consistent with `powf`above. - // (We wouldn't have two, unlike powf all implementations seem to agree for powi, - // but for now we are maximally conservative.) - if return_nan { ecx.generate_nan(&[base]) } else { one } - } - - _ => return None, - }) -} - -/// Given an floating-point operation and a floating-point value, clamps the result to the output -/// range of the given operation. -fn clamp_float_value<S: Semantics>(intrinsic_name: &str, val: IeeeFloat<S>) -> IeeeFloat<S> { - match intrinsic_name { - // sin and cos: [-1, 1] - "sinf32" | "cosf32" | "sinf64" | "cosf64" => - val.clamp(IeeeFloat::<S>::one().neg(), IeeeFloat::<S>::one()), - // exp: [0, +INF] - "expf32" | "exp2f32" | "expf64" | "exp2f64" => - IeeeFloat::<S>::maximum(val, IeeeFloat::<S>::ZERO), - _ => val, - } -} diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 2b92c25a424..9272cd29788 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -1,3 +1,4 @@ +#![cfg_attr(bootstrap, feature(strict_overflow_ops))] #![feature(abort_unwind)] #![feature(cfg_select)] #![feature(rustc_private)] @@ -11,13 +12,14 @@ #![feature(variant_count)] #![feature(yeet_expr)] #![feature(nonzero_ops)] -#![feature(strict_overflow_ops)] #![feature(pointer_is_aligned_to)] #![feature(ptr_metadata)] #![feature(unqualified_local_imports)] #![feature(derive_coerce_pointee)] #![feature(arbitrary_self_types)] #![feature(iter_advance_by)] +#![feature(f16)] +#![feature(f128)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index e9e5a1070c9..3eee6b82e8c 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -1,6 +1,9 @@ +use std::ops::Neg; +use std::{f16, f32, f64, f128}; + use rand::Rng as _; use rustc_apfloat::Float as _; -use rustc_apfloat::ieee::IeeeFloat; +use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, Semantics, SingleS}; use rustc_middle::ty::{self, FloatTy, ScalarInt}; use crate::*; @@ -50,29 +53,236 @@ pub(crate) fn apply_random_float_error_ulp<F: rustc_apfloat::Float>( apply_random_float_error(ecx, val, err_scale) } -/// Applies a random 16ULP floating point error to `val` and returns the new value. +/// Applies a random ULP floating point error to `val` and returns the new value. +/// So if you want an X ULP error, `ulp_exponent` should be log2(X). +/// /// Will fail if `val` is not a floating point number. pub(crate) fn apply_random_float_error_to_imm<'tcx>( ecx: &mut MiriInterpCx<'tcx>, val: ImmTy<'tcx>, ulp_exponent: u32, ) -> InterpResult<'tcx, ImmTy<'tcx>> { + let this = ecx.eval_context_mut(); let scalar = val.to_scalar_int()?; let res: ScalarInt = match val.layout.ty.kind() { ty::Float(FloatTy::F16) => - apply_random_float_error_ulp(ecx, scalar.to_f16(), ulp_exponent).into(), + apply_random_float_error_ulp(this, scalar.to_f16(), ulp_exponent).into(), ty::Float(FloatTy::F32) => - apply_random_float_error_ulp(ecx, scalar.to_f32(), ulp_exponent).into(), + apply_random_float_error_ulp(this, scalar.to_f32(), ulp_exponent).into(), ty::Float(FloatTy::F64) => - apply_random_float_error_ulp(ecx, scalar.to_f64(), ulp_exponent).into(), + apply_random_float_error_ulp(this, scalar.to_f64(), ulp_exponent).into(), ty::Float(FloatTy::F128) => - apply_random_float_error_ulp(ecx, scalar.to_f128(), ulp_exponent).into(), + apply_random_float_error_ulp(this, scalar.to_f128(), ulp_exponent).into(), _ => bug!("intrinsic called with non-float input type"), }; interp_ok(ImmTy::from_scalar_int(res, val.layout)) } +/// Given a floating-point operation and a floating-point value, clamps the result to the output +/// range of the given operation according to the C standard, if any. +pub(crate) fn clamp_float_value<S: Semantics>( + intrinsic_name: &str, + val: IeeeFloat<S>, +) -> IeeeFloat<S> +where + IeeeFloat<S>: IeeeExt, +{ + let zero = IeeeFloat::<S>::ZERO; + let one = IeeeFloat::<S>::one(); + let two = IeeeFloat::<S>::two(); + let pi = IeeeFloat::<S>::pi(); + let pi_over_2 = (pi / two).value; + + match intrinsic_name { + // sin, cos, tanh: [-1, 1] + #[rustfmt::skip] + | "sinf32" + | "sinf64" + | "cosf32" + | "cosf64" + | "tanhf" + | "tanh" + => val.clamp(one.neg(), one), + + // exp: [0, +INF) + "expf32" | "exp2f32" | "expf64" | "exp2f64" => val.maximum(zero), + + // cosh: [1, +INF) + "coshf" | "cosh" => val.maximum(one), + + // acos: [0, π] + "acosf" | "acos" => val.clamp(zero, pi), + + // asin: [-π, +π] + "asinf" | "asin" => val.clamp(pi.neg(), pi), + + // atan: (-π/2, +π/2) + "atanf" | "atan" => val.clamp(pi_over_2.neg(), pi_over_2), + + // erfc: (-1, 1) + "erff" | "erf" => val.clamp(one.neg(), one), + + // erfc: (0, 2) + "erfcf" | "erfc" => val.clamp(zero, two), + + // atan2(y, x): arctan(y/x) in [−π, +π] + "atan2f" | "atan2" => val.clamp(pi.neg(), pi), + + _ => val, + } +} + +/// For the intrinsics: +/// - sinf32, sinf64, sinhf, sinh +/// - cosf32, cosf64, coshf, cosh +/// - tanhf, tanh, atanf, atan, atan2f, atan2 +/// - expf32, expf64, exp2f32, exp2f64 +/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64 +/// - powf32, powf64 +/// - erff, erf, erfcf, erfc +/// - hypotf, hypot +/// +/// # Return +/// +/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard +/// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error +/// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying +/// implementation. Returns `None` if no specific value is guaranteed. +/// +/// # Note +/// +/// For `powf*` operations of the form: +/// +/// - `(SNaN)^(±0)` +/// - `1^(SNaN)` +/// +/// The result is implementation-defined: +/// - musl returns for both `1.0` +/// - glibc returns for both `NaN` +/// +/// This discrepancy exists because SNaN handling is not consistently defined across platforms, +/// and the C standard leaves behavior for SNaNs unspecified. +/// +/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically. +pub(crate) fn fixed_float_value<S: Semantics>( + ecx: &mut MiriInterpCx<'_>, + intrinsic_name: &str, + args: &[IeeeFloat<S>], +) -> Option<IeeeFloat<S>> +where + IeeeFloat<S>: IeeeExt, +{ + let this = ecx.eval_context_mut(); + let one = IeeeFloat::<S>::one(); + let two = IeeeFloat::<S>::two(); + let three = IeeeFloat::<S>::three(); + let pi = IeeeFloat::<S>::pi(); + let pi_over_2 = (pi / two).value; + let pi_over_4 = (pi_over_2 / two).value; + + Some(match (intrinsic_name, args) { + // cos(±0) and cosh(±0)= 1 + ("cosf32" | "cosf64" | "coshf" | "cosh", [input]) if input.is_zero() => one, + + // e^0 = 1 + ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one, + + // tanh(±INF) = ±1 + ("tanhf" | "tanh", [input]) if input.is_infinite() => one.copy_sign(*input), + + // atan(±INF) = ±π/2 + ("atanf" | "atan", [input]) if input.is_infinite() => pi_over_2.copy_sign(*input), + + // erf(±INF) = ±1 + ("erff" | "erf", [input]) if input.is_infinite() => one.copy_sign(*input), + + // erfc(-INF) = 2 + ("erfcf" | "erfc", [input]) if input.is_neg_infinity() => (one + one).value, + + // hypot(x, ±0) = abs(x), if x is not a NaN. + ("_hypotf" | "hypotf" | "_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() => + x.abs(), + + // atan2(±0,−0) = ±π. + // atan2(±0, y) = ±π for y < 0. + // Must check for non NaN because `y.is_negative()` also applies to NaN. + ("atan2f" | "atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) => + pi.copy_sign(*x), + + // atan2(±x,−∞) = ±π for finite x > 0. + ("atan2f" | "atan2", [x, y]) + if (!x.is_zero() && !x.is_infinite()) && y.is_neg_infinity() => + pi.copy_sign(*x), + + // atan2(x, ±0) = −π/2 for x < 0. + // atan2(x, ±0) = π/2 for x > 0. + ("atan2f" | "atan2", [x, y]) if !x.is_zero() && y.is_zero() => pi_over_2.copy_sign(*x), + + //atan2(±∞, −∞) = ±3π/4 + ("atan2f" | "atan2", [x, y]) if x.is_infinite() && y.is_neg_infinity() => + (pi_over_4 * three).value.copy_sign(*x), + + //atan2(±∞, +∞) = ±π/4 + ("atan2f" | "atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() => + pi_over_4.copy_sign(*x), + + // atan2(±∞, y) returns ±π/2 for finite y. + ("atan2f" | "atan2", [x, y]) if x.is_infinite() && (!y.is_infinite() && !y.is_nan()) => + pi_over_2.copy_sign(*x), + + // (-1)^(±INF) = 1 + ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one, + + // 1^y = 1 for any y, even a NaN + ("powf32" | "powf64", [base, exp]) if *base == one => { + let rng = this.machine.rng.get_mut(); + // SNaN exponents get special treatment: they might return 1, or a NaN. + let return_nan = exp.is_signaling() && this.machine.float_nondet && rng.random(); + // Handle both the musl and glibc cases non-deterministically. + if return_nan { this.generate_nan(args) } else { one } + } + + // x^(±0) = 1 for any x, even a NaN + ("powf32" | "powf64", [base, exp]) if exp.is_zero() => { + let rng = this.machine.rng.get_mut(); + // SNaN bases get special treatment: they might return 1, or a NaN. + let return_nan = base.is_signaling() && this.machine.float_nondet && rng.random(); + // Handle both the musl and glibc cases non-deterministically. + if return_nan { this.generate_nan(args) } else { one } + } + + // There are a lot of cases for fixed outputs according to the C Standard, but these are + // mainly INF or zero which are not affected by the applied error. + _ => return None, + }) +} + +/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the +/// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`. +pub(crate) fn fixed_powi_value<S: Semantics>( + ecx: &mut MiriInterpCx<'_>, + base: IeeeFloat<S>, + exp: i32, +) -> Option<IeeeFloat<S>> +where + IeeeFloat<S>: IeeeExt, +{ + match exp { + 0 => { + let one = IeeeFloat::<S>::one(); + let rng = ecx.machine.rng.get_mut(); + let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling(); + // For SNaN treatment, we are consistent with `powf`above. + // (We wouldn't have two, unlike powf all implementations seem to agree for powi, + // but for now we are maximally conservative.) + Some(if return_nan { ecx.generate_nan(&[base]) } else { one }) + } + + _ => return None, + } +} + pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFloat<S> { match x.category() { // preserve zero sign @@ -155,19 +365,49 @@ pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFl } } -/// Extend functionality of rustc_apfloat softfloats +/// Extend functionality of `rustc_apfloat` softfloats for IEEE float types. pub trait IeeeExt: rustc_apfloat::Float { + // Some values we use: + #[inline] fn one() -> Self { Self::from_u128(1).value } #[inline] + fn two() -> Self { + Self::from_u128(2).value + } + + #[inline] + fn three() -> Self { + Self::from_u128(3).value + } + + fn pi() -> Self; + + #[inline] fn clamp(self, min: Self, max: Self) -> Self { self.maximum(min).minimum(max) } } -impl<S: rustc_apfloat::ieee::Semantics> IeeeExt for IeeeFloat<S> {} + +macro_rules! impl_ieee_pi { + ($float_ty:ident, $semantic:ty) => { + impl IeeeExt for IeeeFloat<$semantic> { + #[inline] + fn pi() -> Self { + // We take the value from the standard library as the most reasonable source for an exact π here. + Self::from_bits($float_ty::consts::PI.to_bits() as _) + } + } + }; +} + +impl_ieee_pi!(f16, HalfS); +impl_ieee_pi!(f32, SingleS); +impl_ieee_pi!(f64, DoubleS); +impl_ieee_pi!(f128, QuadS); #[cfg(test)] mod tests { diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 21545b68029..bf4db2de2d7 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -17,6 +17,7 @@ use rustc_target::callconv::FnAbi; use self::helpers::{ToHost, ToSoft}; use super::alloc::EvalContextExt as _; use super::backtrace::EvalContextExt as _; +use crate::helpers::EvalContextExt as _; use crate::*; /// Type of dynamic symbols (for `dlsym` et al) @@ -779,33 +780,38 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { => { let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f = this.read_scalar(f)?.to_f32()?; - // Using host floats (but it's fine, these operations do not have guaranteed precision). - let f_host = f.to_host(); - let res = match link_name.as_str() { - "cbrtf" => f_host.cbrt(), - "coshf" => f_host.cosh(), - "sinhf" => f_host.sinh(), - "tanf" => f_host.tan(), - "tanhf" => f_host.tanh(), - "acosf" => f_host.acos(), - "asinf" => f_host.asin(), - "atanf" => f_host.atan(), - "log1pf" => f_host.ln_1p(), - "expm1f" => f_host.exp_m1(), - "tgammaf" => f_host.gamma(), - "erff" => f_host.erf(), - "erfcf" => f_host.erfc(), - _ => bug!(), - }; - let res = res.to_soft(); - // Apply a relative error of 16ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = math::apply_random_float_error_ulp( - // this, - // res, - // 4, // log2(16) - // ); + + let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let f_host = f.to_host(); + let res = match link_name.as_str() { + "cbrtf" => f_host.cbrt(), + "coshf" => f_host.cosh(), + "sinhf" => f_host.sinh(), + "tanf" => f_host.tan(), + "tanhf" => f_host.tanh(), + "acosf" => f_host.acos(), + "asinf" => f_host.asin(), + "atanf" => f_host.atan(), + "log1pf" => f_host.ln_1p(), + "expm1f" => f_host.exp_m1(), + "tgammaf" => f_host.gamma(), + "erff" => f_host.erf(), + "erfcf" => f_host.erfc(), + _ => bug!(), + }; + let res = res.to_soft(); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp( + this, res, 2, // log2(4) + ); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } @@ -818,24 +824,28 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f1 = this.read_scalar(f1)?.to_f32()?; let f2 = this.read_scalar(f2)?.to_f32()?; - // underscore case for windows, here and below - // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) - // Using host floats (but it's fine, these operations do not have guaranteed precision). - let res = match link_name.as_str() { - "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(), - "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(), - #[allow(deprecated)] - "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(), - _ => bug!(), - }; - // Apply a relative error of 16ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = math::apply_random_float_error_ulp( - // this, - // res, - // 4, // log2(16) - // ); + + let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]).unwrap_or_else(|| { + let res = match link_name.as_str() { + // underscore case for windows, here and below + // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) + // Using host floats (but it's fine, these operations do not have guaranteed precision). + "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(), + "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(), + #[allow(deprecated)] + "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(), + _ => bug!(), + }; + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp( + this, res, 2, // log2(4) + ); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } @@ -856,33 +866,38 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { => { let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f = this.read_scalar(f)?.to_f64()?; - // Using host floats (but it's fine, these operations do not have guaranteed precision). - let f_host = f.to_host(); - let res = match link_name.as_str() { - "cbrt" => f_host.cbrt(), - "cosh" => f_host.cosh(), - "sinh" => f_host.sinh(), - "tan" => f_host.tan(), - "tanh" => f_host.tanh(), - "acos" => f_host.acos(), - "asin" => f_host.asin(), - "atan" => f_host.atan(), - "log1p" => f_host.ln_1p(), - "expm1" => f_host.exp_m1(), - "tgamma" => f_host.gamma(), - "erf" => f_host.erf(), - "erfc" => f_host.erfc(), - _ => bug!(), - }; - let res = res.to_soft(); - // Apply a relative error of 16ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = math::apply_random_float_error_ulp( - // this, - // res.to_soft(), - // 4, // log2(16) - // ); + + let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let f_host = f.to_host(); + let res = match link_name.as_str() { + "cbrt" => f_host.cbrt(), + "cosh" => f_host.cosh(), + "sinh" => f_host.sinh(), + "tan" => f_host.tan(), + "tanh" => f_host.tanh(), + "acos" => f_host.acos(), + "asin" => f_host.asin(), + "atan" => f_host.atan(), + "log1p" => f_host.ln_1p(), + "expm1" => f_host.exp_m1(), + "tgamma" => f_host.gamma(), + "erf" => f_host.erf(), + "erfc" => f_host.erfc(), + _ => bug!(), + }; + let res = res.to_soft(); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp( + this, res, 2, // log2(4) + ); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } @@ -895,24 +910,28 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f1 = this.read_scalar(f1)?.to_f64()?; let f2 = this.read_scalar(f2)?.to_f64()?; - // underscore case for windows, here and below - // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) - // Using host floats (but it's fine, these operations do not have guaranteed precision). - let res = match link_name.as_str() { - "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(), - "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(), - #[allow(deprecated)] - "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(), - _ => bug!(), - }; - // Apply a relative error of 16ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = math::apply_random_float_error_ulp( - // this, - // res, - // 4, // log2(16) - // ); + + let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]).unwrap_or_else(|| { + let res = match link_name.as_str() { + // underscore case for windows, here and below + // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) + // Using host floats (but it's fine, these operations do not have guaranteed precision). + "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(), + "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(), + #[allow(deprecated)] + "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(), + _ => bug!(), + }; + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp( + this, res, 2, // log2(4) + ); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } @@ -938,11 +957,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Using host floats (but it's fine, these operations do not have guaranteed precision). let (res, sign) = x.to_host().ln_gamma(); this.write_int(sign, &signp)?; + let res = res.to_soft(); - // Apply a relative error of 16ULP to introduce some non-determinism + // Apply a relative error of 4ULP to introduce some non-determinism // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = math::apply_random_float_error_ulp(this, res, 4 /* log2(16) */); + let res = math::apply_random_float_error_ulp(this, res, 2 /* log2(4) */); + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + let res = math::clamp_float_value(link_name.as_str(), res); let res = this.adjust_nan(res, &[x]); this.write_scalar(res, dest)?; } @@ -954,11 +976,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Using host floats (but it's fine, these operations do not have guaranteed precision). let (res, sign) = x.to_host().ln_gamma(); this.write_int(sign, &signp)?; + let res = res.to_soft(); - // Apply a relative error of 16ULP to introduce some non-determinism + // Apply a relative error of 4ULP to introduce some non-determinism // simulating imprecise implementations and optimizations. - // FIXME: temporarily disabled as it breaks std tests. - // let res = math::apply_random_float_error_ulp(this, res, 4 /* log2(16) */); + let res = math::apply_random_float_error_ulp(this, res, 2 /* log2(4) */); + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + let res = math::clamp_float_value(link_name.as_str(), res); let res = this.adjust_nan(res, &[x]); this.write_scalar(res, dest)?; } diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index fe7316c6680..96590b4bf2b 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -1088,6 +1088,8 @@ pub fn libm() { assert_approx_eq!(1f32.exp_m1(), f32::consts::E - 1.0); assert_approx_eq!(1f64.exp_m1(), f64::consts::E - 1.0); + assert_approx_eq!(f32::NEG_INFINITY.exp_m1(), -1.0); + assert_approx_eq!(f64::NEG_INFINITY.exp_m1(), -1.0); assert_approx_eq!(10f32.exp2(), 1024f32); assert_approx_eq!(50f64.exp2(), 1125899906842624f64); @@ -1123,6 +1125,7 @@ pub fn libm() { assert_eq!(ldexp(0.65f64, 3i32), 5.2f64); assert_eq!(ldexp(1.42, 0xFFFF), f64::INFINITY); assert_eq!(ldexp(1.42, -0xFFFF), 0f64); + assert_eq!(ldexp(42.0, 0), 42.0); // Trigonometric functions. @@ -1131,8 +1134,14 @@ pub fn libm() { assert_approx_eq!((f64::consts::PI / 2f64).sin(), 1f64); assert_approx_eq!(f32::consts::FRAC_PI_6.sin(), 0.5); assert_approx_eq!(f64::consts::FRAC_PI_6.sin(), 0.5); - assert_approx_eq!(f32::consts::FRAC_PI_4.sin().asin(), f32::consts::FRAC_PI_4); - assert_approx_eq!(f64::consts::FRAC_PI_4.sin().asin(), f64::consts::FRAC_PI_4); + // Increase error tolerance from 12ULP to 16ULP because of the extra operation. + assert_approx_eq!(f32::consts::FRAC_PI_4.sin().asin(), f32::consts::FRAC_PI_4, 16); + assert_approx_eq!(f64::consts::FRAC_PI_4.sin().asin(), f64::consts::FRAC_PI_4, 16); + assert_biteq(0.0f32.asin(), 0.0f32, "asin(+0) = +0"); + assert_biteq((-0.0f32).asin(), -0.0, "asin(-0) = -0"); + assert_biteq(0.0f64.asin(), 0.0, "asin(+0) = +0"); + assert_biteq((-0.0f64).asin(), -0.0, "asin(-0) = -0"); + assert_approx_eq!(1.0f32.sinh(), 1.1752012f32); assert_approx_eq!(1.0f64.sinh(), 1.1752011936438014f64); @@ -1159,11 +1168,18 @@ pub fn libm() { assert_approx_eq!((f64::consts::PI * 2f64).cos(), 1f64); assert_approx_eq!(f32::consts::FRAC_PI_3.cos(), 0.5); assert_approx_eq!(f64::consts::FRAC_PI_3.cos(), 0.5); - assert_approx_eq!(f32::consts::FRAC_PI_4.cos().acos(), f32::consts::FRAC_PI_4); - assert_approx_eq!(f64::consts::FRAC_PI_4.cos().acos(), f64::consts::FRAC_PI_4); + // Increase error tolerance from 12ULP to 16ULP because of the extra operation. + assert_approx_eq!(f32::consts::FRAC_PI_4.cos().acos(), f32::consts::FRAC_PI_4, 16); + assert_approx_eq!(f64::consts::FRAC_PI_4.cos().acos(), f64::consts::FRAC_PI_4, 16); + assert_biteq(1.0f32.acos(), 0.0, "acos(1) = 0"); + assert_biteq(1.0f64.acos(), 0.0, "acos(1) = 0"); assert_approx_eq!(1.0f32.cosh(), 1.54308f32); assert_approx_eq!(1.0f64.cosh(), 1.5430806348152437f64); + assert_eq!(0.0f32.cosh(), 1.0); + assert_eq!(0.0f64.cosh(), 1.0); + assert_eq!((-0.0f32).cosh(), 1.0); + assert_eq!((-0.0f64).cosh(), 1.0); assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32); assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64); @@ -1173,6 +1189,47 @@ pub fn libm() { assert_approx_eq!(1.0_f64, 1.0_f64.tan().atan()); assert_approx_eq!(1.0f32.atan2(2.0f32), 0.46364761f32); assert_approx_eq!(1.0f32.atan2(2.0f32), 0.46364761f32); + // C standard defines a bunch of fixed outputs for atan2 + macro_rules! fixed_atan2_cases{ + ($float_type:ident) => {{ + use std::$float_type::consts::{PI, FRAC_PI_2, FRAC_PI_4}; + use $float_type::{INFINITY, NEG_INFINITY}; + + // atan2(±0,−0) = ±π. + assert_eq!($float_type::atan2(0.0, -0.0), PI, "atan2(0,−0) = π"); + assert_eq!($float_type::atan2(-0.0, -0.0), -PI, "atan2(-0,−0) = -π"); + + // atan2(±0, y) = ±π for y < 0. + assert_eq!($float_type::atan2(0.0, -1.0), PI, "atan2(0, y) = π for y < 0."); + assert_eq!($float_type::atan2(-0.0, -1.0), -PI, "atan2(-0, y) = -π for y < 0."); + + // atan2(x, ±0) = −π/2 for x < 0. + assert_eq!($float_type::atan2(-1.0, 0.0), -FRAC_PI_2, "atan2(x, 0) = −π/2 for x < 0"); + assert_eq!($float_type::atan2(-1.0, -0.0), -FRAC_PI_2, "atan2(x, -0) = −π/2 for x < 0"); + + // atan2(x, ±0) = π/2 for x > 0. + assert_eq!($float_type::atan2(1.0, 0.0), FRAC_PI_2, "atan2(x, 0) = π/2 for x > 0."); + assert_eq!($float_type::atan2(1.0, -0.0), FRAC_PI_2, "atan2(x, -0) = π/2 for x > 0."); + + // atan2(±x,−∞) = ±π for finite x > 0. + assert_eq!($float_type::atan2(1.0, NEG_INFINITY), PI, "atan2(x, −∞) = π for finite x > 0"); + assert_eq!($float_type::atan2(-1.0, NEG_INFINITY), -PI, "atan2(-x, −∞) = -π for finite x > 0"); + + // atan2(±∞, y) returns ±π/2 for finite y. + assert_eq!($float_type::atan2(INFINITY, 1.0), FRAC_PI_2, "atan2(+∞, y) returns π/2 for finite y"); + assert_eq!($float_type::atan2(NEG_INFINITY, 1.0), -FRAC_PI_2, "atan2(-∞, y) returns -π/2 for finite y"); + + // atan2(±∞, −∞) = ±3π/4 + assert_eq!($float_type::atan2(INFINITY, NEG_INFINITY), 3.0 * FRAC_PI_4, "atan2(+∞, −∞) = 3π/4"); + assert_eq!($float_type::atan2(NEG_INFINITY, NEG_INFINITY), -3.0 * FRAC_PI_4, "atan2(-∞, −∞) = -3π/4"); + + // atan2(±∞, +∞) = ±π/4 + assert_eq!($float_type::atan2(INFINITY, INFINITY), FRAC_PI_4, "atan2(+∞, +∞) = π/4"); + assert_eq!($float_type::atan2(NEG_INFINITY, INFINITY), -FRAC_PI_4, "atan2(-∞, +∞) = -π/4"); + }} + } + fixed_atan2_cases!(f32); + fixed_atan2_cases!(f64); assert_approx_eq!( 1.0f32.tanh(), @@ -1182,6 +1239,11 @@ pub fn libm() { 1.0f64.tanh(), (1.0 - f64::consts::E.powi(-2)) / (1.0 + f64::consts::E.powi(-2)) ); + assert_eq!(f32::INFINITY.tanh(), 1.0); + assert_eq!(f32::NEG_INFINITY.tanh(), -1.0); + assert_eq!(f64::INFINITY.tanh(), 1.0); + assert_eq!(f64::NEG_INFINITY.tanh(), -1.0); + assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32); assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64); @@ -1202,8 +1264,14 @@ pub fn libm() { assert_approx_eq!(1.0f32.erf(), 0.84270079294971486934122063508260926f32); assert_approx_eq!(1.0f64.erf(), 0.84270079294971486934122063508260926f64); + assert_eq!(f32::INFINITY.erf(), 1.0); + assert_eq!(f64::INFINITY.erf(), 1.0); assert_approx_eq!(1.0f32.erfc(), 0.15729920705028513065877936491739074f32); assert_approx_eq!(1.0f64.erfc(), 0.15729920705028513065877936491739074f64); + assert_eq!(f32::NEG_INFINITY.erfc(), 2.0); + assert_eq!(f64::NEG_INFINITY.erfc(), 2.0); + assert_eq!(f32::INFINITY.erfc(), 0.0); + assert_eq!(f64::INFINITY.erfc(), 0.0); } fn test_fast() { @@ -1413,7 +1481,6 @@ fn test_non_determinism() { } pub fn test_operations_f32(a: f32, b: f32) { test_operations_f!(a, b); - // FIXME: some are temporarily disabled as it breaks std tests. ensure_nondet(|| a.powf(b)); ensure_nondet(|| a.powi(2)); ensure_nondet(|| a.log(b)); @@ -1422,35 +1489,34 @@ fn test_non_determinism() { ensure_nondet(|| f32::consts::E.ln()); ensure_nondet(|| 10f32.log10()); ensure_nondet(|| 8f32.log2()); - // ensure_nondet(|| 1f32.ln_1p()); - // ensure_nondet(|| 27.0f32.cbrt()); - // ensure_nondet(|| 3.0f32.hypot(4.0f32)); + ensure_nondet(|| 1f32.ln_1p()); + ensure_nondet(|| 27.0f32.cbrt()); + ensure_nondet(|| 3.0f32.hypot(4.0f32)); ensure_nondet(|| 1f32.sin()); ensure_nondet(|| 1f32.cos()); // On i686-pc-windows-msvc , these functions are implemented by calling the `f64` version, // which means the little rounding errors Miri introduces are discarded by the cast down to // `f32`. Just skip the test for them. - // if !cfg!(all(target_os = "windows", target_env = "msvc", target_arch = "x86")) { - // ensure_nondet(|| 1.0f32.tan()); - // ensure_nondet(|| 1.0f32.asin()); - // ensure_nondet(|| 5.0f32.acos()); - // ensure_nondet(|| 1.0f32.atan()); - // ensure_nondet(|| 1.0f32.atan2(2.0f32)); - // ensure_nondet(|| 1.0f32.sinh()); - // ensure_nondet(|| 1.0f32.cosh()); - // ensure_nondet(|| 1.0f32.tanh()); - // } - // ensure_nondet(|| 1.0f32.asinh()); - // ensure_nondet(|| 2.0f32.acosh()); - // ensure_nondet(|| 0.5f32.atanh()); - // ensure_nondet(|| 5.0f32.gamma()); - // ensure_nondet(|| 5.0f32.ln_gamma()); - // ensure_nondet(|| 5.0f32.erf()); - // ensure_nondet(|| 5.0f32.erfc()); + if !cfg!(all(target_os = "windows", target_env = "msvc", target_arch = "x86")) { + ensure_nondet(|| 1.0f32.tan()); + ensure_nondet(|| 1.0f32.asin()); + ensure_nondet(|| 5.0f32.acos()); + ensure_nondet(|| 1.0f32.atan()); + ensure_nondet(|| 1.0f32.atan2(2.0f32)); + ensure_nondet(|| 1.0f32.sinh()); + ensure_nondet(|| 1.0f32.cosh()); + ensure_nondet(|| 1.0f32.tanh()); + } + ensure_nondet(|| 1.0f32.asinh()); + ensure_nondet(|| 2.0f32.acosh()); + ensure_nondet(|| 0.5f32.atanh()); + ensure_nondet(|| 5.0f32.gamma()); + ensure_nondet(|| 5.0f32.ln_gamma()); + ensure_nondet(|| 5.0f32.erf()); + ensure_nondet(|| 5.0f32.erfc()); } pub fn test_operations_f64(a: f64, b: f64) { test_operations_f!(a, b); - // FIXME: some are temporarily disabled as it breaks std tests. ensure_nondet(|| a.powf(b)); ensure_nondet(|| a.powi(2)); ensure_nondet(|| a.log(b)); @@ -1459,26 +1525,26 @@ fn test_non_determinism() { ensure_nondet(|| 3f64.ln()); ensure_nondet(|| f64::consts::E.log10()); ensure_nondet(|| f64::consts::E.log2()); - // ensure_nondet(|| 1f64.ln_1p()); - // ensure_nondet(|| 27.0f64.cbrt()); - // ensure_nondet(|| 3.0f64.hypot(4.0f64)); + ensure_nondet(|| 1f64.ln_1p()); + ensure_nondet(|| 27.0f64.cbrt()); + ensure_nondet(|| 3.0f64.hypot(4.0f64)); ensure_nondet(|| 1f64.sin()); ensure_nondet(|| 1f64.cos()); - // ensure_nondet(|| 1.0f64.tan()); - // ensure_nondet(|| 1.0f64.asin()); - // ensure_nondet(|| 5.0f64.acos()); - // ensure_nondet(|| 1.0f64.atan()); - // ensure_nondet(|| 1.0f64.atan2(2.0f64)); - // ensure_nondet(|| 1.0f64.sinh()); - // ensure_nondet(|| 1.0f64.cosh()); - // ensure_nondet(|| 1.0f64.tanh()); - // ensure_nondet(|| 1.0f64.asinh()); - // ensure_nondet(|| 3.0f64.acosh()); - // ensure_nondet(|| 0.5f64.atanh()); - // ensure_nondet(|| 5.0f64.gamma()); - // ensure_nondet(|| 5.0f64.ln_gamma()); - // ensure_nondet(|| 5.0f64.erf()); - // ensure_nondet(|| 5.0f64.erfc()); + ensure_nondet(|| 1.0f64.tan()); + ensure_nondet(|| 1.0f64.asin()); + ensure_nondet(|| 5.0f64.acos()); + ensure_nondet(|| 1.0f64.atan()); + ensure_nondet(|| 1.0f64.atan2(2.0f64)); + ensure_nondet(|| 1.0f64.sinh()); + ensure_nondet(|| 1.0f64.cosh()); + ensure_nondet(|| 1.0f64.tanh()); + ensure_nondet(|| 1.0f64.asinh()); + ensure_nondet(|| 3.0f64.acosh()); + ensure_nondet(|| 0.5f64.atanh()); + ensure_nondet(|| 5.0f64.gamma()); + ensure_nondet(|| 5.0f64.ln_gamma()); + ensure_nondet(|| 5.0f64.erf()); + ensure_nondet(|| 5.0f64.erfc()); } pub fn test_operations_f128(a: f128, b: f128) { test_operations_f!(a, b); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index f9eb44d03ab..14bc380586a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -10706,20 +10706,6 @@ The tracking issue for this feature is: [#77998] deny_since: None, }, Lint { - label: "strict_overflow_ops", - description: r##"# `strict_overflow_ops` - -The tracking issue for this feature is: [#118260] - -[#118260]: https://github.com/rust-lang/rust/issues/118260 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "strict_provenance_atomic_ptr", description: r##"# `strict_provenance_atomic_ptr` |
