From 897c4ee4066c71de11a918d4c1fd7f00b8f77e1d Mon Sep 17 00:00:00 2001 From: kilavvy <140459108+kilavvy@users.noreply.github.com> Date: Sun, 29 Jun 2025 10:01:27 +0200 Subject: Update README.md - Update ui.md - Update type-alias-impl-trait.md - Update README.md --- src/tools/rust-installer/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/tools') diff --git a/src/tools/rust-installer/README.md b/src/tools/rust-installer/README.md index 99d8e5ca4cf..505ffe4093f 100644 --- a/src/tools/rust-installer/README.md +++ b/src/tools/rust-installer/README.md @@ -51,7 +51,7 @@ To combine installers. * Make install.sh not have to be customized, pull it's data from a config file. -* Be more resiliant to installation failures, particularly if the disk +* Be more resilient to installation failures, particularly if the disk is full. * Pre-install and post-uninstall scripts. * Allow components to depend on or contradict other components. -- cgit 1.4.1-3-g733a5 From d0bd27924e69084f11290e7876ee63b69c0d9667 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Thu, 12 Jun 2025 20:49:48 +0500 Subject: cleaned up some tests --- src/tools/tidy/src/issues.txt | 1 - tests/ui/attributes/inner-attrs-impl-cfg.rs | 36 ++++++++ tests/ui/indexing/indexing-integral-types.rs | 20 +++++ tests/ui/indexing/indexing-integral-types.stderr | 99 ++++++++++++++++++++++ tests/ui/inner-attrs-on-impl.rs | 24 ------ tests/ui/inner-module.rs | 10 --- tests/ui/inner-static-type-parameter.rs | 11 --- tests/ui/inner-static-type-parameter.stderr | 23 ----- tests/ui/integral-indexing.rs | 16 ---- tests/ui/integral-indexing.stderr | 99 ---------------------- tests/ui/integral-variable-unification-error.rs | 8 -- .../ui/integral-variable-unification-error.stderr | 14 --- tests/ui/invalid_dispatch_from_dyn_impls.rs | 55 ------------ tests/ui/invalid_dispatch_from_dyn_impls.stderr | 60 ------------- tests/ui/issue-11881.rs | 91 -------------------- .../ui/mismatched_types/int-float-type-mismatch.rs | 11 +++ .../int-float-type-mismatch.stderr | 14 +++ tests/ui/modules/nested-modules-basic.rs | 19 +++++ tests/ui/statics/static-generic-param-soundness.rs | 20 +++++ .../statics/static-generic-param-soundness.stderr | 23 +++++ tests/ui/traits/dispatch-from-dyn-invalid-impls.rs | 71 ++++++++++++++++ .../traits/dispatch-from-dyn-invalid-impls.stderr | 60 +++++++++++++ tests/ui/traits/encoder-trait-bounds-regression.rs | 98 +++++++++++++++++++++ 23 files changed, 471 insertions(+), 412 deletions(-) create mode 100644 tests/ui/attributes/inner-attrs-impl-cfg.rs create mode 100644 tests/ui/indexing/indexing-integral-types.rs create mode 100644 tests/ui/indexing/indexing-integral-types.stderr delete mode 100644 tests/ui/inner-attrs-on-impl.rs delete mode 100644 tests/ui/inner-module.rs delete mode 100644 tests/ui/inner-static-type-parameter.rs delete mode 100644 tests/ui/inner-static-type-parameter.stderr delete mode 100644 tests/ui/integral-indexing.rs delete mode 100644 tests/ui/integral-indexing.stderr delete mode 100644 tests/ui/integral-variable-unification-error.rs delete mode 100644 tests/ui/integral-variable-unification-error.stderr delete mode 100644 tests/ui/invalid_dispatch_from_dyn_impls.rs delete mode 100644 tests/ui/invalid_dispatch_from_dyn_impls.stderr delete mode 100644 tests/ui/issue-11881.rs create mode 100644 tests/ui/mismatched_types/int-float-type-mismatch.rs create mode 100644 tests/ui/mismatched_types/int-float-type-mismatch.stderr create mode 100644 tests/ui/modules/nested-modules-basic.rs create mode 100644 tests/ui/statics/static-generic-param-soundness.rs create mode 100644 tests/ui/statics/static-generic-param-soundness.stderr create mode 100644 tests/ui/traits/dispatch-from-dyn-invalid-impls.rs create mode 100644 tests/ui/traits/dispatch-from-dyn-invalid-impls.stderr create mode 100644 tests/ui/traits/encoder-trait-bounds-regression.rs (limited to 'src/tools') diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index b3517b2e9da..38c6da5ba96 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -1368,7 +1368,6 @@ ui/infinite/issue-41731-infinite-macro-println.rs ui/intrinsics/issue-28575.rs ui/intrinsics/issue-84297-reifying-copy.rs ui/invalid/issue-114435-layout-type-err.rs -ui/issue-11881.rs ui/issue-15924.rs ui/issue-16822.rs ui/issues-71798.rs diff --git a/tests/ui/attributes/inner-attrs-impl-cfg.rs b/tests/ui/attributes/inner-attrs-impl-cfg.rs new file mode 100644 index 00000000000..e7a5cfa9e2f --- /dev/null +++ b/tests/ui/attributes/inner-attrs-impl-cfg.rs @@ -0,0 +1,36 @@ +//! Test inner attributes (#![...]) behavior in impl blocks with cfg conditions. +//! +//! This test verifies that: +//! - Inner attributes can conditionally exclude entire impl blocks +//! - Regular attributes within impl blocks work independently +//! - Attribute parsing doesn't consume too eagerly + +//@ run-pass + +struct Foo; + +impl Foo { + #![cfg(false)] + + fn method(&self) -> bool { + false + } +} + +impl Foo { + #![cfg(not(FALSE))] + + // Check that we don't eat attributes too eagerly. + #[cfg(false)] + fn method(&self) -> bool { + false + } + + fn method(&self) -> bool { + true + } +} + +pub fn main() { + assert!(Foo.method()); +} diff --git a/tests/ui/indexing/indexing-integral-types.rs b/tests/ui/indexing/indexing-integral-types.rs new file mode 100644 index 00000000000..a91696a6fd5 --- /dev/null +++ b/tests/ui/indexing/indexing-integral-types.rs @@ -0,0 +1,20 @@ +//! Test that only usize can be used for indexing arrays and slices. + +pub fn main() { + let v: Vec = vec![0, 1, 2, 3, 4, 5]; + let s: String = "abcdef".to_string(); + + // Valid indexing with usize + v[3_usize]; + v[3]; + v[3u8]; //~ ERROR the type `[isize]` cannot be indexed by `u8` + v[3i8]; //~ ERROR the type `[isize]` cannot be indexed by `i8` + v[3u32]; //~ ERROR the type `[isize]` cannot be indexed by `u32` + v[3i32]; //~ ERROR the type `[isize]` cannot be indexed by `i32` + s.as_bytes()[3_usize]; + s.as_bytes()[3]; + s.as_bytes()[3u8]; //~ ERROR the type `[u8]` cannot be indexed by `u8` + s.as_bytes()[3i8]; //~ ERROR the type `[u8]` cannot be indexed by `i8` + s.as_bytes()[3u32]; //~ ERROR the type `[u8]` cannot be indexed by `u32` + s.as_bytes()[3i32]; //~ ERROR the type `[u8]` cannot be indexed by `i32` +} diff --git a/tests/ui/indexing/indexing-integral-types.stderr b/tests/ui/indexing/indexing-integral-types.stderr new file mode 100644 index 00000000000..b63991ec2c4 --- /dev/null +++ b/tests/ui/indexing/indexing-integral-types.stderr @@ -0,0 +1,99 @@ +error[E0277]: the type `[isize]` cannot be indexed by `u8` + --> $DIR/indexing-integral-types.rs:10:7 + | +LL | v[3u8]; + | ^^^ slice indices are of type `usize` or ranges of `usize` + | + = help: the trait `SliceIndex<[isize]>` is not implemented for `u8` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` + = note: required for `Vec` to implement `Index` + +error[E0277]: the type `[isize]` cannot be indexed by `i8` + --> $DIR/indexing-integral-types.rs:11:7 + | +LL | v[3i8]; + | ^^^ slice indices are of type `usize` or ranges of `usize` + | + = help: the trait `SliceIndex<[isize]>` is not implemented for `i8` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` + = note: required for `Vec` to implement `Index` + +error[E0277]: the type `[isize]` cannot be indexed by `u32` + --> $DIR/indexing-integral-types.rs:12:7 + | +LL | v[3u32]; + | ^^^^ slice indices are of type `usize` or ranges of `usize` + | + = help: the trait `SliceIndex<[isize]>` is not implemented for `u32` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` + = note: required for `Vec` to implement `Index` + +error[E0277]: the type `[isize]` cannot be indexed by `i32` + --> $DIR/indexing-integral-types.rs:13:7 + | +LL | v[3i32]; + | ^^^^ slice indices are of type `usize` or ranges of `usize` + | + = help: the trait `SliceIndex<[isize]>` is not implemented for `i32` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` + = note: required for `Vec` to implement `Index` + +error[E0277]: the type `[u8]` cannot be indexed by `u8` + --> $DIR/indexing-integral-types.rs:16:18 + | +LL | s.as_bytes()[3u8]; + | ^^^ slice indices are of type `usize` or ranges of `usize` + | + = help: the trait `SliceIndex<[u8]>` is not implemented for `u8` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` + = note: required for `[u8]` to implement `Index` + +error[E0277]: the type `[u8]` cannot be indexed by `i8` + --> $DIR/indexing-integral-types.rs:17:18 + | +LL | s.as_bytes()[3i8]; + | ^^^ slice indices are of type `usize` or ranges of `usize` + | + = help: the trait `SliceIndex<[u8]>` is not implemented for `i8` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` + = note: required for `[u8]` to implement `Index` + +error[E0277]: the type `[u8]` cannot be indexed by `u32` + --> $DIR/indexing-integral-types.rs:18:18 + | +LL | s.as_bytes()[3u32]; + | ^^^^ slice indices are of type `usize` or ranges of `usize` + | + = help: the trait `SliceIndex<[u8]>` is not implemented for `u32` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` + = note: required for `[u8]` to implement `Index` + +error[E0277]: the type `[u8]` cannot be indexed by `i32` + --> $DIR/indexing-integral-types.rs:19:18 + | +LL | s.as_bytes()[3i32]; + | ^^^^ slice indices are of type `usize` or ranges of `usize` + | + = help: the trait `SliceIndex<[u8]>` is not implemented for `i32` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` + = note: required for `[u8]` to implement `Index` + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/inner-attrs-on-impl.rs b/tests/ui/inner-attrs-on-impl.rs deleted file mode 100644 index 1dce1cdd261..00000000000 --- a/tests/ui/inner-attrs-on-impl.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ run-pass - -struct Foo; - -impl Foo { - #![cfg(false)] - - fn method(&self) -> bool { false } -} - -impl Foo { - #![cfg(not(FALSE))] - - // check that we don't eat attributes too eagerly. - #[cfg(false)] - fn method(&self) -> bool { false } - - fn method(&self) -> bool { true } -} - - -pub fn main() { - assert!(Foo.method()); -} diff --git a/tests/ui/inner-module.rs b/tests/ui/inner-module.rs deleted file mode 100644 index 111f2cab857..00000000000 --- a/tests/ui/inner-module.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass - -mod inner { - pub mod inner2 { - pub fn hello() { println!("hello, modular world"); } - } - pub fn hello() { inner2::hello(); } -} - -pub fn main() { inner::hello(); inner::inner2::hello(); } diff --git a/tests/ui/inner-static-type-parameter.rs b/tests/ui/inner-static-type-parameter.rs deleted file mode 100644 index a1994e7529c..00000000000 --- a/tests/ui/inner-static-type-parameter.rs +++ /dev/null @@ -1,11 +0,0 @@ -// see #9186 - -enum Bar { What } //~ ERROR parameter `T` is never used - -fn foo() { - static a: Bar = Bar::What; -//~^ ERROR can't use generic parameters from outer item -} - -fn main() { -} diff --git a/tests/ui/inner-static-type-parameter.stderr b/tests/ui/inner-static-type-parameter.stderr deleted file mode 100644 index 88d33b44c59..00000000000 --- a/tests/ui/inner-static-type-parameter.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0401]: can't use generic parameters from outer item - --> $DIR/inner-static-type-parameter.rs:6:19 - | -LL | fn foo() { - | - type parameter from outer item -LL | static a: Bar = Bar::What; - | ^ use of generic parameter from outer item - | - = note: a `static` is a separate item from the item that contains it - -error[E0392]: type parameter `T` is never used - --> $DIR/inner-static-type-parameter.rs:3:10 - | -LL | enum Bar { What } - | ^ unused type parameter - | - = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` - = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0392, E0401. -For more information about an error, try `rustc --explain E0392`. diff --git a/tests/ui/integral-indexing.rs b/tests/ui/integral-indexing.rs deleted file mode 100644 index e20553af8a2..00000000000 --- a/tests/ui/integral-indexing.rs +++ /dev/null @@ -1,16 +0,0 @@ -pub fn main() { - let v: Vec = vec![0, 1, 2, 3, 4, 5]; - let s: String = "abcdef".to_string(); - v[3_usize]; - v[3]; - v[3u8]; //~ ERROR the type `[isize]` cannot be indexed by `u8` - v[3i8]; //~ ERROR the type `[isize]` cannot be indexed by `i8` - v[3u32]; //~ ERROR the type `[isize]` cannot be indexed by `u32` - v[3i32]; //~ ERROR the type `[isize]` cannot be indexed by `i32` - s.as_bytes()[3_usize]; - s.as_bytes()[3]; - s.as_bytes()[3u8]; //~ ERROR the type `[u8]` cannot be indexed by `u8` - s.as_bytes()[3i8]; //~ ERROR the type `[u8]` cannot be indexed by `i8` - s.as_bytes()[3u32]; //~ ERROR the type `[u8]` cannot be indexed by `u32` - s.as_bytes()[3i32]; //~ ERROR the type `[u8]` cannot be indexed by `i32` -} diff --git a/tests/ui/integral-indexing.stderr b/tests/ui/integral-indexing.stderr deleted file mode 100644 index 26253e078cb..00000000000 --- a/tests/ui/integral-indexing.stderr +++ /dev/null @@ -1,99 +0,0 @@ -error[E0277]: the type `[isize]` cannot be indexed by `u8` - --> $DIR/integral-indexing.rs:6:7 - | -LL | v[3u8]; - | ^^^ slice indices are of type `usize` or ranges of `usize` - | - = help: the trait `SliceIndex<[isize]>` is not implemented for `u8` - = help: the following other types implement trait `SliceIndex`: - `usize` implements `SliceIndex` - `usize` implements `SliceIndex<[T]>` - = note: required for `Vec` to implement `Index` - -error[E0277]: the type `[isize]` cannot be indexed by `i8` - --> $DIR/integral-indexing.rs:7:7 - | -LL | v[3i8]; - | ^^^ slice indices are of type `usize` or ranges of `usize` - | - = help: the trait `SliceIndex<[isize]>` is not implemented for `i8` - = help: the following other types implement trait `SliceIndex`: - `usize` implements `SliceIndex` - `usize` implements `SliceIndex<[T]>` - = note: required for `Vec` to implement `Index` - -error[E0277]: the type `[isize]` cannot be indexed by `u32` - --> $DIR/integral-indexing.rs:8:7 - | -LL | v[3u32]; - | ^^^^ slice indices are of type `usize` or ranges of `usize` - | - = help: the trait `SliceIndex<[isize]>` is not implemented for `u32` - = help: the following other types implement trait `SliceIndex`: - `usize` implements `SliceIndex` - `usize` implements `SliceIndex<[T]>` - = note: required for `Vec` to implement `Index` - -error[E0277]: the type `[isize]` cannot be indexed by `i32` - --> $DIR/integral-indexing.rs:9:7 - | -LL | v[3i32]; - | ^^^^ slice indices are of type `usize` or ranges of `usize` - | - = help: the trait `SliceIndex<[isize]>` is not implemented for `i32` - = help: the following other types implement trait `SliceIndex`: - `usize` implements `SliceIndex` - `usize` implements `SliceIndex<[T]>` - = note: required for `Vec` to implement `Index` - -error[E0277]: the type `[u8]` cannot be indexed by `u8` - --> $DIR/integral-indexing.rs:12:18 - | -LL | s.as_bytes()[3u8]; - | ^^^ slice indices are of type `usize` or ranges of `usize` - | - = help: the trait `SliceIndex<[u8]>` is not implemented for `u8` - = help: the following other types implement trait `SliceIndex`: - `usize` implements `SliceIndex` - `usize` implements `SliceIndex<[T]>` - = note: required for `[u8]` to implement `Index` - -error[E0277]: the type `[u8]` cannot be indexed by `i8` - --> $DIR/integral-indexing.rs:13:18 - | -LL | s.as_bytes()[3i8]; - | ^^^ slice indices are of type `usize` or ranges of `usize` - | - = help: the trait `SliceIndex<[u8]>` is not implemented for `i8` - = help: the following other types implement trait `SliceIndex`: - `usize` implements `SliceIndex` - `usize` implements `SliceIndex<[T]>` - = note: required for `[u8]` to implement `Index` - -error[E0277]: the type `[u8]` cannot be indexed by `u32` - --> $DIR/integral-indexing.rs:14:18 - | -LL | s.as_bytes()[3u32]; - | ^^^^ slice indices are of type `usize` or ranges of `usize` - | - = help: the trait `SliceIndex<[u8]>` is not implemented for `u32` - = help: the following other types implement trait `SliceIndex`: - `usize` implements `SliceIndex` - `usize` implements `SliceIndex<[T]>` - = note: required for `[u8]` to implement `Index` - -error[E0277]: the type `[u8]` cannot be indexed by `i32` - --> $DIR/integral-indexing.rs:15:18 - | -LL | s.as_bytes()[3i32]; - | ^^^^ slice indices are of type `usize` or ranges of `usize` - | - = help: the trait `SliceIndex<[u8]>` is not implemented for `i32` - = help: the following other types implement trait `SliceIndex`: - `usize` implements `SliceIndex` - `usize` implements `SliceIndex<[T]>` - = note: required for `[u8]` to implement `Index` - -error: aborting due to 8 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/integral-variable-unification-error.rs b/tests/ui/integral-variable-unification-error.rs deleted file mode 100644 index 8d1621321e8..00000000000 --- a/tests/ui/integral-variable-unification-error.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn main() { - let mut x //~ NOTE expected due to the type of this binding - = - 2; //~ NOTE expected due to this value - x = 5.0; - //~^ ERROR mismatched types - //~| NOTE expected integer, found floating-point number -} diff --git a/tests/ui/integral-variable-unification-error.stderr b/tests/ui/integral-variable-unification-error.stderr deleted file mode 100644 index 1caa6042fd2..00000000000 --- a/tests/ui/integral-variable-unification-error.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/integral-variable-unification-error.rs:5:9 - | -LL | let mut x - | ----- expected due to the type of this binding -LL | = -LL | 2; - | - expected due to this value -LL | x = 5.0; - | ^^^ expected integer, found floating-point number - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/invalid_dispatch_from_dyn_impls.rs b/tests/ui/invalid_dispatch_from_dyn_impls.rs deleted file mode 100644 index b1d4b261bab..00000000000 --- a/tests/ui/invalid_dispatch_from_dyn_impls.rs +++ /dev/null @@ -1,55 +0,0 @@ -#![feature(unsize, dispatch_from_dyn)] - -use std::{ - ops::DispatchFromDyn, - marker::{Unsize, PhantomData}, -}; - -struct WrapperWithExtraField(T, i32); - -impl DispatchFromDyn> for WrapperWithExtraField -//~^ ERROR [E0378] -where - T: DispatchFromDyn, -{} - - -struct MultiplePointers{ - ptr1: *const T, - ptr2: *const T, -} - -impl DispatchFromDyn> for MultiplePointers -//~^ ERROR implementing `DispatchFromDyn` does not allow multiple fields to be coerced -where - T: Unsize, -{} - - -struct NothingToCoerce { - data: PhantomData, -} - -impl DispatchFromDyn> for NothingToCoerce {} -//~^ ERROR implementing `DispatchFromDyn` requires a field to be coerced - -#[repr(C)] -struct HasReprC(Box); - -impl DispatchFromDyn> for HasReprC -//~^ ERROR [E0378] -where - T: Unsize, -{} - -#[repr(align(64))] -struct OverAlignedZst; -struct OverAligned(Box, OverAlignedZst); - -impl DispatchFromDyn> for OverAligned -//~^ ERROR [E0378] - where - T: Unsize, -{} - -fn main() {} diff --git a/tests/ui/invalid_dispatch_from_dyn_impls.stderr b/tests/ui/invalid_dispatch_from_dyn_impls.stderr deleted file mode 100644 index 93ec6bbe089..00000000000 --- a/tests/ui/invalid_dispatch_from_dyn_impls.stderr +++ /dev/null @@ -1,60 +0,0 @@ -error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else - --> $DIR/invalid_dispatch_from_dyn_impls.rs:10:1 - | -LL | / impl DispatchFromDyn> for WrapperWithExtraField -LL | | -LL | | where -LL | | T: DispatchFromDyn, - | |__________________________^ - | - = note: extra field `1` of type `i32` is not allowed - -error[E0375]: implementing `DispatchFromDyn` does not allow multiple fields to be coerced - --> $DIR/invalid_dispatch_from_dyn_impls.rs:22:1 - | -LL | / impl DispatchFromDyn> for MultiplePointers -LL | | -LL | | where -LL | | T: Unsize, - | |_________________^ - | -note: the trait `DispatchFromDyn` may only be implemented when a single field is being coerced - --> $DIR/invalid_dispatch_from_dyn_impls.rs:18:5 - | -LL | ptr1: *const T, - | ^^^^^^^^^^^^^^ -LL | ptr2: *const T, - | ^^^^^^^^^^^^^^ - -error[E0374]: implementing `DispatchFromDyn` requires a field to be coerced - --> $DIR/invalid_dispatch_from_dyn_impls.rs:33:1 - | -LL | impl DispatchFromDyn> for NothingToCoerce {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: expected a single field to be coerced, none found - -error[E0378]: structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]` - --> $DIR/invalid_dispatch_from_dyn_impls.rs:39:1 - | -LL | / impl DispatchFromDyn> for HasReprC -LL | | -LL | | where -LL | | T: Unsize, - | |_________________^ - -error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else - --> $DIR/invalid_dispatch_from_dyn_impls.rs:49:1 - | -LL | / impl DispatchFromDyn> for OverAligned -LL | | -LL | | where -LL | | T: Unsize, - | |_____________________^ - | - = note: extra field `1` of type `OverAlignedZst` is not allowed - -error: aborting due to 5 previous errors - -Some errors have detailed explanations: E0374, E0375, E0378. -For more information about an error, try `rustc --explain E0374`. diff --git a/tests/ui/issue-11881.rs b/tests/ui/issue-11881.rs deleted file mode 100644 index 1abe0797203..00000000000 --- a/tests/ui/issue-11881.rs +++ /dev/null @@ -1,91 +0,0 @@ -//@ run-pass - -#![allow(unused_must_use)] -#![allow(dead_code)] -#![allow(unused_imports)] - -use std::fmt; -use std::io::prelude::*; -use std::io::Cursor; -use std::slice; -use std::marker::PhantomData; - -trait Encoder { - type Error; -} - -trait Encodable { - fn encode(&self, s: &mut S) -> Result<(), S::Error>; -} - -struct JsonEncoder<'a>(PhantomData<&'a mut ()>); - -impl Encoder for JsonEncoder<'_> { - type Error = (); -} - -struct AsJson<'a, T> { - inner: &'a T, -} - -impl<'a, T: for<'r> Encodable>> fmt::Display for AsJson<'a, T> { - /// Encodes a json value into a string - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) - } -} - -fn as_json(t: &T) -> AsJson<'_, T> { - AsJson { inner: t } -} - -struct OpaqueEncoder(Vec); - -impl Encoder for OpaqueEncoder { - type Error = (); -} - - -struct Foo { - baz: bool, -} - -impl Encodable for Foo { - fn encode(&self, _s: &mut S) -> Result<(), S::Error> { - Ok(()) - } -} - -struct Bar { - froboz: usize, -} - -impl Encodable for Bar { - fn encode(&self, _s: &mut S) -> Result<(), S::Error> { - Ok(()) - } -} - -enum WireProtocol { - JSON, - Opaque, - // ... -} - -fn encode_json Encodable>>(val: &T, wr: &mut Cursor>) { - write!(wr, "{}", as_json(val)); -} - -fn encode_opaque>(val: &T, wr: Vec) { - let mut encoder = OpaqueEncoder(wr); - val.encode(&mut encoder); -} - -pub fn main() { - let target = Foo { baz: false }; - let proto = WireProtocol::JSON; - match proto { - WireProtocol::JSON => encode_json(&target, &mut Cursor::new(Vec::new())), - WireProtocol::Opaque => encode_opaque(&target, Vec::new()), - } -} diff --git a/tests/ui/mismatched_types/int-float-type-mismatch.rs b/tests/ui/mismatched_types/int-float-type-mismatch.rs new file mode 100644 index 00000000000..b45d02730d9 --- /dev/null +++ b/tests/ui/mismatched_types/int-float-type-mismatch.rs @@ -0,0 +1,11 @@ +//! Check that a type mismatch error is reported when trying +//! to unify a {float} value assignment to an {integer} variable. + +fn main() { + let mut x //~ NOTE expected due to the type of this binding + = + 2; //~ NOTE expected due to this value + x = 5.0; + //~^ ERROR mismatched types + //~| NOTE expected integer, found floating-point number +} diff --git a/tests/ui/mismatched_types/int-float-type-mismatch.stderr b/tests/ui/mismatched_types/int-float-type-mismatch.stderr new file mode 100644 index 00000000000..43b8609a49d --- /dev/null +++ b/tests/ui/mismatched_types/int-float-type-mismatch.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/int-float-type-mismatch.rs:8:9 + | +LL | let mut x + | ----- expected due to the type of this binding +LL | = +LL | 2; + | - expected due to this value +LL | x = 5.0; + | ^^^ expected integer, found floating-point number + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/modules/nested-modules-basic.rs b/tests/ui/modules/nested-modules-basic.rs new file mode 100644 index 00000000000..12eccec2808 --- /dev/null +++ b/tests/ui/modules/nested-modules-basic.rs @@ -0,0 +1,19 @@ +//! Basic test for nested module functionality and path resolution + +//@ run-pass + +mod inner { + pub mod inner2 { + pub fn hello() { + println!("hello, modular world"); + } + } + pub fn hello() { + inner2::hello(); + } +} + +pub fn main() { + inner::hello(); + inner::inner2::hello(); +} diff --git a/tests/ui/statics/static-generic-param-soundness.rs b/tests/ui/statics/static-generic-param-soundness.rs new file mode 100644 index 00000000000..aabcca514d3 --- /dev/null +++ b/tests/ui/statics/static-generic-param-soundness.rs @@ -0,0 +1,20 @@ +//! Originally, inner statics in generic functions were generated only once, causing the same +//! static to be shared across all generic instantiations. This created a soundness hole where +//! different types could be coerced through thread-local storage in safe code. +//! +//! This test checks that generic parameters from outer scopes cannot be used in inner statics, +//! preventing this soundness issue. +//! +//! See https://github.com/rust-lang/rust/issues/9186 + +enum Bar { + //~^ ERROR parameter `T` is never used + What, +} + +fn foo() { + static a: Bar = Bar::What; + //~^ ERROR can't use generic parameters from outer item +} + +fn main() {} diff --git a/tests/ui/statics/static-generic-param-soundness.stderr b/tests/ui/statics/static-generic-param-soundness.stderr new file mode 100644 index 00000000000..47554c7fcb0 --- /dev/null +++ b/tests/ui/statics/static-generic-param-soundness.stderr @@ -0,0 +1,23 @@ +error[E0401]: can't use generic parameters from outer item + --> $DIR/static-generic-param-soundness.rs:16:19 + | +LL | fn foo() { + | - type parameter from outer item +LL | static a: Bar = Bar::What; + | ^ use of generic parameter from outer item + | + = note: a `static` is a separate item from the item that contains it + +error[E0392]: type parameter `T` is never used + --> $DIR/static-generic-param-soundness.rs:10:10 + | +LL | enum Bar { + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0392, E0401. +For more information about an error, try `rustc --explain E0392`. diff --git a/tests/ui/traits/dispatch-from-dyn-invalid-impls.rs b/tests/ui/traits/dispatch-from-dyn-invalid-impls.rs new file mode 100644 index 00000000000..f5f66ca69cf --- /dev/null +++ b/tests/ui/traits/dispatch-from-dyn-invalid-impls.rs @@ -0,0 +1,71 @@ +//! Test various invalid implementations of DispatchFromDyn trait. +//! +//! DispatchFromDyn is a special trait used by the compiler for dyn-compatible dynamic dispatch. +//! This checks that the compiler correctly rejects invalid implementations: +//! - Structs with extra non-coercible fields +//! - Structs with multiple pointer fields +//! - Structs with no coercible fields +//! - Structs with repr(C) or other incompatible representations +//! - Structs with over-aligned fields + +#![feature(unsize, dispatch_from_dyn)] + +use std::marker::{PhantomData, Unsize}; +use std::ops::DispatchFromDyn; + +// Extra field prevents DispatchFromDyn +struct WrapperWithExtraField(T, i32); + +impl DispatchFromDyn> for WrapperWithExtraField +//~^ ERROR [E0378] +where + T: DispatchFromDyn +{ +} + +// Multiple pointer fields create ambiguous coercion +struct MultiplePointers { + ptr1: *const T, + ptr2: *const T, +} + +impl DispatchFromDyn> for MultiplePointers +//~^ ERROR implementing `DispatchFromDyn` does not allow multiple fields to be coerced +where + T: Unsize +{ +} + +// No coercible fields (only PhantomData) +struct NothingToCoerce { + data: PhantomData, +} + +impl DispatchFromDyn> for NothingToCoerce {} +//~^ ERROR implementing `DispatchFromDyn` requires a field to be coerced + +// repr(C) is incompatible with DispatchFromDyn +#[repr(C)] +struct HasReprC(Box); + +impl DispatchFromDyn> for HasReprC +//~^ ERROR [E0378] +where + T: Unsize +{ +} + +// Over-aligned fields are incompatible +#[repr(align(64))] +struct OverAlignedZst; + +struct OverAligned(Box, OverAlignedZst); + +impl DispatchFromDyn> for OverAligned +//~^ ERROR [E0378] +where + T: Unsize +{ +} + +fn main() {} diff --git a/tests/ui/traits/dispatch-from-dyn-invalid-impls.stderr b/tests/ui/traits/dispatch-from-dyn-invalid-impls.stderr new file mode 100644 index 00000000000..676da0ce81f --- /dev/null +++ b/tests/ui/traits/dispatch-from-dyn-invalid-impls.stderr @@ -0,0 +1,60 @@ +error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else + --> $DIR/dispatch-from-dyn-invalid-impls.rs:19:1 + | +LL | / impl DispatchFromDyn> for WrapperWithExtraField +LL | | +LL | | where +LL | | T: DispatchFromDyn + | |_________________________^ + | + = note: extra field `1` of type `i32` is not allowed + +error[E0375]: implementing `DispatchFromDyn` does not allow multiple fields to be coerced + --> $DIR/dispatch-from-dyn-invalid-impls.rs:32:1 + | +LL | / impl DispatchFromDyn> for MultiplePointers +LL | | +LL | | where +LL | | T: Unsize + | |________________^ + | +note: the trait `DispatchFromDyn` may only be implemented when a single field is being coerced + --> $DIR/dispatch-from-dyn-invalid-impls.rs:28:5 + | +LL | ptr1: *const T, + | ^^^^^^^^^^^^^^ +LL | ptr2: *const T, + | ^^^^^^^^^^^^^^ + +error[E0374]: implementing `DispatchFromDyn` requires a field to be coerced + --> $DIR/dispatch-from-dyn-invalid-impls.rs:44:1 + | +LL | impl DispatchFromDyn> for NothingToCoerce {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected a single field to be coerced, none found + +error[E0378]: structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]` + --> $DIR/dispatch-from-dyn-invalid-impls.rs:51:1 + | +LL | / impl DispatchFromDyn> for HasReprC +LL | | +LL | | where +LL | | T: Unsize + | |________________^ + +error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else + --> $DIR/dispatch-from-dyn-invalid-impls.rs:64:1 + | +LL | / impl DispatchFromDyn> for OverAligned +LL | | +LL | | where +LL | | T: Unsize + | |________________^ + | + = note: extra field `1` of type `OverAlignedZst` is not allowed + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0374, E0375, E0378. +For more information about an error, try `rustc --explain E0374`. diff --git a/tests/ui/traits/encoder-trait-bounds-regression.rs b/tests/ui/traits/encoder-trait-bounds-regression.rs new file mode 100644 index 00000000000..292b921cdf7 --- /dev/null +++ b/tests/ui/traits/encoder-trait-bounds-regression.rs @@ -0,0 +1,98 @@ +//! Regression test for issue #11881 +//! +//! Originally, the compiler would ICE when trying to parameterize on certain encoder types +//! due to issues with higher-ranked trait bounds and lifetime inference. This test checks +//! that various encoder patterns work correctly: +//! - Generic encoders with associated error types +//! - Higher-ranked trait bounds (for<'r> Encodable>) +//! - Multiple encoder implementations for the same type +//! - Polymorphic encoding functions + +//@ run-pass + +#![allow(unused_must_use)] +#![allow(dead_code)] +#![allow(unused_imports)] + +use std::io::Cursor; +use std::io::prelude::*; +use std::marker::PhantomData; +use std::{fmt, slice}; + +trait Encoder { + type Error; +} + +trait Encodable { + fn encode(&self, s: &mut S) -> Result<(), S::Error>; +} + +struct JsonEncoder<'a>(PhantomData<&'a mut ()>); + +impl Encoder for JsonEncoder<'_> { + type Error = (); +} + +struct AsJson<'a, T> { + inner: &'a T, +} + +impl<'a, T: for<'r> Encodable>> fmt::Display for AsJson<'a, T> { + /// Encodes a json value into a string + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + Ok(()) + } +} + +fn as_json(t: &T) -> AsJson<'_, T> { + AsJson { inner: t } +} + +struct OpaqueEncoder(Vec); + +impl Encoder for OpaqueEncoder { + type Error = (); +} + +struct Foo { + baz: bool, +} + +impl Encodable for Foo { + fn encode(&self, _s: &mut S) -> Result<(), S::Error> { + Ok(()) + } +} + +struct Bar { + froboz: usize, +} + +impl Encodable for Bar { + fn encode(&self, _s: &mut S) -> Result<(), S::Error> { + Ok(()) + } +} + +enum WireProtocol { + JSON, + Opaque, +} + +fn encode_json Encodable>>(val: &T, wr: &mut Cursor>) { + write!(wr, "{}", as_json(val)); +} + +fn encode_opaque>(val: &T, wr: Vec) { + let mut encoder = OpaqueEncoder(wr); + val.encode(&mut encoder); +} + +pub fn main() { + let target = Foo { baz: false }; + let proto = WireProtocol::JSON; + match proto { + WireProtocol::JSON => encode_json(&target, &mut Cursor::new(Vec::new())), + WireProtocol::Opaque => encode_opaque(&target, Vec::new()), + } +} -- cgit 1.4.1-3-g733a5 From 60a48287d7d779e4922a69b5a54154f0c075825a Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Sat, 14 Jun 2025 14:42:16 +0200 Subject: make some powf and powi cases involving SNaN non-deterministic --- src/tools/miri/src/intrinsics/mod.rs | 90 +++++++++++++++++++++++++----------- src/tools/miri/tests/pass/float.rs | 25 +++++----- 2 files changed, 77 insertions(+), 38 deletions(-) (limited to 'src/tools') diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index ec77a162cdb..2dbcaefb94b 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -17,6 +17,7 @@ use self::atomic::EvalContextExt as _; use self::helpers::{ToHost, ToSoft, check_intrinsic_arg_count}; use self::simd::EvalContextExt as _; use crate::math::{IeeeExt, apply_random_float_error_ulp}; +use crate::operator::EvalContextExt as _; use crate::*; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -191,7 +192,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(intrinsic_name, &[f]).unwrap_or_else(||{ + let res = 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(); @@ -235,7 +236,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(intrinsic_name, &[f]).unwrap_or_else(||{ + let res = 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(); @@ -312,7 +313,7 @@ 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(intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + 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(); @@ -330,7 +331,7 @@ 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(intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + 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(); @@ -349,7 +350,7 @@ 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(f, i).unwrap_or_else(|| { + let res = fixed_powi_float_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(); @@ -367,7 +368,7 @@ 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(f, i).unwrap_or_else(|| { + let res = fixed_powi_float_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(); @@ -496,49 +497,84 @@ fn apply_random_float_error_to_imm<'tcx>( /// - 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( + ecx: &mut MiriInterpCx<'_>, intrinsic_name: &str, args: &[IeeeFloat], ) -> Option> { let one = IeeeFloat::::one(); - match (intrinsic_name, args) { + Some(match (intrinsic_name, args) { // cos(+- 0) = 1 - ("cosf32" | "cosf64", [input]) if input.is_zero() => Some(one), + ("cosf32" | "cosf64", [input]) if input.is_zero() => one, // e^0 = 1 - ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => Some(one), - - // 1^y = 1 for any y, even a NaN. - ("powf32" | "powf64", [base, _]) if *base == one => Some(one), + ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one, // (-1)^(±INF) = 1 - ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => Some(one), + ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one, + + // 1^y = 1 for any y, even a NaN, *but* not a SNaN + ("powf32" | "powf64", [base, exp]) if *base == one => { + let rng = ecx.machine.rng.get_mut(); + let return_nan = ecx.machine.float_nondet && rng.random() && exp.is_signaling(); + // Handle both the musl and glibc cases non-deterministically. + if return_nan { ecx.generate_nan(args) } else { one } + } - // FIXME(#4286): The C ecosystem is inconsistent with handling sNaN's, some return 1 others propogate - // the NaN. We should return either 1 or the NaN non-deterministically here. - // But for now, just handle them all the same. - // x^(±0) = 1 for any x, even a NaN - ("powf32" | "powf64", [_, exp]) if exp.is_zero() => Some(one), + // x^(±0) = 1 for any x, even a NaN, *but* not a SNaN + ("powf32" | "powf64", [base, exp]) if exp.is_zero() => { + let rng = ecx.machine.rng.get_mut(); + let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling(); + // 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. - _ => None, - } + _ => 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(base: IeeeFloat, exp: i32) -> Option> { - match (base.category(), exp) { - // x^0 = 1, if x is not a Signaling NaN - // FIXME(#4286): The C ecosystem is inconsistent with handling sNaN's, some return 1 others propogate - // the NaN. We should return either 1 or the NaN non-deterministically here. - // But for now, just handle them all the same. - (_, 0) => Some(IeeeFloat::::one()), +// TODO: I'm not sure what I should document here about pown(1, SNaN) since musl and glibc do the same and the C standard is explicit here. +fn fixed_powi_float_value( + ecx: &mut MiriInterpCx<'_>, + base: IeeeFloat, + exp: i32, +) -> Option> { + match exp { + 0 => { + let one = IeeeFloat::::one(); + let rng = ecx.machine.rng.get_mut(); + let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling(); + Some( + // Handle both the musl and glibc powf cases non-deterministically. + if return_nan { ecx.generate_nan(&[base]) } else { one }, + ) + } _ => None, } diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 383579198bb..041e86fbe68 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -1066,17 +1066,6 @@ pub fn libm() { assert_eq!((-1f32).powf(f32::NEG_INFINITY), 1.0); assert_eq!((-1f64).powf(f64::NEG_INFINITY), 1.0); - // For pow (powf in rust) the C standard says: - // x^0 = 1 for all x even a sNaN - // FIXME(#4286): this does not match the behavior of all implementations. - assert_eq!(SNAN_F32.powf(0.0), 1.0); - assert_eq!(SNAN_F64.powf(0.0), 1.0); - - // For pown (powi in rust) the C standard says: - // x^0 = 1 for all x even a sNaN - // FIXME(#4286): this does not match the behavior of all implementations. - assert_eq!(SNAN_F32.powi(0), 1.0); - assert_eq!(SNAN_F64.powi(0), 1.0); assert_eq!(0f32.powi(10), 0.0); assert_eq!(0f64.powi(100), 0.0); @@ -1500,4 +1489,18 @@ fn test_non_determinism() { test_operations_f32(12., 5.); test_operations_f64(19., 11.); test_operations_f128(25., 18.); + + + // x^(SNaN) = (1 | NaN) + ensure_nondet(|| f32::powf(SNAN_F32, 0.0)); + ensure_nondet(|| f64::powf(SNAN_F64, 0.0)); + + // 1^(SNaN) = (1 | NaN) + ensure_nondet(|| f32::powf(1.0, SNAN_F32)); + ensure_nondet(|| f64::powf(1.0, SNAN_F64)); + + // same as powf (keep it consistent): + // x^(SNaN) = (1 | NaN) + ensure_nondet(|| f32::powi(SNAN_F32, 0)); + ensure_nondet(|| f64::powi(SNAN_F64, 0)); } -- cgit 1.4.1-3-g733a5 From 67ab61e8ba0bbf802e169f7084b6760509a15392 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 29 Jun 2025 22:18:02 +0200 Subject: add float_nan test for powf plus various minor tweaks --- src/tools/miri/src/intrinsics/mod.rs | 38 +++++++++++++++++----------------- src/tools/miri/tests/pass/float.rs | 18 ++++++++-------- src/tools/miri/tests/pass/float_nan.rs | 26 +++++++++++++++++++++++ 3 files changed, 54 insertions(+), 28 deletions(-) (limited to 'src/tools') diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 2dbcaefb94b..ed1851a19ae 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -17,7 +17,6 @@ use self::atomic::EvalContextExt as _; use self::helpers::{ToHost, ToSoft, check_intrinsic_arg_count}; use self::simd::EvalContextExt as _; use crate::math::{IeeeExt, apply_random_float_error_ulp}; -use crate::operator::EvalContextExt as _; use crate::*; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -192,7 +191,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 = 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(); @@ -236,7 +235,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 = 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(); @@ -535,49 +534,50 @@ fn fixed_float_value( // (-1)^(±INF) = 1 ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one, - // 1^y = 1 for any y, even a NaN, *but* not a SNaN + // 1^y = 1 for any y, even a NaN ("powf32" | "powf64", [base, exp]) if *base == one => { let rng = ecx.machine.rng.get_mut(); - let return_nan = ecx.machine.float_nondet && rng.random() && exp.is_signaling(); + // 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, *but* not a SNaN + // x^(±0) = 1 for any x, even a NaN ("powf32" | "powf64", [base, exp]) if exp.is_zero() => { let rng = ecx.machine.rng.get_mut(); - let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling(); + // 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. + // 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`. -// TODO: I'm not sure what I should document here about pown(1, SNaN) since musl and glibc do the same and the C standard is explicit here. +/// 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( ecx: &mut MiriInterpCx<'_>, base: IeeeFloat, exp: i32, ) -> Option> { - match exp { + Some(match exp { 0 => { let one = IeeeFloat::::one(); let rng = ecx.machine.rng.get_mut(); let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling(); - Some( - // Handle both the musl and glibc powf cases non-deterministically. - if return_nan { ecx.generate_nan(&[base]) } else { one }, - ) + // 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 } } - _ => None, - } + _ => return None, + }) } /// Given an floating-point operation and a floating-point value, clamps the result to the output diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 041e86fbe68..f21c6089a7b 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -1491,16 +1491,16 @@ fn test_non_determinism() { test_operations_f128(25., 18.); - // x^(SNaN) = (1 | NaN) - ensure_nondet(|| f32::powf(SNAN_F32, 0.0)); - ensure_nondet(|| f64::powf(SNAN_F64, 0.0)); + // SNaN^0 = (1 | NaN) + ensure_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan()); + ensure_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan()); - // 1^(SNaN) = (1 | NaN) - ensure_nondet(|| f32::powf(1.0, SNAN_F32)); - ensure_nondet(|| f64::powf(1.0, SNAN_F64)); + // 1^SNaN = (1 | NaN) + ensure_nondet(|| f32::powf(1.0, SNAN_F32).is_nan()); + ensure_nondet(|| f64::powf(1.0, SNAN_F64).is_nan()); // same as powf (keep it consistent): - // x^(SNaN) = (1 | NaN) - ensure_nondet(|| f32::powi(SNAN_F32, 0)); - ensure_nondet(|| f64::powi(SNAN_F64, 0)); + // x^SNaN = (1 | NaN) + ensure_nondet(|| f32::powi(SNAN_F32, 0).is_nan()); + ensure_nondet(|| f64::powi(SNAN_F64, 0).is_nan()); } diff --git a/src/tools/miri/tests/pass/float_nan.rs b/src/tools/miri/tests/pass/float_nan.rs index cadbbd58af5..3ffdb6868ac 100644 --- a/src/tools/miri/tests/pass/float_nan.rs +++ b/src/tools/miri/tests/pass/float_nan.rs @@ -260,6 +260,7 @@ fn test_f32() { // Intrinsics let nan = F32::nan(Neg, Quiet, 0).as_f32(); + let snan = F32::nan(Neg, Signaling, 1).as_f32(); check_all_outcomes( HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), || F32::from(f32::min(nan, nan)), @@ -313,6 +314,18 @@ fn test_f32() { HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), || F32::from(nan.ln_gamma().0), ); + check_all_outcomes( + HashSet::from_iter([ + F32::from(1.0), + F32::nan(Pos, Quiet, 0), + F32::nan(Neg, Quiet, 0), + F32::nan(Pos, Quiet, 1), + F32::nan(Neg, Quiet, 1), + F32::nan(Pos, Signaling, 1), + F32::nan(Neg, Signaling, 1), + ]), + || F32::from(snan.powf(0.0)), + ); } fn test_f64() { @@ -376,6 +389,7 @@ fn test_f64() { // Intrinsics let nan = F64::nan(Neg, Quiet, 0).as_f64(); + let snan = F64::nan(Neg, Signaling, 1).as_f64(); check_all_outcomes( HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), || F64::from(f64::min(nan, nan)), @@ -433,6 +447,18 @@ fn test_f64() { HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), || F64::from(nan.ln_gamma().0), ); + check_all_outcomes( + HashSet::from_iter([ + F64::from(1.0), + F64::nan(Pos, Quiet, 0), + F64::nan(Neg, Quiet, 0), + F64::nan(Pos, Quiet, 1), + F64::nan(Neg, Quiet, 1), + F64::nan(Pos, Signaling, 1), + F64::nan(Neg, Signaling, 1), + ]), + || F64::from(snan.powf(0.0)), + ); } fn test_casts() { -- cgit 1.4.1-3-g733a5