From b61570ac11650579252a9350505a7064e85b48c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 24 Jul 2024 19:31:02 +0000 Subject: [PATCH 01/18] Structured suggestion for `extern crate foo` when `foo` isn't resolved in import When encountering a name in an import that could have come from a crate that wasn't imported, use a structured suggestion to suggest `extern crate foo;` pointing at the right place in the crate. When encountering `_` in an import, do not suggest `extern crate _;`. ``` error[E0432]: unresolved import `spam` --> $DIR/import-from-missing-star-3.rs:2:9 | LL | use spam::*; | ^^^^ maybe a missing crate `spam`? | help: consider importing the `spam` crate | LL + extern crate spam; | ``` --- compiler/rustc_interface/src/passes.rs | 8 +++++++- compiler/rustc_resolve/src/diagnostics.rs | 11 ++++++---- compiler/rustc_resolve/src/lib.rs | 6 ++++++ .../ice-unresolved-import-100241.stderr | 5 ++++- .../unresolved-import-recovery.stderr | 5 ++++- tests/rustdoc-ui/issues/issue-61732.stderr | 5 ++++- .../field-attributes-vis-unresolved.stderr | 10 ++++++++-- tests/ui/error-codes/E0432.stderr | 5 ++++- .../imports/import-from-missing-star-2.stderr | 5 ++++- .../imports/import-from-missing-star-3.stderr | 10 ++++++++-- .../imports/import-from-missing-star.stderr | 5 ++++- tests/ui/imports/import3.stderr | 5 ++++- tests/ui/imports/issue-109343.stderr | 5 ++++- tests/ui/imports/issue-1697.rs | 2 +- tests/ui/imports/issue-1697.stderr | 5 ++++- tests/ui/imports/issue-33464.stderr | 15 +++++++++++--- tests/ui/imports/issue-36881.stderr | 5 ++++- tests/ui/imports/issue-37887.stderr | 5 ++++- tests/ui/imports/issue-53269.stderr | 5 ++++- tests/ui/imports/issue-55457.stderr | 5 ++++- tests/ui/imports/issue-81413.stderr | 5 ++++- tests/ui/imports/tool-mod-child.stderr | 20 +++++++++++++++---- .../ui/imports/unresolved-imports-used.stderr | 20 +++++++++++++++---- .../keyword-extern-as-identifier-use.stderr | 5 ++++- tests/ui/privacy/restricted/test.stderr | 5 ++++- .../resolve/editions-crate-root-2015.stderr | 10 ++++++++-- tests/ui/resolve/extern-prelude-fail.stderr | 10 ++++++++-- tests/ui/resolve/issue-82865.stderr | 5 ++++- .../ui/resolve/resolve-bad-visibility.stderr | 10 ++++++++-- .../ui/underscore-imports/issue-110164.stderr | 16 ++++----------- .../unresolved-asterisk-imports.stderr | 5 ++++- tests/ui/unresolved/unresolved-import.rs | 3 ++- tests/ui/unresolved/unresolved-import.stderr | 15 ++++++++------ 33 files changed, 192 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index a5e25b917dc5b..8c99b1f444766 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -544,7 +544,13 @@ fn resolver_for_lowering_raw<'tcx>( let arenas = Resolver::arenas(); let _ = tcx.registered_tools(()); // Uses `crate_for_resolver`. let (krate, pre_configured_attrs) = tcx.crate_for_resolver(()).steal(); - let mut resolver = Resolver::new(tcx, &pre_configured_attrs, krate.spans.inner_span, &arenas); + let mut resolver = Resolver::new( + tcx, + &pre_configured_attrs, + krate.spans.inner_span, + krate.spans.inject_use_span, + &arenas, + ); let krate = configure_and_expand(krate, &pre_configured_attrs, &mut resolver); // Make sure we don't mutate the cstore from here on. diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index ccb7223b62128..8080bb60e415e 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2026,14 +2026,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Applicability::MaybeIncorrect, )), ) + } else if ident.name == kw::Underscore { + (format!("`_` is not a valid crate or module name"), None) } else if self.tcx.sess.is_rust_2015() { ( format!("you might be missing crate `{ident}`"), Some(( - vec![], - format!( - "consider adding `extern crate {ident}` to use the `{ident}` crate" - ), + vec![( + self.current_crate_outer_attr_insert_span, + format!("extern crate {ident};\n"), + )], + format!("consider importing the `{ident}` crate"), Applicability::MaybeIncorrect, )), ) diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 6aca0545e64de..6405cb82493b5 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1180,6 +1180,10 @@ pub struct Resolver<'a, 'tcx> { /// Simplified analogue of module `resolutions` but in trait impls, excluding glob delegations. /// Needed because glob delegations exclude explicitly defined names. impl_binding_keys: FxHashMap>, + + /// This is the `Span` where an `extern crate foo;` suggestion would be inserted, if `foo` + /// could be a crate that wasn't imported. For diagnostics use only. + current_crate_outer_attr_insert_span: Span, } /// Nothing really interesting here; it just provides memory for the rest of the crate. @@ -1342,6 +1346,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { tcx: TyCtxt<'tcx>, attrs: &[ast::Attribute], crate_span: Span, + current_crate_outer_attr_insert_span: Span, arenas: &'a ResolverArenas<'a>, ) -> Resolver<'a, 'tcx> { let root_def_id = CRATE_DEF_ID.to_def_id(); @@ -1525,6 +1530,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { glob_delegation_invoc_ids: Default::default(), impl_unexpanded_invocations: Default::default(), impl_binding_keys: Default::default(), + current_crate_outer_attr_insert_span, }; let root_parent_scope = ParentScope::module(graph_root, &resolver); diff --git a/tests/rustdoc-ui/ice-unresolved-import-100241.stderr b/tests/rustdoc-ui/ice-unresolved-import-100241.stderr index e23e0f01fabe3..2eebedba9a5ee 100644 --- a/tests/rustdoc-ui/ice-unresolved-import-100241.stderr +++ b/tests/rustdoc-ui/ice-unresolved-import-100241.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `inner` LL | pub use inner::S; | ^^^^^ you might be missing crate `inner` | - = help: consider adding `extern crate inner` to use the `inner` crate +help: consider importing the `inner` crate + | +LL + extern crate inner; + | error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr b/tests/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr index a74e6b7393891..e68943192130d 100644 --- a/tests/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr +++ b/tests/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr @@ -4,7 +4,10 @@ error[E0433]: failed to resolve: you might be missing crate `unresolved_crate` LL | use unresolved_crate::module::Name; | ^^^^^^^^^^^^^^^^ you might be missing crate `unresolved_crate` | - = help: consider adding `extern crate unresolved_crate` to use the `unresolved_crate` crate +help: consider importing the `unresolved_crate` crate + | +LL + extern crate unresolved_crate; + | error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/issues/issue-61732.stderr b/tests/rustdoc-ui/issues/issue-61732.stderr index f49d53b0d9ad0..0aa7d558c307d 100644 --- a/tests/rustdoc-ui/issues/issue-61732.stderr +++ b/tests/rustdoc-ui/issues/issue-61732.stderr @@ -4,7 +4,10 @@ error[E0433]: failed to resolve: you might be missing crate `r#mod` LL | pub(in crate::r#mod) fn main() {} | ^^^^^ you might be missing crate `r#mod` | - = help: consider adding `extern crate r#mod` to use the `r#mod` crate +help: consider importing the `r#mod` crate + | +LL + extern crate r#mod; + | error: aborting due to 1 previous error diff --git a/tests/ui/attributes/field-attributes-vis-unresolved.stderr b/tests/ui/attributes/field-attributes-vis-unresolved.stderr index 819cd859ae909..f8610c08b0220 100644 --- a/tests/ui/attributes/field-attributes-vis-unresolved.stderr +++ b/tests/ui/attributes/field-attributes-vis-unresolved.stderr @@ -4,7 +4,10 @@ error[E0433]: failed to resolve: you might be missing crate `nonexistent` LL | pub(in nonexistent) field: u8 | ^^^^^^^^^^^ you might be missing crate `nonexistent` | - = help: consider adding `extern crate nonexistent` to use the `nonexistent` crate +help: consider importing the `nonexistent` crate + | +LL + extern crate nonexistent; + | error[E0433]: failed to resolve: you might be missing crate `nonexistent` --> $DIR/field-attributes-vis-unresolved.rs:22:12 @@ -12,7 +15,10 @@ error[E0433]: failed to resolve: you might be missing crate `nonexistent` LL | pub(in nonexistent) u8 | ^^^^^^^^^^^ you might be missing crate `nonexistent` | - = help: consider adding `extern crate nonexistent` to use the `nonexistent` crate +help: consider importing the `nonexistent` crate + | +LL + extern crate nonexistent; + | error: aborting due to 2 previous errors diff --git a/tests/ui/error-codes/E0432.stderr b/tests/ui/error-codes/E0432.stderr index a0b17e35c94a8..36fefc95897d6 100644 --- a/tests/ui/error-codes/E0432.stderr +++ b/tests/ui/error-codes/E0432.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `something` LL | use something::Foo; | ^^^^^^^^^ you might be missing crate `something` | - = help: consider adding `extern crate something` to use the `something` crate +help: consider importing the `something` crate + | +LL + extern crate something; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/import-from-missing-star-2.stderr b/tests/ui/imports/import-from-missing-star-2.stderr index 59b000a43822b..dd35627c68465 100644 --- a/tests/ui/imports/import-from-missing-star-2.stderr +++ b/tests/ui/imports/import-from-missing-star-2.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `spam` LL | use spam::*; | ^^^^ you might be missing crate `spam` | - = help: consider adding `extern crate spam` to use the `spam` crate +help: consider importing the `spam` crate + | +LL + extern crate spam; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/import-from-missing-star-3.stderr b/tests/ui/imports/import-from-missing-star-3.stderr index 23df6b354450b..1e2412b095975 100644 --- a/tests/ui/imports/import-from-missing-star-3.stderr +++ b/tests/ui/imports/import-from-missing-star-3.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `spam` LL | use spam::*; | ^^^^ you might be missing crate `spam` | - = help: consider adding `extern crate spam` to use the `spam` crate +help: consider importing the `spam` crate + | +LL + extern crate spam; + | error[E0432]: unresolved import `spam` --> $DIR/import-from-missing-star-3.rs:27:13 @@ -12,7 +15,10 @@ error[E0432]: unresolved import `spam` LL | use spam::*; | ^^^^ you might be missing crate `spam` | - = help: consider adding `extern crate spam` to use the `spam` crate +help: consider importing the `spam` crate + | +LL + extern crate spam; + | error: aborting due to 2 previous errors diff --git a/tests/ui/imports/import-from-missing-star.stderr b/tests/ui/imports/import-from-missing-star.stderr index b311527bc28e4..c9bb9baeb4dde 100644 --- a/tests/ui/imports/import-from-missing-star.stderr +++ b/tests/ui/imports/import-from-missing-star.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `spam` LL | use spam::*; | ^^^^ you might be missing crate `spam` | - = help: consider adding `extern crate spam` to use the `spam` crate +help: consider importing the `spam` crate + | +LL + extern crate spam; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/import3.stderr b/tests/ui/imports/import3.stderr index 06260ef9ebc75..157b5b6356687 100644 --- a/tests/ui/imports/import3.stderr +++ b/tests/ui/imports/import3.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `main` LL | use main::bar; | ^^^^ you might be missing crate `main` | - = help: consider adding `extern crate main` to use the `main` crate +help: consider importing the `main` crate + | +LL + extern crate main; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-109343.stderr b/tests/ui/imports/issue-109343.stderr index fe06eddeada69..e66528e8df528 100644 --- a/tests/ui/imports/issue-109343.stderr +++ b/tests/ui/imports/issue-109343.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `unresolved` LL | pub use unresolved::f; | ^^^^^^^^^^ you might be missing crate `unresolved` | - = help: consider adding `extern crate unresolved` to use the `unresolved` crate +help: consider importing the `unresolved` crate + | +LL + extern crate unresolved; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-1697.rs b/tests/ui/imports/issue-1697.rs index 8ec48d4d28628..019237611df0d 100644 --- a/tests/ui/imports/issue-1697.rs +++ b/tests/ui/imports/issue-1697.rs @@ -3,6 +3,6 @@ use unresolved::*; //~^ ERROR unresolved import `unresolved` [E0432] //~| NOTE you might be missing crate `unresolved` -//~| HELP consider adding `extern crate unresolved` to use the `unresolved` crate +//~| HELP consider importing the `unresolved` crate fn main() {} diff --git a/tests/ui/imports/issue-1697.stderr b/tests/ui/imports/issue-1697.stderr index df2957b8f2b13..ec0d94bd672f9 100644 --- a/tests/ui/imports/issue-1697.stderr +++ b/tests/ui/imports/issue-1697.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `unresolved` LL | use unresolved::*; | ^^^^^^^^^^ you might be missing crate `unresolved` | - = help: consider adding `extern crate unresolved` to use the `unresolved` crate +help: consider importing the `unresolved` crate + | +LL + extern crate unresolved; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-33464.stderr b/tests/ui/imports/issue-33464.stderr index 17cc0e4469e43..28fbcee401f91 100644 --- a/tests/ui/imports/issue-33464.stderr +++ b/tests/ui/imports/issue-33464.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `abc` LL | use abc::one_el; | ^^^ you might be missing crate `abc` | - = help: consider adding `extern crate abc` to use the `abc` crate +help: consider importing the `abc` crate + | +LL + extern crate abc; + | error[E0432]: unresolved import `abc` --> $DIR/issue-33464.rs:5:5 @@ -12,7 +15,10 @@ error[E0432]: unresolved import `abc` LL | use abc::{a, bbb, cccccc}; | ^^^ you might be missing crate `abc` | - = help: consider adding `extern crate abc` to use the `abc` crate +help: consider importing the `abc` crate + | +LL + extern crate abc; + | error[E0432]: unresolved import `a_very_long_name` --> $DIR/issue-33464.rs:7:5 @@ -20,7 +26,10 @@ error[E0432]: unresolved import `a_very_long_name` LL | use a_very_long_name::{el, el2}; | ^^^^^^^^^^^^^^^^ you might be missing crate `a_very_long_name` | - = help: consider adding `extern crate a_very_long_name` to use the `a_very_long_name` crate +help: consider importing the `a_very_long_name` crate + | +LL + extern crate a_very_long_name; + | error: aborting due to 3 previous errors diff --git a/tests/ui/imports/issue-36881.stderr b/tests/ui/imports/issue-36881.stderr index 3c136df83fe4c..004836e072c5e 100644 --- a/tests/ui/imports/issue-36881.stderr +++ b/tests/ui/imports/issue-36881.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `issue_36881_aux` LL | use issue_36881_aux::Foo; | ^^^^^^^^^^^^^^^ you might be missing crate `issue_36881_aux` | - = help: consider adding `extern crate issue_36881_aux` to use the `issue_36881_aux` crate +help: consider importing the `issue_36881_aux` crate + | +LL + extern crate issue_36881_aux; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-37887.stderr b/tests/ui/imports/issue-37887.stderr index 36020707405f1..02c2c80326217 100644 --- a/tests/ui/imports/issue-37887.stderr +++ b/tests/ui/imports/issue-37887.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `test` LL | use test::*; | ^^^^ you might be missing crate `test` | - = help: consider adding `extern crate test` to use the `test` crate +help: consider importing the `test` crate + | +LL + extern crate test; + | error[E0658]: use of unstable library feature 'test' --> $DIR/issue-37887.rs:2:5 diff --git a/tests/ui/imports/issue-53269.stderr b/tests/ui/imports/issue-53269.stderr index 317b3c633a65f..d25d85bf46f02 100644 --- a/tests/ui/imports/issue-53269.stderr +++ b/tests/ui/imports/issue-53269.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `nonexistent_module` LL | use nonexistent_module::mac; | ^^^^^^^^^^^^^^^^^^ you might be missing crate `nonexistent_module` | - = help: consider adding `extern crate nonexistent_module` to use the `nonexistent_module` crate +help: consider importing the `nonexistent_module` crate + | +LL + extern crate nonexistent_module; + | error[E0659]: `mac` is ambiguous --> $DIR/issue-53269.rs:8:5 diff --git a/tests/ui/imports/issue-55457.stderr b/tests/ui/imports/issue-55457.stderr index e9126e6575c0d..9c99b6a20de7c 100644 --- a/tests/ui/imports/issue-55457.stderr +++ b/tests/ui/imports/issue-55457.stderr @@ -13,7 +13,10 @@ error[E0432]: unresolved import `non_existent` LL | use non_existent::non_existent; | ^^^^^^^^^^^^ you might be missing crate `non_existent` | - = help: consider adding `extern crate non_existent` to use the `non_existent` crate +help: consider importing the `non_existent` crate + | +LL + extern crate non_existent; + | error: aborting due to 2 previous errors diff --git a/tests/ui/imports/issue-81413.stderr b/tests/ui/imports/issue-81413.stderr index 321b3695d2cc0..aa1246c1d2f50 100644 --- a/tests/ui/imports/issue-81413.stderr +++ b/tests/ui/imports/issue-81413.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `doesnt_exist` LL | pub use doesnt_exist::*; | ^^^^^^^^^^^^ you might be missing crate `doesnt_exist` | - = help: consider adding `extern crate doesnt_exist` to use the `doesnt_exist` crate +help: consider importing the `doesnt_exist` crate + | +LL + extern crate doesnt_exist; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/tool-mod-child.stderr b/tests/ui/imports/tool-mod-child.stderr index 764256e76f045..ec110ccd75dbc 100644 --- a/tests/ui/imports/tool-mod-child.stderr +++ b/tests/ui/imports/tool-mod-child.stderr @@ -4,7 +4,10 @@ error[E0433]: failed to resolve: you might be missing crate `clippy` LL | use clippy::a::b; | ^^^^^^ you might be missing crate `clippy` | - = help: consider adding `extern crate clippy` to use the `clippy` crate +help: consider importing the `clippy` crate + | +LL + extern crate clippy; + | error[E0432]: unresolved import `clippy` --> $DIR/tool-mod-child.rs:1:5 @@ -12,7 +15,10 @@ error[E0432]: unresolved import `clippy` LL | use clippy::a; | ^^^^^^ you might be missing crate `clippy` | - = help: consider adding `extern crate clippy` to use the `clippy` crate +help: consider importing the `clippy` crate + | +LL + extern crate clippy; + | error[E0433]: failed to resolve: you might be missing crate `rustdoc` --> $DIR/tool-mod-child.rs:5:5 @@ -20,7 +26,10 @@ error[E0433]: failed to resolve: you might be missing crate `rustdoc` LL | use rustdoc::a::b; | ^^^^^^^ you might be missing crate `rustdoc` | - = help: consider adding `extern crate rustdoc` to use the `rustdoc` crate +help: consider importing the `rustdoc` crate + | +LL + extern crate rustdoc; + | error[E0432]: unresolved import `rustdoc` --> $DIR/tool-mod-child.rs:4:5 @@ -28,7 +37,10 @@ error[E0432]: unresolved import `rustdoc` LL | use rustdoc::a; | ^^^^^^^ you might be missing crate `rustdoc` | - = help: consider adding `extern crate rustdoc` to use the `rustdoc` crate +help: consider importing the `rustdoc` crate + | +LL + extern crate rustdoc; + | error: aborting due to 4 previous errors diff --git a/tests/ui/imports/unresolved-imports-used.stderr b/tests/ui/imports/unresolved-imports-used.stderr index 1cbc2356320f7..4bf02ff6e3a9f 100644 --- a/tests/ui/imports/unresolved-imports-used.stderr +++ b/tests/ui/imports/unresolved-imports-used.stderr @@ -16,7 +16,10 @@ error[E0432]: unresolved import `foo` LL | use foo::bar; | ^^^ you might be missing crate `foo` | - = help: consider adding `extern crate foo` to use the `foo` crate +help: consider importing the `foo` crate + | +LL + extern crate foo; + | error[E0432]: unresolved import `baz` --> $DIR/unresolved-imports-used.rs:12:5 @@ -24,7 +27,10 @@ error[E0432]: unresolved import `baz` LL | use baz::*; | ^^^ you might be missing crate `baz` | - = help: consider adding `extern crate baz` to use the `baz` crate +help: consider importing the `baz` crate + | +LL + extern crate baz; + | error[E0432]: unresolved import `foo2` --> $DIR/unresolved-imports-used.rs:14:5 @@ -32,7 +38,10 @@ error[E0432]: unresolved import `foo2` LL | use foo2::bar2; | ^^^^ you might be missing crate `foo2` | - = help: consider adding `extern crate foo2` to use the `foo2` crate +help: consider importing the `foo2` crate + | +LL + extern crate foo2; + | error[E0432]: unresolved import `baz2` --> $DIR/unresolved-imports-used.rs:15:5 @@ -40,7 +49,10 @@ error[E0432]: unresolved import `baz2` LL | use baz2::*; | ^^^^ you might be missing crate `baz2` | - = help: consider adding `extern crate baz2` to use the `baz2` crate +help: consider importing the `baz2` crate + | +LL + extern crate baz2; + | error[E0603]: function `quz` is private --> $DIR/unresolved-imports-used.rs:9:10 diff --git a/tests/ui/keyword/extern/keyword-extern-as-identifier-use.stderr b/tests/ui/keyword/extern/keyword-extern-as-identifier-use.stderr index a647ca27f1c26..f23f855c9e8e8 100644 --- a/tests/ui/keyword/extern/keyword-extern-as-identifier-use.stderr +++ b/tests/ui/keyword/extern/keyword-extern-as-identifier-use.stderr @@ -15,7 +15,10 @@ error[E0432]: unresolved import `r#extern` LL | use extern::foo; | ^^^^^^ you might be missing crate `r#extern` | - = help: consider adding `extern crate r#extern` to use the `r#extern` crate +help: consider importing the `r#extern` crate + | +LL + extern crate r#extern; + | error: aborting due to 2 previous errors diff --git a/tests/ui/privacy/restricted/test.stderr b/tests/ui/privacy/restricted/test.stderr index a48bb671d9f88..5deaffbdbf3f8 100644 --- a/tests/ui/privacy/restricted/test.stderr +++ b/tests/ui/privacy/restricted/test.stderr @@ -4,7 +4,10 @@ error[E0433]: failed to resolve: you might be missing crate `bad` LL | pub(in bad::path) mod m1 {} | ^^^ you might be missing crate `bad` | - = help: consider adding `extern crate bad` to use the `bad` crate +help: consider importing the `bad` crate + | +LL + extern crate bad; + | error[E0742]: visibilities can only be restricted to ancestor modules --> $DIR/test.rs:51:12 diff --git a/tests/ui/resolve/editions-crate-root-2015.stderr b/tests/ui/resolve/editions-crate-root-2015.stderr index 74fb7e6019ff5..7a842aca0fd27 100644 --- a/tests/ui/resolve/editions-crate-root-2015.stderr +++ b/tests/ui/resolve/editions-crate-root-2015.stderr @@ -4,7 +4,10 @@ error[E0433]: failed to resolve: you might be missing crate `nonexistant` LL | fn global_inner(_: ::nonexistant::Foo) { | ^^^^^^^^^^^ you might be missing crate `nonexistant` | - = help: consider adding `extern crate nonexistant` to use the `nonexistant` crate +help: consider importing the `nonexistant` crate + | +LL + extern crate nonexistant; + | error[E0433]: failed to resolve: you might be missing crate `nonexistant` --> $DIR/editions-crate-root-2015.rs:7:30 @@ -12,7 +15,10 @@ error[E0433]: failed to resolve: you might be missing crate `nonexistant` LL | fn crate_inner(_: crate::nonexistant::Foo) { | ^^^^^^^^^^^ you might be missing crate `nonexistant` | - = help: consider adding `extern crate nonexistant` to use the `nonexistant` crate +help: consider importing the `nonexistant` crate + | +LL + extern crate nonexistant; + | error[E0412]: cannot find type `nonexistant` in the crate root --> $DIR/editions-crate-root-2015.rs:11:25 diff --git a/tests/ui/resolve/extern-prelude-fail.stderr b/tests/ui/resolve/extern-prelude-fail.stderr index 4c2d5abb78247..77c10f5f995bc 100644 --- a/tests/ui/resolve/extern-prelude-fail.stderr +++ b/tests/ui/resolve/extern-prelude-fail.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `extern_prelude` LL | use extern_prelude::S; | ^^^^^^^^^^^^^^ you might be missing crate `extern_prelude` | - = help: consider adding `extern crate extern_prelude` to use the `extern_prelude` crate +help: consider importing the `extern_prelude` crate + | +LL + extern crate extern_prelude; + | error[E0433]: failed to resolve: you might be missing crate `extern_prelude` --> $DIR/extern-prelude-fail.rs:8:15 @@ -12,7 +15,10 @@ error[E0433]: failed to resolve: you might be missing crate `extern_prelude` LL | let s = ::extern_prelude::S; | ^^^^^^^^^^^^^^ you might be missing crate `extern_prelude` | - = help: consider adding `extern crate extern_prelude` to use the `extern_prelude` crate +help: consider importing the `extern_prelude` crate + | +LL + extern crate extern_prelude; + | error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/issue-82865.stderr b/tests/ui/resolve/issue-82865.stderr index ce0061a2b665e..bc7e0f0798177 100644 --- a/tests/ui/resolve/issue-82865.stderr +++ b/tests/ui/resolve/issue-82865.stderr @@ -4,7 +4,10 @@ error[E0433]: failed to resolve: you might be missing crate `x` LL | use x::y::z; | ^ you might be missing crate `x` | - = help: consider adding `extern crate x` to use the `x` crate +help: consider importing the `x` crate + | +LL + extern crate x; + | error[E0599]: no function or associated item named `z` found for struct `Box<_, _>` in the current scope --> $DIR/issue-82865.rs:8:10 diff --git a/tests/ui/resolve/resolve-bad-visibility.stderr b/tests/ui/resolve/resolve-bad-visibility.stderr index 8e4757354030e..281e5afb22302 100644 --- a/tests/ui/resolve/resolve-bad-visibility.stderr +++ b/tests/ui/resolve/resolve-bad-visibility.stderr @@ -22,7 +22,10 @@ error[E0433]: failed to resolve: you might be missing crate `nonexistent` LL | pub(in nonexistent) struct G; | ^^^^^^^^^^^ you might be missing crate `nonexistent` | - = help: consider adding `extern crate nonexistent` to use the `nonexistent` crate +help: consider importing the `nonexistent` crate + | +LL + extern crate nonexistent; + | error[E0433]: failed to resolve: you might be missing crate `too_soon` --> $DIR/resolve-bad-visibility.rs:8:8 @@ -30,7 +33,10 @@ error[E0433]: failed to resolve: you might be missing crate `too_soon` LL | pub(in too_soon) struct H; | ^^^^^^^^ you might be missing crate `too_soon` | - = help: consider adding `extern crate too_soon` to use the `too_soon` crate +help: consider importing the `too_soon` crate + | +LL + extern crate too_soon; + | error: aborting due to 5 previous errors diff --git a/tests/ui/underscore-imports/issue-110164.stderr b/tests/ui/underscore-imports/issue-110164.stderr index 240742996e12a..d8a4b6bbb7549 100644 --- a/tests/ui/underscore-imports/issue-110164.stderr +++ b/tests/ui/underscore-imports/issue-110164.stderr @@ -38,33 +38,25 @@ error[E0432]: unresolved import `_` --> $DIR/issue-110164.rs:8:5 | LL | use _::*; - | ^ you might be missing crate `_` - | - = help: consider adding `extern crate _` to use the `_` crate + | ^ `_` is not a valid crate or module name error[E0432]: unresolved import `_` --> $DIR/issue-110164.rs:5:5 | LL | use _::a; - | ^ you might be missing crate `_` - | - = help: consider adding `extern crate _` to use the `_` crate + | ^ `_` is not a valid crate or module name error[E0432]: unresolved import `_` --> $DIR/issue-110164.rs:13:9 | LL | use _::a; - | ^ you might be missing crate `_` - | - = help: consider adding `extern crate _` to use the `_` crate + | ^ `_` is not a valid crate or module name error[E0432]: unresolved import `_` --> $DIR/issue-110164.rs:16:9 | LL | use _::*; - | ^ you might be missing crate `_` - | - = help: consider adding `extern crate _` to use the `_` crate + | ^ `_` is not a valid crate or module name error: aborting due to 10 previous errors diff --git a/tests/ui/unresolved/unresolved-asterisk-imports.stderr b/tests/ui/unresolved/unresolved-asterisk-imports.stderr index 299ec699775e9..ed01f3fdbea41 100644 --- a/tests/ui/unresolved/unresolved-asterisk-imports.stderr +++ b/tests/ui/unresolved/unresolved-asterisk-imports.stderr @@ -4,7 +4,10 @@ error[E0432]: unresolved import `not_existing_crate` LL | use not_existing_crate::*; | ^^^^^^^^^^^^^^^^^^ you might be missing crate `not_existing_crate` | - = help: consider adding `extern crate not_existing_crate` to use the `not_existing_crate` crate +help: consider importing the `not_existing_crate` crate + | +LL + extern crate not_existing_crate; + | error: aborting due to 1 previous error diff --git a/tests/ui/unresolved/unresolved-import.rs b/tests/ui/unresolved/unresolved-import.rs index e8f3b323e3375..ee520d65e6f4a 100644 --- a/tests/ui/unresolved/unresolved-import.rs +++ b/tests/ui/unresolved/unresolved-import.rs @@ -1,7 +1,8 @@ use foo::bar; //~^ ERROR unresolved import `foo` [E0432] //~| NOTE you might be missing crate `foo` -//~| HELP consider adding `extern crate foo` to use the `foo` crate +//~| HELP consider importing the `foo` crate +//~| SUGGESTION extern crate foo; use bar::Baz as x; //~^ ERROR unresolved import `bar::Baz` [E0432] diff --git a/tests/ui/unresolved/unresolved-import.stderr b/tests/ui/unresolved/unresolved-import.stderr index 7b03717c827ce..a1ff2f19eb640 100644 --- a/tests/ui/unresolved/unresolved-import.stderr +++ b/tests/ui/unresolved/unresolved-import.stderr @@ -4,10 +4,13 @@ error[E0432]: unresolved import `foo` LL | use foo::bar; | ^^^ you might be missing crate `foo` | - = help: consider adding `extern crate foo` to use the `foo` crate +help: consider importing the `foo` crate + | +LL + extern crate foo; + | error[E0432]: unresolved import `bar::Baz` - --> $DIR/unresolved-import.rs:6:5 + --> $DIR/unresolved-import.rs:7:5 | LL | use bar::Baz as x; | ^^^^^---^^^^^ @@ -16,7 +19,7 @@ LL | use bar::Baz as x; | no `Baz` in `bar` error[E0432]: unresolved import `food::baz` - --> $DIR/unresolved-import.rs:12:5 + --> $DIR/unresolved-import.rs:13:5 | LL | use food::baz; | ^^^^^^--- @@ -25,7 +28,7 @@ LL | use food::baz; | no `baz` in `food` error[E0432]: unresolved import `food::beens` - --> $DIR/unresolved-import.rs:18:12 + --> $DIR/unresolved-import.rs:19:12 | LL | use food::{beens as Foo}; | -----^^^^^^^ @@ -34,13 +37,13 @@ LL | use food::{beens as Foo}; | help: a similar name exists in the module: `beans` error[E0432]: unresolved import `MyEnum` - --> $DIR/unresolved-import.rs:43:9 + --> $DIR/unresolved-import.rs:44:9 | LL | use MyEnum::*; | ^^^^^^ help: a similar path exists: `self::MyEnum` error[E0432]: unresolved import `Enum` - --> $DIR/unresolved-import.rs:54:9 + --> $DIR/unresolved-import.rs:55:9 | LL | use Enum::*; | ^^^^ help: a similar path exists: `self::Enum` From 67a08b5deba5a6fdf5fd4acfc095851f79172e59 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 30 Jul 2024 15:02:40 +0000 Subject: [PATCH 02/18] Attribute checking simplifications remove an unused boolean and then merge two big matches into one --- compiler/rustc_passes/src/check_attr.rs | 546 ++++++++---------------- 1 file changed, 179 insertions(+), 367 deletions(-) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a58c57041f830..07c0a116bfeaf 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -207,37 +207,34 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::rustc_safe_intrinsic] => { self.check_rustc_safe_intrinsic(hir_id, attr, span, target) } - _ => true, - }; - - // lint-only checks - match attr.name_or_empty() { - sym::cold => self.check_cold(hir_id, attr, span, target), - sym::link => self.check_link(hir_id, attr, span, target), - sym::link_name => self.check_link_name(hir_id, attr, span, target), - sym::link_section => self.check_link_section(hir_id, attr, span, target), - sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target), - sym::deprecated => self.check_deprecated(hir_id, attr, span, target), - sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target), - sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod), - sym::macro_export => self.check_macro_export(hir_id, attr, target), - sym::ignore | sym::should_panic => { + [sym::cold] => self.check_cold(hir_id, attr, span, target), + [sym::link] => self.check_link(hir_id, attr, span, target), + [sym::link_name] => self.check_link_name(hir_id, attr, span, target), + [sym::link_section] => self.check_link_section(hir_id, attr, span, target), + [sym::no_mangle] => self.check_no_mangle(hir_id, attr, span, target), + [sym::deprecated] => self.check_deprecated(hir_id, attr, span, target), + [sym::macro_use] | [sym::macro_escape] => { + self.check_macro_use(hir_id, attr, target) + } + [sym::path] => self.check_generic_attr(hir_id, attr, target, Target::Mod), + [sym::macro_export] => self.check_macro_export(hir_id, attr, target), + [sym::ignore] | [sym::should_panic] => { self.check_generic_attr(hir_id, attr, target, Target::Fn) } - sym::automatically_derived => { + [sym::automatically_derived] => { self.check_generic_attr(hir_id, attr, target, Target::Impl) } - sym::no_implicit_prelude => { + [sym::no_implicit_prelude] => { self.check_generic_attr(hir_id, attr, target, Target::Mod) } - sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id), - sym::proc_macro => { + [sym::rustc_object_lifetime_default] => self.check_object_lifetime_default(hir_id), + [sym::proc_macro] => { self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) } - sym::proc_macro_attribute => { + [sym::proc_macro_attribute] => { self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute); } - sym::proc_macro_derive => { + [sym::proc_macro_derive] => { self.check_generic_attr(hir_id, attr, target, Target::Fn); self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) } @@ -297,7 +294,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl. - fn check_do_not_recommend(&self, attr_span: Span, hir_id: HirId, target: Target) -> bool { + fn check_do_not_recommend(&self, attr_span: Span, hir_id: HirId, target: Target) { if !matches!(target, Target::Impl) { self.tcx.emit_node_span_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, @@ -306,16 +303,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::IncorrectDoNotRecommendLocation, ); } - true } /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition - fn check_diagnostic_on_unimplemented( - &self, - attr_span: Span, - hir_id: HirId, - target: Target, - ) -> bool { + fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) { if !matches!(target, Target::Trait) { self.tcx.emit_node_span_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, @@ -324,68 +315,60 @@ impl<'tcx> CheckAttrVisitor<'tcx> { DiagnosticOnUnimplementedOnlyForTraits, ); } - true } - /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. - fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { + /// Checks if an `#[inline]` is applied to a function or a closure. + fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { Target::Fn | Target::Closure - | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, + | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {} Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, attr.span, errors::IgnoredInlineAttrFnProto, - ); - true + ) } // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with // just a lint, because we previously erroneously allowed it and some crates used it // accidentally, to be compatible with crates depending on them, we can't throw an // error here. - Target::AssocConst => { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr.span, - errors::IgnoredInlineAttrConstants, - ); - true - } + Target::AssocConst => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredInlineAttrConstants, + ), // FIXME(#80564): Same for fields, arms, and macro defs Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline"); - true + self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline") } _ => { self.dcx().emit_err(errors::InlineNotFnOrClosure { attr_span: attr.span, defn_span: span, }); - false } } } /// Checks that `#[coverage(..)]` is applied to a function/closure/method, /// or to an impl block or module. - fn check_coverage(&self, attr: &Attribute, span: Span, target: Target) -> bool { + fn check_coverage(&self, attr: &Attribute, span: Span, target: Target) { match target { Target::Fn | Target::Closure | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) | Target::Impl - | Target::Mod => true, + | Target::Mod => {} _ => { self.dcx().emit_err(errors::CoverageNotFnOrClosure { attr_span: attr.span, defn_span: span, }); - false } } } @@ -418,7 +401,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, attrs: &[Attribute], - ) -> bool { + ) { // many attributes don't make sense in combination with #[naked]. // Notable attributes that are incompatible with `#[naked]` are: // @@ -468,19 +451,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: other_attr.name_or_empty(), }); - return false; + return; } } - - true } // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[naked]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked"); - true + self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked") } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { @@ -488,7 +468,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); - false } } } @@ -500,17 +479,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, span: Span, target: Target, - ) -> bool { + ) { match target { Target::Fn - | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, + | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {} _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { attr_span: attr.span, defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); - false } } } @@ -536,19 +514,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[collapse_debuginfo]` is applied to a macro. - fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) -> bool { + fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::MacroDef => true, + Target::MacroDef => {} _ => { self.tcx .dcx() .emit_err(errors::CollapseDebuginfo { attr_span: attr.span, defn_span: span }); - false } } } - /// Checks if a `#[track_caller]` is applied to a function. Returns `true` if valid. + /// Checks if a `#[track_caller]` is applied to a function. fn check_track_caller( &self, hir_id: HirId, @@ -556,7 +533,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attrs: &[Attribute], span: Span, target: Target, - ) -> bool { + ) { match target { Target::Fn => { // `#[track_caller]` is not valid on weak lang items because they are called via @@ -572,12 +549,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { name: lang_item, sig_span: sig.span, }); - false - } else { - true } } - Target::Method(..) | Target::ForeignFn | Target::Closure => true, + Target::Method(..) | Target::ForeignFn | Target::Closure => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[track_caller]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible @@ -586,7 +560,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { for attr in attrs { self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller"); } - true } _ => { self.dcx().emit_err(errors::TrackedCallerWrongLocation { @@ -594,62 +567,51 @@ impl<'tcx> CheckAttrVisitor<'tcx> { defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); - false } } } - /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid. - fn check_non_exhaustive( - &self, - hir_id: HirId, - attr: &Attribute, - span: Span, - target: Target, - ) -> bool { + /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. + fn check_non_exhaustive(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { - Target::Struct | Target::Enum | Target::Variant => true, + Target::Struct | Target::Enum | Target::Variant => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[non_exhaustive]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive"); - true } _ => { self.dcx().emit_err(errors::NonExhaustiveWrongLocation { attr_span: attr.span, defn_span: span, }); - false } } } - /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid. - fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { + /// Checks if the `#[marker]` attribute on an `item` is valid. + fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { - Target::Trait => true, + Target::Trait => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[marker]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker"); - true } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { attr_span: attr.span, defn_span: span, }); - false } } } - /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid. + /// Checks if the `#[target_feature]` attribute on `item` is valid. fn check_target_feature( &self, hir_id: HirId, @@ -657,7 +619,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, attrs: &[Attribute], - ) -> bool { + ) { match target { Target::Fn => { // `#[target_feature]` is not allowed in lang items. @@ -674,12 +636,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { name: lang_item, sig_span: sig.span, }); - false - } else { - true } } - Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, + Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {} // FIXME: #[target_feature] was previously erroneously allowed on statements and some // crates used this, so only emit a warning. Target::Statement => { @@ -689,7 +648,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr.span, errors::TargetFeatureOnStatement, ); - true } // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[target_feature]` attribute with just a lint, because we previously @@ -697,7 +655,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature"); - true } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { @@ -705,21 +662,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); - false } } } - /// Checks if the `#[thread_local]` attribute on `item` is valid. Returns `true` if valid. - fn check_thread_local(&self, attr: &Attribute, span: Span, target: Target) -> bool { + /// Checks if the `#[thread_local]` attribute on `item` is valid. + fn check_thread_local(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::ForeignStatic | Target::Static => true, + Target::ForeignStatic | Target::Static => {} _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToStatic { attr_span: attr.span, defn_span: span, }); - false } } } @@ -736,14 +691,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { target: Target, is_list: bool, aliases: &mut FxHashMap, - ) -> bool { + ) { let tcx = self.tcx; let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span()); let attr_str = &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" }); if doc_alias == kw::Empty { tcx.dcx().emit_err(errors::DocAliasEmpty { span, attr_str }); - return false; + return; } let doc_alias_str = doc_alias.as_str(); @@ -752,11 +707,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' ')) { tcx.dcx().emit_err(errors::DocAliasBadChar { span, attr_str, char_: c }); - return false; + return; } if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') { tcx.dcx().emit_err(errors::DocAliasStartEnd { span, attr_str }); - return false; + return; } let span = meta.span(); @@ -781,7 +736,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } // we check the validity of params elsewhere - Target::Param => return false, + Target::Param => return, Target::Expression | Target::Statement | Target::Arm @@ -814,12 +769,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | Target::ExprField => None, } { tcx.dcx().emit_err(errors::DocAliasBadLocation { span, attr_str, location }); - return false; + return; } let item_name = self.tcx.hir().name(hir_id); if item_name == doc_alias { tcx.dcx().emit_err(errors::DocAliasNotAnAlias { span, attr_str }); - return false; + return; } if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) { self.tcx.emit_node_span_lint( @@ -829,7 +784,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::DocAliasDuplicated { first_defn: *entry.entry.get() }, ); } - true } fn check_doc_alias( @@ -838,46 +792,39 @@ impl<'tcx> CheckAttrVisitor<'tcx> { hir_id: HirId, target: Target, aliases: &mut FxHashMap, - ) -> bool { + ) { if let Some(values) = meta.meta_item_list() { - let mut errors = 0; for v in values { match v.lit() { Some(l) => match l.kind { LitKind::Str(s, _) => { - if !self.check_doc_alias_value(v, s, hir_id, target, true, aliases) { - errors += 1; - } + self.check_doc_alias_value(v, s, hir_id, target, true, aliases); } _ => { self.tcx .dcx() .emit_err(errors::DocAliasNotStringLiteral { span: v.span() }); - errors += 1; } }, None => { self.tcx .dcx() .emit_err(errors::DocAliasNotStringLiteral { span: v.span() }); - errors += 1; } } } - errors == 0 } else if let Some(doc_alias) = meta.value_str() { self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases) } else { self.dcx().emit_err(errors::DocAliasMalformed { span: meta.span() }); - false } } - fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { + fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) { let doc_keyword = meta.value_str().unwrap_or(kw::Empty); if doc_keyword == kw::Empty { self.doc_attr_str_error(meta, "keyword"); - return false; + return; } let item_kind = match self.tcx.hir_node(hir_id) { hir::Node::Item(item) => Some(&item.kind), @@ -887,12 +834,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Some(ItemKind::Mod(module)) => { if !module.item_ids.is_empty() { self.dcx().emit_err(errors::DocKeywordEmptyMod { span: meta.span() }); - return false; + return; } } _ => { self.dcx().emit_err(errors::DocKeywordNotMod { span: meta.span() }); - return false; + return; } } if !rustc_lexer::is_ident(doc_keyword.as_str()) { @@ -900,12 +847,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()), doc_keyword, }); - return false; } - true } - fn check_doc_fake_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { + fn check_doc_fake_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) { let item_kind = match self.tcx.hir_node(hir_id) { hir::Node::Item(item) => Some(&item.kind), _ => None, @@ -920,18 +865,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }; if !is_valid { self.dcx().emit_err(errors::DocFakeVariadicNotValid { span: meta.span() }); - return false; } } _ => { self.dcx().emit_err(errors::DocKeywordOnlyImpl { span: meta.span() }); - return false; } } - true } - /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. Returns `true` if valid. + /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. /// /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or /// if there are conflicting attributes for one item. @@ -947,7 +889,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { hir_id: HirId, target: Target, specified_inline: &mut Option<(bool, Span)>, - ) -> bool { + ) { match target { Target::Use | Target::ExternCrate => { let do_inline = meta.name_or_empty() == sym::inline; @@ -960,12 +902,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fluent::passes_doc_inline_conflict_second, ); self.dcx().emit_err(errors::DocKeywordConflict { spans }); - return false; } - true } else { *specified_inline = Some((do_inline, meta.span())); - true } } _ => { @@ -979,7 +918,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { .then(|| self.tcx.hir().span(hir_id)), }, ); - false } } } @@ -990,7 +928,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta: &NestedMetaItem, hir_id: HirId, target: Target, - ) -> bool { + ) { if target != Target::ExternCrate { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, @@ -1002,7 +940,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { .then(|| self.tcx.hir().span(hir_id)), }, ); - return false; + return; } if self.tcx.extern_mod_stmt_cnum(hir_id.owner).is_none() { @@ -1016,10 +954,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { .then(|| self.tcx.hir().span(hir_id)), }, ); - return false; } - - true } /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid. @@ -1064,8 +999,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that `doc(test(...))` attribute contains only valid attributes. Returns `true` if /// valid. - fn check_test_attr(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { - let mut is_valid = true; + fn check_test_attr(&self, meta: &NestedMetaItem, hir_id: HirId) { if let Some(metas) = meta.meta_item_list() { for i_meta in metas { match (i_meta.name_or_empty(), i_meta.meta_item()) { @@ -1079,7 +1013,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { path: rustc_ast_pretty::pprust::path_to_string(&m.path), }, ); - is_valid = false; } (_, None) => { self.tcx.emit_node_span_lint( @@ -1088,7 +1021,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { i_meta.span(), errors::DocTestLiteral, ); - is_valid = false; } } } @@ -1099,28 +1031,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocTestTakesList, ); - is_valid = false; } - is_valid } /// Check that the `#![doc(cfg_hide(...))]` attribute only contains a list of attributes. - /// Returns `true` if valid. - fn check_doc_cfg_hide(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { - if meta.meta_item_list().is_some() { - true - } else { + /// + fn check_doc_cfg_hide(&self, meta: &NestedMetaItem, hir_id: HirId) { + if meta.meta_item_list().is_none() { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), errors::DocCfgHideTakesList, ); - false } } - /// Runs various checks on `#[doc]` attributes. Returns `true` if valid. + /// Runs various checks on `#[doc]` attributes. /// /// `specified_inline` should be initialized to `None` and kept for the scope /// of one item. Read the documentation of [`check_doc_inline`] for more information. @@ -1134,34 +1061,35 @@ impl<'tcx> CheckAttrVisitor<'tcx> { target: Target, specified_inline: &mut Option<(bool, Span)>, aliases: &mut FxHashMap, - ) -> bool { - let mut is_valid = true; - + ) { if let Some(mi) = attr.meta() && let Some(list) = mi.meta_item_list() { for meta in list { if let Some(i_meta) = meta.meta_item() { match i_meta.name_or_empty() { - sym::alias - if !self.check_attr_not_crate_level(meta, hir_id, "alias") - || !self.check_doc_alias(meta, hir_id, target, aliases) => - { - is_valid = false + sym::alias => { + if self.check_attr_not_crate_level(meta, hir_id, "alias") { + self.check_doc_alias(meta, hir_id, target, aliases); + } + } + + sym::keyword => { + if self.check_attr_not_crate_level(meta, hir_id, "keyword") { + self.check_doc_keyword(meta, hir_id); + } } - sym::keyword - if !self.check_attr_not_crate_level(meta, hir_id, "keyword") - || !self.check_doc_keyword(meta, hir_id) => - { - is_valid = false + sym::fake_variadic => { + if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") { + self.check_doc_fake_variadic(meta, hir_id); + } } - sym::fake_variadic - if !self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") - || !self.check_doc_fake_variadic(meta, hir_id) => - { - is_valid = false + sym::test => { + if self.check_attr_crate_level(attr, meta, hir_id) { + self.check_test_attr(meta, hir_id); + } } sym::html_favicon_url @@ -1169,62 +1097,36 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::html_playground_url | sym::issue_tracker_base_url | sym::html_root_url - | sym::html_no_source - | sym::test - | sym::rust_logo - if !self.check_attr_crate_level(attr, meta, hir_id) => - { - is_valid = false; + | sym::html_no_source => { + self.check_attr_crate_level(attr, meta, hir_id); } - sym::cfg_hide - if !self.check_attr_crate_level(attr, meta, hir_id) - || !self.check_doc_cfg_hide(meta, hir_id) => - { - is_valid = false; + sym::cfg_hide => { + if self.check_attr_crate_level(attr, meta, hir_id) { + self.check_doc_cfg_hide(meta, hir_id); + } } - sym::inline | sym::no_inline - if !self.check_doc_inline( - attr, - meta, - hir_id, - target, - specified_inline, - ) => - { - is_valid = false; + sym::inline | sym::no_inline => { + self.check_doc_inline(attr, meta, hir_id, target, specified_inline) } - sym::masked if !self.check_doc_masked(attr, meta, hir_id, target) => { - is_valid = false; - } + sym::masked => self.check_doc_masked(attr, meta, hir_id, target), // no_default_passes: deprecated // passes: deprecated // plugins: removed, but rustdoc warns about it itself - sym::alias - | sym::cfg - | sym::cfg_hide + sym::cfg | sym::hidden - | sym::html_favicon_url - | sym::html_logo_url - | sym::html_no_source - | sym::html_playground_url - | sym::html_root_url - | sym::inline - | sym::issue_tracker_base_url - | sym::keyword - | sym::masked | sym::no_default_passes - | sym::no_inline | sym::notable_trait | sym::passes - | sym::plugins - | sym::fake_variadic => {} + | sym::plugins => {} sym::rust_logo => { - if !self.tcx.features().rustdoc_internals { + if self.check_attr_crate_level(attr, meta, hir_id) + && !self.tcx.features().rustdoc_internals + { feature_err( &self.tcx.sess, sym::rustdoc_internals, @@ -1235,12 +1137,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - sym::test => { - if !self.check_test_attr(meta, hir_id) { - is_valid = false; - } - } - _ => { let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path); if i_meta.has_name(sym::spotlight) { @@ -1282,7 +1178,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::DocTestUnknownAny { path }, ); } - is_valid = false; } } } else { @@ -1292,79 +1187,60 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocInvalid, ); - is_valid = false; } } } - - is_valid } /// Warns against some misuses of `#[pass_by_value]` - fn check_pass_by_value(&self, attr: &Attribute, span: Span, target: Target) -> bool { + fn check_pass_by_value(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Struct | Target::Enum | Target::TyAlias => true, + Target::Struct | Target::Enum | Target::TyAlias => {} _ => { self.dcx().emit_err(errors::PassByValue { attr_span: attr.span, span }); - false } } } - fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) -> bool { + fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Method(MethodKind::Inherent) => true, + Target::Method(MethodKind::Inherent) => {} _ => { self.dcx().emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span }); - false } } } - fn check_has_incoherent_inherent_impls( - &self, - attr: &Attribute, - span: Span, - target: Target, - ) -> bool { + fn check_has_incoherent_inherent_impls(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => { - true - } + Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => {} _ => { self.tcx .dcx() .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span }); - false } } } - fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute], target: Target) -> bool { + fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute], target: Target) { if target != Target::ForeignFn { self.dcx().emit_err(errors::FfiPureInvalidTarget { attr_span }); - return false; + return; } if attrs.iter().any(|a| a.has_name(sym::ffi_const)) { // `#[ffi_const]` functions cannot be `#[ffi_pure]` self.dcx().emit_err(errors::BothFfiConstAndPure { attr_span }); - false - } else { - true } } - fn check_ffi_const(&self, attr_span: Span, target: Target) -> bool { - if target == Target::ForeignFn { - true - } else { + fn check_ffi_const(&self, attr_span: Span, target: Target) { + if target != Target::ForeignFn { self.dcx().emit_err(errors::FfiConstInvalidTarget { attr_span }); - false } } /// Warns against some misuses of `#[must_use]` - fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) -> bool { + fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) { if !matches!( target, Target::Fn @@ -1397,23 +1273,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::MustUseNoEffect { article, target }, ); } - - // For now, its always valid - true } - /// Checks if `#[must_not_suspend]` is applied to a function. Returns `true` if valid. - fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) -> bool { + /// Checks if `#[must_not_suspend]` is applied to a function. + fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Struct | Target::Enum | Target::Union | Target::Trait => true, + Target::Struct | Target::Enum | Target::Union | Target::Trait => {} _ => { self.dcx().emit_err(errors::MustNotSuspend { attr_span: attr.span, span }); - false } } } - /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid. + /// Checks if `#[cold]` is applied to a non-function. fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {} @@ -1489,21 +1361,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid. - fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { + /// Checks if `#[no_link]` is applied to an `extern crate`. + fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { - Target::ExternCrate => true, + Target::ExternCrate => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[no_link]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link"); - true } _ => { self.dcx().emit_err(errors::NoLink { attr_span: attr.span, span }); - false } } } @@ -1512,57 +1382,42 @@ impl<'tcx> CheckAttrVisitor<'tcx> { matches!(self.tcx.hir_node(hir_id), hir::Node::ImplItem(..)) } - /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid. - fn check_export_name( - &self, - hir_id: HirId, - attr: &Attribute, - span: Span, - target: Target, - ) -> bool { + /// Checks if `#[export_name]` is applied to a function or static. + fn check_export_name(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { - Target::Static | Target::Fn => true, - Target::Method(..) if self.is_impl_item(hir_id) => true, + Target::Static | Target::Fn => {} + Target::Method(..) if self.is_impl_item(hir_id) => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[export_name]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name"); - true } _ => { self.dcx().emit_err(errors::ExportName { attr_span: attr.span, span }); - false } } } - fn check_rustc_layout_scalar_valid_range( - &self, - attr: &Attribute, - span: Span, - target: Target, - ) -> bool { + fn check_rustc_layout_scalar_valid_range(&self, attr: &Attribute, span: Span, target: Target) { if target != Target::Struct { self.dcx().emit_err(errors::RustcLayoutScalarValidRangeNotStruct { attr_span: attr.span, span, }); - return false; + return; } let Some(list) = attr.meta_item_list() else { - return false; + return; }; - if matches!(&list[..], &[NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Int(..), .. })]) { - true - } else { + if !matches!(&list[..], &[NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Int(..), .. })]) + { self.tcx .dcx() .emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span }); - false } } @@ -1574,7 +1429,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, item: Option>, - ) -> bool { + ) { let is_function = matches!(target, Target::Fn); if !is_function { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { @@ -1582,12 +1437,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); - return false; + return; } let Some(list) = attr.meta_item_list() else { // The attribute form is validated on AST. - return false; + return; }; let Some(ItemLike::Item(Item { @@ -1605,7 +1460,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr_span: attr.span, param_span: param.span, }); - return false; + return; } } } @@ -1615,7 +1470,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr_span: attr.span, generics_span: generics.span, }); - return false; + return; } let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128; @@ -1628,7 +1483,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span, arg_count: arg_count as usize, }); - return false; + return; } } else { invalid_args.push(meta.span()); @@ -1637,9 +1492,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if !invalid_args.is_empty() { self.dcx().emit_err(errors::RustcLegacyConstGenericsIndexNegative { invalid_args }); - false - } else { - true } } @@ -1651,7 +1503,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, span: Span, target: Target, - ) -> bool { + ) { let is_function = matches!(target, Target::Fn | Target::Method(..)); if !is_function { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { @@ -1659,9 +1511,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); - false - } else { - true } } @@ -1673,7 +1522,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, span: Span, target: Target, - ) -> bool { + ) { self.check_applied_to_fn_or_method(hir_id, attr, span, target) } @@ -1685,60 +1534,49 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, span: Span, target: Target, - ) -> bool { + ) { self.check_applied_to_fn_or_method(hir_id, attr, span, target) } /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct. - fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool { + fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Struct => true, + Target::Struct => {} _ => { self.dcx().emit_err(errors::RustcLintOptTy { attr_span: attr.span, span }); - false } } } /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field. - fn check_rustc_lint_opt_deny_field_access( - &self, - attr: &Attribute, - span: Span, - target: Target, - ) -> bool { + fn check_rustc_lint_opt_deny_field_access(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Field => true, + Target::Field => {} _ => { self.tcx .dcx() .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span }); - false } } } /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph /// option is passed to the compiler. - fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool { - if self.tcx.sess.opts.unstable_opts.query_dep_graph { - true - } else { + fn check_rustc_dirty_clean(&self, attr: &Attribute) { + if !self.tcx.sess.opts.unstable_opts.query_dep_graph { self.dcx().emit_err(errors::RustcDirtyClean { span: attr.span }); - false } } /// Checks if the attribute is applied to a trait. - fn check_must_be_applied_to_trait(&self, attr: &Attribute, span: Span, target: Target) -> bool { + fn check_must_be_applied_to_trait(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Trait => true, + Target::Trait => {} _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { attr_span: attr.span, defn_span: span, }); - false } } } @@ -2036,43 +1874,38 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, attrs: &[Attribute], - ) -> bool { + ) { debug!("Checking target: {:?}", target); match target { Target::Fn => { for attr in attrs { if attr.is_proc_macro_attr() { debug!("Is proc macro attr"); - return true; + return; } } debug!("Is not proc macro attr"); - false } - Target::MacroDef => true, + Target::MacroDef => {} // FIXME(#80564): We permit struct fields and match arms to have an // `#[allow_internal_unstable]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. - Target::Field | Target::Arm => { - self.inline_attr_str_error_without_macro_def( - hir_id, - attr, - "allow_internal_unstable", - ); - true - } + Target::Field | Target::Arm => self.inline_attr_str_error_without_macro_def( + hir_id, + attr, + "allow_internal_unstable", + ), _ => { self.tcx .dcx() .emit_err(errors::AllowInternalUnstable { attr_span: attr.span, span }); - false } } } /// Checks if the items on the `#[debugger_visualizer]` attribute are valid. - fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool { + fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) { // Here we only check that the #[debugger_visualizer] attribute is attached // to nothing other than a module. All other checks are done in the // `debugger_visualizer` query where they need to be done for decoding @@ -2081,11 +1914,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::Mod => {} _ => { self.dcx().emit_err(errors::DebugVisualizerPlacement { span: attr.span }); - return false; } } - - true } /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. @@ -2096,26 +1926,21 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, span: Span, target: Target, - ) -> bool { + ) { match target { Target::Fn | Target::Method(_) - if self.tcx.is_const_fn_raw(hir_id.expect_owner().to_def_id()) => - { - true - } + if self.tcx.is_const_fn_raw(hir_id.expect_owner().to_def_id()) => {} // FIXME(#80564): We permit struct fields and match arms to have an // `#[allow_internal_unstable]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable"); - true + self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable") } _ => { self.tcx .dcx() .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span, span }); - false } } } @@ -2126,65 +1951,56 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, span: Span, target: Target, - ) -> bool { + ) { if let Target::ForeignFn = target && let hir::Node::Item(Item { kind: ItemKind::ForeignMod { abi: Abi::RustIntrinsic, .. }, .. }) = self.tcx.parent_hir_node(hir_id) { - return true; + return; } self.dcx().emit_err(errors::RustcSafeIntrinsic { attr_span: attr.span, span }); - false } - fn check_rustc_std_internal_symbol( - &self, - attr: &Attribute, - span: Span, - target: Target, - ) -> bool { + fn check_rustc_std_internal_symbol(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Fn | Target::Static => true, + Target::Fn | Target::Static => {} _ => { self.tcx .dcx() .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span, span }); - false } } } - fn check_stability_promotable(&self, attr: &Attribute, target: Target) -> bool { + fn check_stability_promotable(&self, attr: &Attribute, target: Target) { match target { Target::Expression => { self.dcx().emit_err(errors::StabilityPromotable { attr_span: attr.span }); - false } - _ => true, + _ => {} } } - fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) -> bool { + fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) { match target { - Target::ForeignFn | Target::ForeignStatic => true, + Target::ForeignFn | Target::ForeignStatic => {} _ => { self.dcx().emit_err(errors::LinkOrdinal { attr_span: attr.span }); - false } } } - fn check_confusables(&self, attr: &Attribute, target: Target) -> bool { + fn check_confusables(&self, attr: &Attribute, target: Target) { match target { Target::Method(MethodKind::Inherent) => { let Some(meta) = attr.meta() else { - return false; + return; }; let ast::MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { - return false; + return; }; let mut candidates = Vec::new(); @@ -2198,21 +2014,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> { hi: meta.span().shrink_to_hi(), }, }); - return false; + return; }; candidates.push(meta_lit.symbol); } if candidates.is_empty() { self.dcx().emit_err(errors::EmptyConfusables { span: attr.span }); - return false; } - - true } _ => { self.dcx().emit_err(errors::Confusables { attr_span: attr.span }); - false } } } From efdf2192d0005b6ae6dd56bd0a2b04ebd04244eb Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Jul 2024 12:28:57 -0400 Subject: [PATCH 03/18] Revert "Rollup merge of #128104 - mu001999-contrib:fix/128053, r=petrochenkov" This reverts commit 91b18a058c4661f82e420b633dc0a3e1ccd14b88, reversing changes made to 9aedec9313dc8ecf9bdcb5f09c4eb0ad8b9a4875. --- compiler/rustc_passes/src/dead.rs | 36 +++++++++---------- ...lone-debug-dead-code-in-the-same-struct.rs | 4 +-- ...-debug-dead-code-in-the-same-struct.stderr | 16 ++------- .../dead-code/unconstructible-pub-struct.rs | 35 ------------------ .../unconstructible-pub-struct.stderr | 14 -------- 5 files changed, 22 insertions(+), 83 deletions(-) delete mode 100644 tests/ui/lint/dead-code/unconstructible-pub-struct.rs delete mode 100644 tests/ui/lint/dead-code/unconstructible-pub-struct.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 8a931fc4158e3..3b1a796130c43 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -73,26 +73,24 @@ fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> { } fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool { - let adt_def = tcx.adt_def(id); - - // skip types contain fields of unit and never type, - // it's usually intentional to make the type not constructible - let not_require_constructor = adt_def.all_fields().any(|field| { + // treat PhantomData and positional ZST as public, + // we don't want to lint types which only have them, + // cause it's a common way to use such types to check things like well-formedness + tcx.adt_def(id).all_fields().all(|field| { let field_type = tcx.type_of(field.did).instantiate_identity(); - field_type.is_unit() || field_type.is_never() - }); - - not_require_constructor - || adt_def.all_fields().all(|field| { - let field_type = tcx.type_of(field.did).instantiate_identity(); - // skip fields of PhantomData, - // cause it's a common way to check things like well-formedness - if field_type.is_phantom_data() { - return true; - } - - field.vis.is_public() - }) + if field_type.is_phantom_data() { + return true; + } + let is_positional = field.name.as_str().starts_with(|c: char| c.is_ascii_digit()); + if is_positional + && tcx + .layout_of(tcx.param_env(field.did).and(field_type)) + .map_or(true, |layout| layout.is_zst()) + { + return true; + } + field.vis.is_public() + }) } /// check struct and its fields are public or not, diff --git a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs index 6ab1fb7b039bd..885dacc727af6 100644 --- a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs +++ b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs @@ -1,9 +1,9 @@ #![forbid(dead_code)] #[derive(Debug)] -pub struct Whatever { +pub struct Whatever { //~ ERROR struct `Whatever` is never constructed pub field0: (), - field1: (), //~ ERROR fields `field1`, `field2`, `field3`, and `field4` are never read + field1: (), field2: (), field3: (), field4: (), diff --git a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr index e9b757b6bae72..e10d28ad03a4e 100644 --- a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr +++ b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr @@ -1,19 +1,9 @@ -error: fields `field1`, `field2`, `field3`, and `field4` are never read - --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:6:5 +error: struct `Whatever` is never constructed + --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:4:12 | LL | pub struct Whatever { - | -------- fields in this struct -LL | pub field0: (), -LL | field1: (), - | ^^^^^^ -LL | field2: (), - | ^^^^^^ -LL | field3: (), - | ^^^^^^ -LL | field4: (), - | ^^^^^^ + | ^^^^^^^^ | - = note: `Whatever` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis note: the lint level is defined here --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:1:11 | diff --git a/tests/ui/lint/dead-code/unconstructible-pub-struct.rs b/tests/ui/lint/dead-code/unconstructible-pub-struct.rs deleted file mode 100644 index 2202cbb673098..0000000000000 --- a/tests/ui/lint/dead-code/unconstructible-pub-struct.rs +++ /dev/null @@ -1,35 +0,0 @@ -#![feature(never_type)] -#![deny(dead_code)] - -pub struct T1(!); -pub struct T2(()); -pub struct T3(std::marker::PhantomData); - -pub struct T4 { - _x: !, -} - -pub struct T5 { - _x: !, - _y: X, -} - -pub struct T6 { - _x: (), -} - -pub struct T7 { - _x: (), - _y: X, -} - -pub struct T8 { - _x: std::marker::PhantomData, -} - -pub struct T9 { //~ ERROR struct `T9` is never constructed - _x: std::marker::PhantomData, - _y: i32, -} - -fn main() {} diff --git a/tests/ui/lint/dead-code/unconstructible-pub-struct.stderr b/tests/ui/lint/dead-code/unconstructible-pub-struct.stderr deleted file mode 100644 index a3dde042bbe15..0000000000000 --- a/tests/ui/lint/dead-code/unconstructible-pub-struct.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: struct `T9` is never constructed - --> $DIR/unconstructible-pub-struct.rs:30:12 - | -LL | pub struct T9 { - | ^^ - | -note: the lint level is defined here - --> $DIR/unconstructible-pub-struct.rs:2:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: aborting due to 1 previous error - From 8a0cd61df248e1551220c5cd00647938348c2d94 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Jul 2024 12:28:58 -0400 Subject: [PATCH 04/18] Revert "Rollup merge of #127017 - mu001999-contrib:dead/enhance, r=pnkfelix" This reverts commit a70dc297a899b76793a14c5705f6ec78fd7a57a7, reversing changes made to ceae37188b9f1be527bb16c9d657a161be7dbbe3. --- compiler/rustc_passes/src/dead.rs | 32 +++++-------- .../dead-code/unused-impl-for-non-adts.rs | 45 ------------------- .../dead-code/unused-impl-for-non-adts.stderr | 20 --------- 3 files changed, 10 insertions(+), 87 deletions(-) delete mode 100644 tests/ui/lint/dead-code/unused-impl-for-non-adts.rs delete mode 100644 tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 3b1a796130c43..9c475c9fbf462 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -55,24 +55,7 @@ impl Publicness { } } -fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> { - match ty.kind { - TyKind::Path(hir::QPath::Resolved(_, path)) => { - if let Res::Def(def_kind, def_id) = path.res - && let Some(local_def_id) = def_id.as_local() - { - Some((local_def_id, def_kind)) - } else { - None - } - } - TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_of(ty), - TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_of(ty.ty), - _ => None, - } -} - -fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool { +fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool { // treat PhantomData and positional ZST as public, // we don't want to lint types which only have them, // cause it's a common way to use such types to check things like well-formedness @@ -97,7 +80,10 @@ fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool { /// for enum and union, just check they are public, /// and doesn't solve types like &T for now, just skip them fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness { - if let Some((def_id, def_kind)) = adt_of(ty) { + if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind + && let Res::Def(def_kind, def_id) = path.res + && def_id.is_local() + { return match def_kind { DefKind::Enum | DefKind::Union => { let ty_is_public = tcx.visibility(def_id).is_public(); @@ -580,8 +566,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool { - if let Some((local_def_id, def_kind)) = - adt_of(self.tcx.hir().item(impl_id).expect_impl().self_ty) + if let TyKind::Path(hir::QPath::Resolved(_, path)) = + self.tcx.hir().item(impl_id).expect_impl().self_ty.kind + && let Res::Def(def_kind, def_id) = path.res + && let Some(local_def_id) = def_id.as_local() && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id @@ -928,7 +916,7 @@ fn create_and_seed_worklist( match tcx.def_kind(id) { DefKind::Impl { .. } => false, DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), - DefKind::Struct => struct_all_fields_are_public(tcx, id) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), + DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), _ => true }) .map(|id| (id, ComesFromAllowExpect::No)) diff --git a/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs b/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs deleted file mode 100644 index 46065dcee81e0..0000000000000 --- a/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs +++ /dev/null @@ -1,45 +0,0 @@ -#![deny(dead_code)] - -struct Foo; //~ ERROR struct `Foo` is never constructed - -trait Trait { //~ ERROR trait `Trait` is never used - fn foo(&self) {} -} - -impl Trait for Foo {} - -impl Trait for [Foo] {} -impl Trait for [Foo; N] {} - -impl Trait for *const Foo {} -impl Trait for *mut Foo {} - -impl Trait for &Foo {} -impl Trait for &&Foo {} -impl Trait for &mut Foo {} - -impl Trait for [&Foo] {} -impl Trait for &[Foo] {} -impl Trait for &*const Foo {} - -pub trait Trait2 { - fn foo(&self) {} -} - -impl Trait2 for Foo {} - -impl Trait2 for [Foo] {} -impl Trait2 for [Foo; N] {} - -impl Trait2 for *const Foo {} -impl Trait2 for *mut Foo {} - -impl Trait2 for &Foo {} -impl Trait2 for &&Foo {} -impl Trait2 for &mut Foo {} - -impl Trait2 for [&Foo] {} -impl Trait2 for &[Foo] {} -impl Trait2 for &*const Foo {} - -fn main() {} diff --git a/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr b/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr deleted file mode 100644 index e61fc403e810d..0000000000000 --- a/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: struct `Foo` is never constructed - --> $DIR/unused-impl-for-non-adts.rs:3:8 - | -LL | struct Foo; - | ^^^ - | -note: the lint level is defined here - --> $DIR/unused-impl-for-non-adts.rs:1:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: trait `Trait` is never used - --> $DIR/unused-impl-for-non-adts.rs:5:7 - | -LL | trait Trait { - | ^^^^^ - -error: aborting due to 2 previous errors - From cfe4039098bc760a3074e8150f5afd5b8043e909 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Jul 2024 12:28:59 -0400 Subject: [PATCH 05/18] Revert "Rollup merge of #127107 - mu001999-contrib:dead/enhance-2, r=pnkfelix" This reverts commit 31fe9628cf830a08e7194a446f66c668aaea86e9, reversing changes made to f20307851ead9fbbb9fa88bbffb3258a069230a6. --- compiler/rustc_passes/src/dead.rs | 40 +++++++++++++------ library/core/src/default.rs | 1 + .../salsa-macros/src/database_storage.rs | 8 ++++ tests/ui-fulldeps/deriving-global.rs | 3 -- tests/ui-fulldeps/deriving-hygiene.rs | 1 - .../ui/const-generics/issues/issue-86535-2.rs | 1 - tests/ui/const-generics/issues/issue-86535.rs | 1 - .../impl-trait/extra-impl-in-trait-impl.fixed | 2 - .../ui/impl-trait/extra-impl-in-trait-impl.rs | 2 - .../extra-impl-in-trait-impl.stderr | 8 ++-- tests/ui/lint/dead-code/issue-59003.rs | 2 +- .../lint-unused-adt-appeared-in-pattern.rs | 37 ----------------- ...lint-unused-adt-appeared-in-pattern.stderr | 20 ---------- .../not-lint-used-adt-appeared-in-pattern.rs | 32 --------------- ...sed-adt-impl-pub-trait-with-assoc-const.rs | 36 ++++++----------- ...adt-impl-pub-trait-with-assoc-const.stderr | 24 ++--------- .../dead-code/unused-struct-derive-default.rs | 1 - .../unused-struct-derive-default.stderr | 1 + tests/ui/parser/issues/issue-105366.fixed | 1 - tests/ui/parser/issues/issue-105366.rs | 1 - tests/ui/parser/issues/issue-105366.stderr | 2 +- 21 files changed, 60 insertions(+), 164 deletions(-) delete mode 100644 tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs delete mode 100644 tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr delete mode 100644 tests/ui/lint/dead-code/not-lint-used-adt-appeared-in-pattern.rs diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 9c475c9fbf462..78fbcc1f40623 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -278,10 +278,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { pats: &[hir::PatField<'_>], ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { - ty::Adt(adt, _) => { - self.check_def_id(adt.did()); - adt.variant_of_res(res) - } + ty::Adt(adt, _) => adt.variant_of_res(res), _ => span_bug!(lhs.span, "non-ADT in struct pattern"), }; for pat in pats { @@ -301,10 +298,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { dotdot: hir::DotDotPos, ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { - ty::Adt(adt, _) => { - self.check_def_id(adt.did()); - adt.variant_of_res(res) - } + ty::Adt(adt, _) => adt.variant_of_res(res), _ => { self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern"); return; @@ -409,6 +403,31 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { return false; } + // don't ignore impls for Enums and pub Structs whose methods don't have self receiver, + // cause external crate may call such methods to construct values of these types + if let Some(local_impl_of) = impl_of.as_local() + && let Some(local_def_id) = def_id.as_local() + && let Some(fn_sig) = + self.tcx.hir().fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id)) + && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None) + && let TyKind::Path(hir::QPath::Resolved(_, path)) = + self.tcx.hir().expect_item(local_impl_of).expect_impl().self_ty.kind + && let Res::Def(def_kind, did) = path.res + { + match def_kind { + // for example, #[derive(Default)] pub struct T(i32); + // external crate can call T::default() to construct T, + // so that don't ignore impl Default for pub Enum and Structs + DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => { + return false; + } + // don't ignore impl Default for Enums, + // cause we don't know which variant is constructed + DefKind::Enum => return false, + _ => (), + }; + } + if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) && self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) { @@ -672,9 +691,6 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.handle_field_pattern_match(pat, res, fields); } PatKind::Path(ref qpath) => { - if let ty::Adt(adt, _) = self.typeck_results().node_type(pat.hir_id).kind() { - self.check_def_id(adt.did()); - } let res = self.typeck_results().qpath_res(qpath, pat.hir_id); self.handle_res(res); } @@ -830,7 +846,7 @@ fn check_item<'tcx>( // mark the method live if the self_ty is public, // or the method is public and may construct self if tcx.visibility(local_def_id).is_public() - && (ty_and_all_fields_are_public || (ty_is_public && may_construct_self)) + && (ty_and_all_fields_are_public || may_construct_self) { // if the impl item is public, // and the ty may be constructed or can be constructed in foreign crates, diff --git a/library/core/src/default.rs b/library/core/src/default.rs index 4524b352ec817..5cacedcb241a5 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -103,6 +103,7 @@ use crate::ascii::Char as AsciiChar; /// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "Default")] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(bootstrap), rustc_trivial_field_reads)] pub trait Default: Sized { /// Returns the "default value" for a type. /// diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs index f16d814b9f038..14238e2fed55d 100644 --- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs +++ b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs @@ -241,3 +241,11 @@ impl Parse for QueryGroup { Ok(QueryGroup { group_path }) } } + +struct Nothing; + +impl Parse for Nothing { + fn parse(_input: ParseStream<'_>) -> syn::Result { + Ok(Nothing) + } +} diff --git a/tests/ui-fulldeps/deriving-global.rs b/tests/ui-fulldeps/deriving-global.rs index 0ba149c9ad654..7783010be441d 100644 --- a/tests/ui-fulldeps/deriving-global.rs +++ b/tests/ui-fulldeps/deriving-global.rs @@ -17,21 +17,18 @@ mod submod { // if any of these are implemented without global calls for any // function calls, then being in a submodule will (correctly) // cause errors about unrecognised module `std` (or `extra`) - #[allow(dead_code)] #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone, Debug, Encodable, Decodable)] enum A { A1(usize), A2(isize), } - #[allow(dead_code)] #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone, Debug, Encodable, Decodable)] struct B { x: usize, y: isize, } - #[allow(dead_code)] #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone, Debug, Encodable, Decodable)] struct C(usize, isize); } diff --git a/tests/ui-fulldeps/deriving-hygiene.rs b/tests/ui-fulldeps/deriving-hygiene.rs index f948d6ac544e5..a3a6f9e022ebb 100644 --- a/tests/ui-fulldeps/deriving-hygiene.rs +++ b/tests/ui-fulldeps/deriving-hygiene.rs @@ -20,7 +20,6 @@ pub const s: u8 = 1; pub const state: u8 = 1; pub const cmp: u8 = 1; -#[allow(dead_code)] #[derive(Ord, Eq, PartialOrd, PartialEq, Debug, Decodable, Encodable, Hash)] struct Foo {} diff --git a/tests/ui/const-generics/issues/issue-86535-2.rs b/tests/ui/const-generics/issues/issue-86535-2.rs index ab68c6b78df34..8d064f3eeb1b7 100644 --- a/tests/ui/const-generics/issues/issue-86535-2.rs +++ b/tests/ui/const-generics/issues/issue-86535-2.rs @@ -9,7 +9,6 @@ pub trait Foo { [(); Self::ASSOC_C]:; } -#[allow(dead_code)] struct Bar; impl Foo for Bar { const ASSOC_C: usize = 3; diff --git a/tests/ui/const-generics/issues/issue-86535.rs b/tests/ui/const-generics/issues/issue-86535.rs index 9aaf7ddc9e86e..62454f4a388a0 100644 --- a/tests/ui/const-generics/issues/issue-86535.rs +++ b/tests/ui/const-generics/issues/issue-86535.rs @@ -2,7 +2,6 @@ #![feature(adt_const_params, unsized_const_params, generic_const_exprs)] #![allow(incomplete_features, unused_variables)] -#[allow(dead_code)] struct F; impl X for F<{ S }> { const W: usize = 3; diff --git a/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed b/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed index 3c4499f017337..886fc1d005802 100644 --- a/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed +++ b/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed @@ -1,8 +1,6 @@ //@ run-rustfix -#[allow(dead_code)] struct S(T); -#[allow(dead_code)] struct S2; impl Default for S { diff --git a/tests/ui/impl-trait/extra-impl-in-trait-impl.rs b/tests/ui/impl-trait/extra-impl-in-trait-impl.rs index ac0783295242e..f3271993867cb 100644 --- a/tests/ui/impl-trait/extra-impl-in-trait-impl.rs +++ b/tests/ui/impl-trait/extra-impl-in-trait-impl.rs @@ -1,8 +1,6 @@ //@ run-rustfix -#[allow(dead_code)] struct S(T); -#[allow(dead_code)] struct S2; impl impl Default for S { diff --git a/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr b/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr index 91c7da5a04fb4..5aafc8b64d4ff 100644 --- a/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr +++ b/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr @@ -1,23 +1,23 @@ error: unexpected `impl` keyword - --> $DIR/extra-impl-in-trait-impl.rs:8:18 + --> $DIR/extra-impl-in-trait-impl.rs:6:18 | LL | impl impl Default for S { | ^^^^^ help: remove the extra `impl` | note: this is parsed as an `impl Trait` type, but a trait is expected at this position - --> $DIR/extra-impl-in-trait-impl.rs:8:18 + --> $DIR/extra-impl-in-trait-impl.rs:6:18 | LL | impl impl Default for S { | ^^^^^^^^^^^^ error: unexpected `impl` keyword - --> $DIR/extra-impl-in-trait-impl.rs:14:6 + --> $DIR/extra-impl-in-trait-impl.rs:12:6 | LL | impl impl Default for S2 { | ^^^^^ help: remove the extra `impl` | note: this is parsed as an `impl Trait` type, but a trait is expected at this position - --> $DIR/extra-impl-in-trait-impl.rs:14:6 + --> $DIR/extra-impl-in-trait-impl.rs:12:6 | LL | impl impl Default for S2 { | ^^^^^^^^^^^^ diff --git a/tests/ui/lint/dead-code/issue-59003.rs b/tests/ui/lint/dead-code/issue-59003.rs index 319cf2db1495f..e3dcaca577889 100644 --- a/tests/ui/lint/dead-code/issue-59003.rs +++ b/tests/ui/lint/dead-code/issue-59003.rs @@ -4,8 +4,8 @@ #![deny(dead_code)] -#[allow(dead_code)] struct Foo { + #[allow(dead_code)] inner: u32, } diff --git a/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs b/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs deleted file mode 100644 index 25777438456b6..0000000000000 --- a/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![deny(dead_code)] - -struct Foo(u8); //~ ERROR struct `Foo` is never constructed - -enum Bar { //~ ERROR enum `Bar` is never used - Var1(u8), - Var2(u8), -} - -pub trait Tr1 { - fn f1() -> Self; -} - -impl Tr1 for Foo { - fn f1() -> Foo { - let f = Foo(0); - let Foo(tag) = f; - Foo(tag) - } -} - -impl Tr1 for Bar { - fn f1() -> Bar { - let b = Bar::Var1(0); - let b = if let Bar::Var1(_) = b { - Bar::Var1(0) - } else { - Bar::Var2(0) - }; - match b { - Bar::Var1(_) => Bar::Var2(0), - Bar::Var2(_) => Bar::Var1(0), - } - } -} - -fn main() {} diff --git a/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr b/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr deleted file mode 100644 index 7c1a4b4597755..0000000000000 --- a/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: struct `Foo` is never constructed - --> $DIR/lint-unused-adt-appeared-in-pattern.rs:3:8 - | -LL | struct Foo(u8); - | ^^^ - | -note: the lint level is defined here - --> $DIR/lint-unused-adt-appeared-in-pattern.rs:1:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: enum `Bar` is never used - --> $DIR/lint-unused-adt-appeared-in-pattern.rs:5:6 - | -LL | enum Bar { - | ^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/lint/dead-code/not-lint-used-adt-appeared-in-pattern.rs b/tests/ui/lint/dead-code/not-lint-used-adt-appeared-in-pattern.rs deleted file mode 100644 index 43a2e43190433..0000000000000 --- a/tests/ui/lint/dead-code/not-lint-used-adt-appeared-in-pattern.rs +++ /dev/null @@ -1,32 +0,0 @@ -//@ check-pass - -#![deny(dead_code)] - -#[repr(u8)] -#[derive(Copy, Clone, Debug)] -pub enum RecordField { - Target = 1, - Level, - Module, - File, - Line, - NumArgs, -} - -unsafe trait Pod {} - -#[repr(transparent)] -struct RecordFieldWrapper(RecordField); - -unsafe impl Pod for RecordFieldWrapper {} - -fn try_read(buf: &[u8]) -> T { - unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const T) } -} - -pub fn foo(buf: &[u8]) -> RecordField { - let RecordFieldWrapper(tag) = try_read(buf); - tag -} - -fn main() {} diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs index 658cc3d6c613a..bf2fc243e8194 100644 --- a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs +++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs @@ -1,9 +1,8 @@ #![deny(dead_code)] struct T1; //~ ERROR struct `T1` is never constructed -struct T2; //~ ERROR struct `T2` is never constructed -pub struct T3(i32); //~ ERROR struct `T3` is never constructed -pub struct T4(i32); //~ ERROR field `0` is never read +pub struct T2(i32); //~ ERROR struct `T2` is never constructed +struct T3; trait Trait1 { //~ ERROR trait `Trait1` is never used const UNUSED: i32; @@ -12,13 +11,13 @@ trait Trait1 { //~ ERROR trait `Trait1` is never used } pub trait Trait2 { - const MAY_USED: i32; - fn may_used(&self) {} + const USED: i32; + fn used(&self) {} } pub trait Trait3 { - const MAY_USED: i32; - fn may_used() -> Self; + const USED: i32; + fn construct_self() -> Self; } impl Trait1 for T1 { @@ -31,34 +30,23 @@ impl Trait1 for T1 { impl Trait1 for T2 { const UNUSED: i32 = 0; fn construct_self() -> Self { - Self + T2(0) } } impl Trait2 for T1 { - const MAY_USED: i32 = 0; + const USED: i32 = 0; } impl Trait2 for T2 { - const MAY_USED: i32 = 0; -} - -impl Trait2 for T3 { - const MAY_USED: i32 = 0; + const USED: i32 = 0; } -impl Trait3 for T2 { - const MAY_USED: i32 = 0; - fn may_used() -> Self { +impl Trait3 for T3 { + const USED: i32 = 0; + fn construct_self() -> Self { Self } } -impl Trait3 for T4 { - const MAY_USED: i32 = 0; - fn may_used() -> Self { - T4(0) - } -} - fn main() {} diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr index 08c7a5cb4b062..174096d939883 100644 --- a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr +++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr @@ -11,32 +11,16 @@ LL | #![deny(dead_code)] | ^^^^^^^^^ error: struct `T2` is never constructed - --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:4:8 + --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:4:12 | -LL | struct T2; - | ^^ - -error: struct `T3` is never constructed - --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:5:12 - | -LL | pub struct T3(i32); +LL | pub struct T2(i32); | ^^ -error: field `0` is never read - --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:6:15 - | -LL | pub struct T4(i32); - | -- ^^^ - | | - | field in this struct - | - = help: consider removing this field - error: trait `Trait1` is never used - --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:8:7 + --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:7:7 | LL | trait Trait1 { | ^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/lint/dead-code/unused-struct-derive-default.rs b/tests/ui/lint/dead-code/unused-struct-derive-default.rs index f20b7cb66ee51..330ad32dd5709 100644 --- a/tests/ui/lint/dead-code/unused-struct-derive-default.rs +++ b/tests/ui/lint/dead-code/unused-struct-derive-default.rs @@ -22,5 +22,4 @@ pub struct T2 { fn main() { let _x: Used = Default::default(); - let _e: E = Default::default(); } diff --git a/tests/ui/lint/dead-code/unused-struct-derive-default.stderr b/tests/ui/lint/dead-code/unused-struct-derive-default.stderr index 7422f9a39f312..bbb0bd7be7064 100644 --- a/tests/ui/lint/dead-code/unused-struct-derive-default.stderr +++ b/tests/ui/lint/dead-code/unused-struct-derive-default.stderr @@ -4,6 +4,7 @@ error: struct `T` is never constructed LL | struct T; | ^ | + = note: `T` has a derived impl for the trait `Default`, but this is intentionally ignored during dead code analysis note: the lint level is defined here --> $DIR/unused-struct-derive-default.rs:1:9 | diff --git a/tests/ui/parser/issues/issue-105366.fixed b/tests/ui/parser/issues/issue-105366.fixed index 95419dc07f2cc..7157b647524dd 100644 --- a/tests/ui/parser/issues/issue-105366.fixed +++ b/tests/ui/parser/issues/issue-105366.fixed @@ -1,6 +1,5 @@ //@ run-rustfix -#[allow(dead_code)] struct Foo; impl From for Foo { diff --git a/tests/ui/parser/issues/issue-105366.rs b/tests/ui/parser/issues/issue-105366.rs index 3278b73799125..dc3cb8b343d32 100644 --- a/tests/ui/parser/issues/issue-105366.rs +++ b/tests/ui/parser/issues/issue-105366.rs @@ -1,6 +1,5 @@ //@ run-rustfix -#[allow(dead_code)] struct Foo; fn From for Foo { diff --git a/tests/ui/parser/issues/issue-105366.stderr b/tests/ui/parser/issues/issue-105366.stderr index 195305a2ec889..18c04dfaf2088 100644 --- a/tests/ui/parser/issues/issue-105366.stderr +++ b/tests/ui/parser/issues/issue-105366.stderr @@ -1,5 +1,5 @@ error: you might have meant to write `impl` instead of `fn` - --> $DIR/issue-105366.rs:6:1 + --> $DIR/issue-105366.rs:5:1 | LL | fn From for Foo { | ^^ From 16d989b077fc855e3d32743fc6d50fa6abb667b3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Jul 2024 12:29:01 -0400 Subject: [PATCH 06/18] Revert "Rollup merge of #126618 - mu001999-contrib:dead/enhance, r=pnkfelix" This reverts commit 2724aeaaeb127a8073e39461caacbe21a128ce7b, reversing changes made to d929a42a664c026167800801b26d734db925314f. --- compiler/rustc_passes/src/dead.rs | 41 ++++++++----------- .../ui/const-generics/cross_crate_complex.rs | 1 - .../missing-bounds.fixed | 4 -- .../missing-bounds.rs | 4 -- .../missing-bounds.stderr | 18 ++++---- .../dead-code/unused-trait-with-assoc-ty.rs | 11 ----- .../unused-trait-with-assoc-ty.stderr | 20 --------- tests/ui/pattern/issue-22546.rs | 2 +- tests/ui/pattern/issue-22546.stderr | 10 +++++ 9 files changed, 36 insertions(+), 75 deletions(-) delete mode 100644 tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs delete mode 100644 tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr create mode 100644 tests/ui/pattern/issue-22546.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 78fbcc1f40623..d4ce9b02473b0 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -156,10 +156,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn handle_res(&mut self, res: Res) { match res { - Res::Def( - DefKind::Const | DefKind::AssocConst | DefKind::AssocTy | DefKind::TyAlias, - def_id, - ) => { + Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, def_id) => { self.check_def_id(def_id); } _ if self.in_pat => {} @@ -470,7 +467,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_item(self, item) } hir::ItemKind::ForeignMod { .. } => {} - hir::ItemKind::Trait(_, _, _, _, trait_item_refs) => { + hir::ItemKind::Trait(..) => { for impl_def_id in self.tcx.all_impls(item.owner_id.to_def_id()) { if let Some(local_def_id) = impl_def_id.as_local() && let ItemKind::Impl(impl_ref) = @@ -483,12 +480,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_path(self, impl_ref.of_trait.unwrap().path); } } - // mark assoc ty live if the trait is live - for trait_item in trait_item_refs { - if let hir::AssocItemKind::Type = trait_item.kind { - self.check_def_id(trait_item.id.owner_id.to_def_id()); - } - } + intravisit::walk_item(self, item) } _ => intravisit::walk_item(self, item), @@ -505,8 +497,9 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && let ItemKind::Impl(impl_ref) = self.tcx.hir().expect_item(local_impl_id).kind { - if !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) - .ty_and_all_fields_are_public + if !matches!(trait_item.kind, hir::TraitItemKind::Type(..)) + && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) + .ty_and_all_fields_are_public { // skip impl-items of non pure pub ty, // cause we don't know the ty is constructed or not, @@ -845,8 +838,9 @@ fn check_item<'tcx>( // for trait impl blocks, // mark the method live if the self_ty is public, // or the method is public and may construct self - if tcx.visibility(local_def_id).is_public() - && (ty_and_all_fields_are_public || may_construct_self) + if of_trait && matches!(tcx.def_kind(local_def_id), DefKind::AssocTy) + || tcx.visibility(local_def_id).is_public() + && (ty_and_all_fields_are_public || may_construct_self) { // if the impl item is public, // and the ty may be constructed or can be constructed in foreign crates, @@ -883,13 +877,10 @@ fn check_trait_item( worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>, id: hir::TraitItemId, ) { - use hir::TraitItemKind::{Const, Fn, Type}; - if matches!( - tcx.def_kind(id.owner_id), - DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn - ) { + use hir::TraitItemKind::{Const, Fn}; + if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) { let trait_item = tcx.hir().trait_item(id); - if matches!(trait_item.kind, Const(_, Some(_)) | Type(_, Some(_)) | Fn(..)) + if matches!(trait_item.kind, Const(_, Some(_)) | Fn(..)) && let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id) { @@ -931,7 +922,7 @@ fn create_and_seed_worklist( // checks impls, impl-items and pub structs with all public fields later match tcx.def_kind(id) { DefKind::Impl { .. } => false, - DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), + DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), _ => true }) @@ -1218,7 +1209,6 @@ impl<'tcx> DeadVisitor<'tcx> { } match self.tcx.def_kind(def_id) { DefKind::AssocConst - | DefKind::AssocTy | DefKind::AssocFn | DefKind::Fn | DefKind::Static { .. } @@ -1260,14 +1250,15 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id)) { for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) { - // We have diagnosed unused assocs in traits + // We have diagnosed unused assoc consts and fns in traits if matches!(def_kind, DefKind::Impl { of_trait: true }) - && matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn) + && matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocFn) // skip unused public inherent methods, // cause we have diagnosed unconstructed struct || matches!(def_kind, DefKind::Impl { of_trait: false }) && tcx.visibility(def_id).is_public() && ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public + || def_kind == DefKind::Trait && tcx.def_kind(def_id) == DefKind::AssocTy { continue; } diff --git a/tests/ui/const-generics/cross_crate_complex.rs b/tests/ui/const-generics/cross_crate_complex.rs index b44d889f5e99e..d13b69aa0cfb4 100644 --- a/tests/ui/const-generics/cross_crate_complex.rs +++ b/tests/ui/const-generics/cross_crate_complex.rs @@ -11,7 +11,6 @@ async fn foo() { async_in_foo(async_out_foo::<4>().await).await; } -#[allow(dead_code)] struct Faz; impl Foo for Faz {} diff --git a/tests/ui/generic-associated-types/missing-bounds.fixed b/tests/ui/generic-associated-types/missing-bounds.fixed index ff69016d8626d..703d3c1e0fb17 100644 --- a/tests/ui/generic-associated-types/missing-bounds.fixed +++ b/tests/ui/generic-associated-types/missing-bounds.fixed @@ -2,7 +2,6 @@ use std::ops::Add; -#[allow(dead_code)] struct A(B); impl Add for A where B: Add { @@ -13,7 +12,6 @@ impl Add for A where B: Add { } } -#[allow(dead_code)] struct C(B); impl> Add for C { @@ -24,7 +22,6 @@ impl> Add for C { } } -#[allow(dead_code)] struct D(B); impl> Add for D { @@ -35,7 +32,6 @@ impl> Add for D { } } -#[allow(dead_code)] struct E(B); impl> Add for E where B: Add { diff --git a/tests/ui/generic-associated-types/missing-bounds.rs b/tests/ui/generic-associated-types/missing-bounds.rs index 1f83356c2fa6b..f40b422887311 100644 --- a/tests/ui/generic-associated-types/missing-bounds.rs +++ b/tests/ui/generic-associated-types/missing-bounds.rs @@ -2,7 +2,6 @@ use std::ops::Add; -#[allow(dead_code)] struct A(B); impl Add for A where B: Add { @@ -13,7 +12,6 @@ impl Add for A where B: Add { } } -#[allow(dead_code)] struct C(B); impl Add for C { @@ -24,7 +22,6 @@ impl Add for C { } } -#[allow(dead_code)] struct D(B); impl Add for D { @@ -35,7 +32,6 @@ impl Add for D { } } -#[allow(dead_code)] struct E(B); impl Add for E where ::Output = B { diff --git a/tests/ui/generic-associated-types/missing-bounds.stderr b/tests/ui/generic-associated-types/missing-bounds.stderr index 0f0dc24c06c0f..1d7d80d1b0768 100644 --- a/tests/ui/generic-associated-types/missing-bounds.stderr +++ b/tests/ui/generic-associated-types/missing-bounds.stderr @@ -1,5 +1,5 @@ error: equality constraints are not yet supported in `where` clauses - --> $DIR/missing-bounds.rs:41:33 + --> $DIR/missing-bounds.rs:37:33 | LL | impl Add for E where ::Output = B { | ^^^^^^^^^^^^^^^^^^^^^^ not supported @@ -11,7 +11,7 @@ LL | impl Add for E where B: Add { | ~~~~~~~~~~~~~~~~~~ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:12:11 + --> $DIR/missing-bounds.rs:11:11 | LL | impl Add for A where B: Add { | - expected this type parameter @@ -24,14 +24,14 @@ LL | A(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` help: the type constructed contains `::Output` due to the type of the argument passed - --> $DIR/missing-bounds.rs:12:9 + --> $DIR/missing-bounds.rs:11:9 | LL | A(self.0 + rhs.0) | ^^--------------^ | | | this argument influences the type of `A` note: tuple struct defined here - --> $DIR/missing-bounds.rs:6:8 + --> $DIR/missing-bounds.rs:5:8 | LL | struct A(B); | ^ @@ -41,7 +41,7 @@ LL | impl Add for A where B: Add { | ++++++++++++ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:23:14 + --> $DIR/missing-bounds.rs:21:14 | LL | impl Add for C { | - expected this type parameter @@ -54,7 +54,7 @@ LL | Self(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` note: tuple struct defined here - --> $DIR/missing-bounds.rs:17:8 + --> $DIR/missing-bounds.rs:15:8 | LL | struct C(B); | ^ @@ -64,7 +64,7 @@ LL | impl> Add for C { | ++++++++++++ error[E0369]: cannot add `B` to `B` - --> $DIR/missing-bounds.rs:34:21 + --> $DIR/missing-bounds.rs:31:21 | LL | Self(self.0 + rhs.0) | ------ ^ ----- B @@ -77,7 +77,7 @@ LL | impl> Add for D { | +++++++++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:46:14 + --> $DIR/missing-bounds.rs:42:14 | LL | impl Add for E where ::Output = B { | - expected this type parameter @@ -90,7 +90,7 @@ LL | Self(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` note: tuple struct defined here - --> $DIR/missing-bounds.rs:39:8 + --> $DIR/missing-bounds.rs:35:8 | LL | struct E(B); | ^ diff --git a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs deleted file mode 100644 index e8116d83ebf1c..0000000000000 --- a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![deny(dead_code)] - -struct T1; //~ ERROR struct `T1` is never constructed - -trait Foo { type Unused; } //~ ERROR trait `Foo` is never used -impl Foo for T1 { type Unused = Self; } - -pub trait Bar { type Used; } -impl Bar for T1 { type Used = Self; } - -fn main() {} diff --git a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr deleted file mode 100644 index ab73c64063431..0000000000000 --- a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: struct `T1` is never constructed - --> $DIR/unused-trait-with-assoc-ty.rs:3:8 - | -LL | struct T1; - | ^^ - | -note: the lint level is defined here - --> $DIR/unused-trait-with-assoc-ty.rs:1:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: trait `Foo` is never used - --> $DIR/unused-trait-with-assoc-ty.rs:5:7 - | -LL | trait Foo { type Unused; } - | ^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/pattern/issue-22546.rs b/tests/ui/pattern/issue-22546.rs index d5c5b68be78d7..fd1d5fb6c4775 100644 --- a/tests/ui/pattern/issue-22546.rs +++ b/tests/ui/pattern/issue-22546.rs @@ -15,7 +15,7 @@ impl Foo { } } -trait Tr { +trait Tr { //~ WARN trait `Tr` is never used type U; } diff --git a/tests/ui/pattern/issue-22546.stderr b/tests/ui/pattern/issue-22546.stderr new file mode 100644 index 0000000000000..e067a95e4226c --- /dev/null +++ b/tests/ui/pattern/issue-22546.stderr @@ -0,0 +1,10 @@ +warning: trait `Tr` is never used + --> $DIR/issue-22546.rs:18:7 + | +LL | trait Tr { + | ^^ + | + = note: `#[warn(dead_code)]` on by default + +warning: 1 warning emitted + From 3037f888eb360d860b5c6be74e41195c7d9b1c7d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Jul 2024 12:29:02 -0400 Subject: [PATCH 07/18] Revert "Rollup merge of #126315 - mu001999-contrib:fix/126289, r=petrochenkov" This reverts commit 977c5fd419ade52467f7de79d5bfc25c0c893275, reversing changes made to 24c94f0e4f5aa333c665fbbba423172c30176624. --- compiler/rustc_passes/src/dead.rs | 2 +- .../allow-unconstructed-pub-struct.rs | 33 ------------------- 2 files changed, 1 insertion(+), 34 deletions(-) delete mode 100644 tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index d4ce9b02473b0..90f436a6fc1ae 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -923,7 +923,7 @@ fn create_and_seed_worklist( match tcx.def_kind(id) { DefKind::Impl { .. } => false, DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), - DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), + DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()), _ => true }) .map(|id| (id, ComesFromAllowExpect::No)) diff --git a/tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs b/tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs deleted file mode 100644 index 8cd1524045b15..0000000000000 --- a/tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs +++ /dev/null @@ -1,33 +0,0 @@ -//@ check-pass - -mod ffi { - use super::*; - - extern "C" { - pub fn DomPromise_AddRef(promise: *const Promise); - pub fn DomPromise_Release(promise: *const Promise); - } -} - -#[repr(C)] -#[allow(unused)] -pub struct Promise { - private: [u8; 0], - __nosync: ::std::marker::PhantomData<::std::rc::Rc>, -} - -pub unsafe trait RefCounted { - unsafe fn addref(&self); - unsafe fn release(&self); -} - -unsafe impl RefCounted for Promise { - unsafe fn addref(&self) { - ffi::DomPromise_AddRef(self) - } - unsafe fn release(&self) { - ffi::DomPromise_Release(self) - } -} - -fn main() {} From ada5b54e5eb998eaf9b48183e15d03e596fa194e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Jul 2024 12:29:20 -0400 Subject: [PATCH 08/18] Revert "Rollup merge of #125572 - mu001999-contrib:dead/enhance, r=pnkfelix" This reverts commit 13314df21b0bb0cdd02c6760581d1b9f1052fa7e, reversing changes made to 6e534c73c35f569492ed5fb5f349075d58ed8b7e. --- compiler/rustc_passes/src/dead.rs | 121 ++++-------------- .../item-collection/generic-impl.rs | 10 +- .../item-collection/overloaded-operators.rs | 24 ++-- tests/ui/coherence/re-rebalance-coherence.rs | 1 - .../defaults/repr-c-issue-82792.rs | 1 - .../generic_const_exprs/associated-consts.rs | 3 +- .../transparent-maybeunit-array-wrapper.rs | 1 - ...lone-debug-dead-code-in-the-same-struct.rs | 4 +- ...-debug-dead-code-in-the-same-struct.stderr | 16 ++- tests/ui/issues/issue-5708.rs | 1 - tests/ui/lint/dead-code/lint-dead-code-1.rs | 5 +- .../ui/lint/dead-code/lint-dead-code-1.stderr | 26 +--- tests/ui/lint/dead-code/unused-assoc-const.rs | 20 --- .../lint/dead-code/unused-assoc-const.stderr | 16 --- tests/ui/lint/dead-code/unused-pub-struct.rs | 48 ------- .../lint/dead-code/unused-pub-struct.stderr | 14 -- ...re-clause-before-tuple-struct-body-0.fixed | 3 +- ...where-clause-before-tuple-struct-body-0.rs | 3 +- ...e-clause-before-tuple-struct-body-0.stderr | 10 +- tests/ui/pub/pub-ident-struct-4.fixed | 3 +- tests/ui/pub/pub-ident-struct-4.rs | 3 +- tests/ui/pub/pub-ident-struct-4.stderr | 8 +- tests/ui/regions/regions-issue-21422.rs | 1 - .../structs-enums/newtype-struct-with-dtor.rs | 2 - .../ui/structs-enums/uninstantiable-struct.rs | 3 +- .../ui/suggestions/derive-clone-for-eq.fixed | 1 - tests/ui/suggestions/derive-clone-for-eq.rs | 1 - .../ui/suggestions/derive-clone-for-eq.stderr | 4 +- .../ui/suggestions/option-content-move.fixed | 2 - tests/ui/suggestions/option-content-move.rs | 2 - .../ui/suggestions/option-content-move.stderr | 4 +- tests/ui/traits/object/generics.rs | 1 - 32 files changed, 89 insertions(+), 273 deletions(-) delete mode 100644 tests/ui/lint/dead-code/unused-assoc-const.rs delete mode 100644 tests/ui/lint/dead-code/unused-assoc-const.stderr delete mode 100644 tests/ui/lint/dead-code/unused-pub-struct.rs delete mode 100644 tests/ui/lint/dead-code/unused-pub-struct.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 90f436a6fc1ae..7ae5c9040042c 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -17,7 +17,7 @@ use rustc_hir::{Node, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, AssocItemContainer, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_session::lint::builtin::DEAD_CODE; @@ -44,63 +44,16 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { ) } -struct Publicness { - ty_is_public: bool, - ty_and_all_fields_are_public: bool, -} - -impl Publicness { - fn new(ty_is_public: bool, ty_and_all_fields_are_public: bool) -> Self { - Self { ty_is_public, ty_and_all_fields_are_public } - } -} - -fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool { - // treat PhantomData and positional ZST as public, - // we don't want to lint types which only have them, - // cause it's a common way to use such types to check things like well-formedness - tcx.adt_def(id).all_fields().all(|field| { - let field_type = tcx.type_of(field.did).instantiate_identity(); - if field_type.is_phantom_data() { - return true; - } - let is_positional = field.name.as_str().starts_with(|c: char| c.is_ascii_digit()); - if is_positional - && tcx - .layout_of(tcx.param_env(field.did).and(field_type)) - .map_or(true, |layout| layout.is_zst()) - { - return true; - } - field.vis.is_public() - }) -} - -/// check struct and its fields are public or not, -/// for enum and union, just check they are public, -/// and doesn't solve types like &T for now, just skip them -fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness { +fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind && let Res::Def(def_kind, def_id) = path.res && def_id.is_local() + && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { - return match def_kind { - DefKind::Enum | DefKind::Union => { - let ty_is_public = tcx.visibility(def_id).is_public(); - Publicness::new(ty_is_public, ty_is_public) - } - DefKind::Struct => { - let ty_is_public = tcx.visibility(def_id).is_public(); - Publicness::new( - ty_is_public, - ty_is_public && struct_all_fields_are_public(tcx, def_id), - ) - } - _ => Publicness::new(true, true), - }; + tcx.visibility(def_id).is_public() + } else { + true } - - Publicness::new(true, true) } /// Determine if a work from the worklist is coming from a `#[allow]` @@ -499,11 +452,9 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { { if !matches!(trait_item.kind, hir::TraitItemKind::Type(..)) && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) - .ty_and_all_fields_are_public { - // skip impl-items of non pure pub ty, - // cause we don't know the ty is constructed or not, - // check these later in `solve_rest_impl_items` + // skip methods of private ty, + // they would be solved in `solve_rest_impl_items` continue; } @@ -584,21 +535,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && let Some(local_def_id) = def_id.as_local() && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { + if self.tcx.visibility(impl_item_id).is_public() { + // for the public method, we don't know the trait item is used or not, + // so we mark the method live if the self is used + return self.live_symbols.contains(&local_def_id); + } + if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id && let Some(local_id) = trait_item_id.as_local() { - // for the local impl item, we can know the trait item is used or not, + // for the private method, we can know the trait item is used or not, // so we mark the method live if the self is used and the trait item is used - self.live_symbols.contains(&local_id) && self.live_symbols.contains(&local_def_id) - } else { - // for the foreign method and inherent pub method, - // we don't know the trait item or the method is used or not, - // so we mark the method live if the self is used - self.live_symbols.contains(&local_def_id) + return self.live_symbols.contains(&local_id) + && self.live_symbols.contains(&local_def_id); } - } else { - false } + false } } @@ -820,9 +772,7 @@ fn check_item<'tcx>( .iter() .filter_map(|def_id| def_id.as_local()); - let self_ty = tcx.hir().item(id).expect_impl().self_ty; - let Publicness { ty_is_public, ty_and_all_fields_are_public } = - ty_ref_to_pub_struct(tcx, self_ty); + let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty); // And we access the Map here to get HirId from LocalDefId for local_def_id in local_def_ids { @@ -838,20 +788,18 @@ fn check_item<'tcx>( // for trait impl blocks, // mark the method live if the self_ty is public, // or the method is public and may construct self - if of_trait && matches!(tcx.def_kind(local_def_id), DefKind::AssocTy) - || tcx.visibility(local_def_id).is_public() - && (ty_and_all_fields_are_public || may_construct_self) + if of_trait + && (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) + || tcx.visibility(local_def_id).is_public() + && (ty_is_pub || may_construct_self)) { - // if the impl item is public, - // and the ty may be constructed or can be constructed in foreign crates, - // mark the impl item live worklist.push((local_def_id, ComesFromAllowExpect::No)); } else if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, local_def_id) { worklist.push((local_def_id, comes_from_allow)); - } else if of_trait || tcx.visibility(local_def_id).is_public() && ty_is_public { - // private impl items of traits || public impl items not constructs self + } else if of_trait { + // private method || public method not constructs self unsolved_impl_items.push((id, local_def_id)); } } @@ -918,14 +866,6 @@ fn create_and_seed_worklist( effective_vis .is_public_at_level(Level::Reachable) .then_some(id) - .filter(|&id| - // checks impls, impl-items and pub structs with all public fields later - match tcx.def_kind(id) { - DefKind::Impl { .. } => false, - DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), - DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()), - _ => true - }) .map(|id| (id, ComesFromAllowExpect::No)) }) // Seed entry point @@ -1250,15 +1190,10 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id)) { for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) { - // We have diagnosed unused assoc consts and fns in traits + // We have diagnosed unused methods in traits if matches!(def_kind, DefKind::Impl { of_trait: true }) - && matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocFn) - // skip unused public inherent methods, - // cause we have diagnosed unconstructed struct - || matches!(def_kind, DefKind::Impl { of_trait: false }) - && tcx.visibility(def_id).is_public() - && ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public - || def_kind == DefKind::Trait && tcx.def_kind(def_id) == DefKind::AssocTy + && tcx.def_kind(def_id) == DefKind::AssocFn + || def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn { continue; } diff --git a/tests/codegen-units/item-collection/generic-impl.rs b/tests/codegen-units/item-collection/generic-impl.rs index b4cd99272b152..23d09e0d8af3e 100644 --- a/tests/codegen-units/item-collection/generic-impl.rs +++ b/tests/codegen-units/item-collection/generic-impl.rs @@ -22,16 +22,16 @@ impl Struct { } } -pub struct _LifeTimeOnly<'a> { +pub struct LifeTimeOnly<'a> { _a: &'a u32, } -impl<'a> _LifeTimeOnly<'a> { - //~ MONO_ITEM fn _LifeTimeOnly::<'_>::foo +impl<'a> LifeTimeOnly<'a> { + //~ MONO_ITEM fn LifeTimeOnly::<'_>::foo pub fn foo(&self) {} - //~ MONO_ITEM fn _LifeTimeOnly::<'_>::bar + //~ MONO_ITEM fn LifeTimeOnly::<'_>::bar pub fn bar(&'a self) {} - //~ MONO_ITEM fn _LifeTimeOnly::<'_>::baz + //~ MONO_ITEM fn LifeTimeOnly::<'_>::baz pub fn baz<'b>(&'b self) {} pub fn non_instantiated(&self) {} diff --git a/tests/codegen-units/item-collection/overloaded-operators.rs b/tests/codegen-units/item-collection/overloaded-operators.rs index e00e22dbab996..69b55695d3d30 100644 --- a/tests/codegen-units/item-collection/overloaded-operators.rs +++ b/tests/codegen-units/item-collection/overloaded-operators.rs @@ -5,44 +5,44 @@ use std::ops::{Add, Deref, Index, IndexMut}; -pub struct _Indexable { +pub struct Indexable { data: [u8; 3], } -impl Index for _Indexable { +impl Index for Indexable { type Output = u8; - //~ MONO_ITEM fn <_Indexable as std::ops::Index>::index + //~ MONO_ITEM fn >::index fn index(&self, index: usize) -> &Self::Output { if index >= 3 { &self.data[0] } else { &self.data[index] } } } -impl IndexMut for _Indexable { - //~ MONO_ITEM fn <_Indexable as std::ops::IndexMut>::index_mut +impl IndexMut for Indexable { + //~ MONO_ITEM fn >::index_mut fn index_mut(&mut self, index: usize) -> &mut Self::Output { if index >= 3 { &mut self.data[0] } else { &mut self.data[index] } } } -//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::eq -//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::ne +//~ MONO_ITEM fn ::eq +//~ MONO_ITEM fn ::ne #[derive(PartialEq)] -pub struct _Equatable(u32); +pub struct Equatable(u32); -impl Add for _Equatable { +impl Add for Equatable { type Output = u32; - //~ MONO_ITEM fn <_Equatable as std::ops::Add>::add + //~ MONO_ITEM fn >::add fn add(self, rhs: u32) -> u32 { self.0 + rhs } } -impl Deref for _Equatable { +impl Deref for Equatable { type Target = u32; - //~ MONO_ITEM fn <_Equatable as std::ops::Deref>::deref + //~ MONO_ITEM fn ::deref fn deref(&self) -> &Self::Target { &self.0 } diff --git a/tests/ui/coherence/re-rebalance-coherence.rs b/tests/ui/coherence/re-rebalance-coherence.rs index 5383a634617f4..9c176d5b1b12b 100644 --- a/tests/ui/coherence/re-rebalance-coherence.rs +++ b/tests/ui/coherence/re-rebalance-coherence.rs @@ -4,7 +4,6 @@ extern crate re_rebalance_coherence_lib as lib; use lib::*; -#[allow(dead_code)] struct Oracle; impl Backend for Oracle {} impl<'a, T:'a, Tab> QueryFragment for BatchInsert<'a, T, Tab> {} diff --git a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs index 4bf2fa761eae8..c23187598bceb 100644 --- a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs +++ b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs @@ -2,7 +2,6 @@ //@ run-pass -#[allow(dead_code)] #[repr(C)] pub struct Loaf { head: [T; N], diff --git a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs index 50a6102c605a9..5d2198f50ad92 100644 --- a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs +++ b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs @@ -16,8 +16,7 @@ impl BlockCipher for BarCipher { const BLOCK_SIZE: usize = 32; } -#[allow(dead_code)] -pub struct Block(C); +pub struct Block(#[allow(dead_code)] C); pub fn test() where diff --git a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs index 35c41ae461520..419d605d0c875 100644 --- a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs +++ b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs @@ -6,7 +6,6 @@ use std::mem::MaybeUninit; -#[allow(dead_code)] #[repr(transparent)] pub struct MaybeUninitWrapper(MaybeUninit<[u64; N]>); diff --git a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs index 885dacc727af6..6ab1fb7b039bd 100644 --- a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs +++ b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs @@ -1,9 +1,9 @@ #![forbid(dead_code)] #[derive(Debug)] -pub struct Whatever { //~ ERROR struct `Whatever` is never constructed +pub struct Whatever { pub field0: (), - field1: (), + field1: (), //~ ERROR fields `field1`, `field2`, `field3`, and `field4` are never read field2: (), field3: (), field4: (), diff --git a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr index e10d28ad03a4e..e9b757b6bae72 100644 --- a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr +++ b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr @@ -1,9 +1,19 @@ -error: struct `Whatever` is never constructed - --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:4:12 +error: fields `field1`, `field2`, `field3`, and `field4` are never read + --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:6:5 | LL | pub struct Whatever { - | ^^^^^^^^ + | -------- fields in this struct +LL | pub field0: (), +LL | field1: (), + | ^^^^^^ +LL | field2: (), + | ^^^^^^ +LL | field3: (), + | ^^^^^^ +LL | field4: (), + | ^^^^^^ | + = note: `Whatever` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis note: the lint level is defined here --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:1:11 | diff --git a/tests/ui/issues/issue-5708.rs b/tests/ui/issues/issue-5708.rs index 89ea9fbdcd8fe..ce9ef78ffcd9c 100644 --- a/tests/ui/issues/issue-5708.rs +++ b/tests/ui/issues/issue-5708.rs @@ -44,7 +44,6 @@ pub trait MyTrait { fn dummy(&self, t: T) -> T { panic!() } } -#[allow(dead_code)] pub struct MyContainer<'a, T:'a> { foos: Vec<&'a (dyn MyTrait+'a)> , } diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.rs b/tests/ui/lint/dead-code/lint-dead-code-1.rs index 3386dfa47470f..ddcafedf7bc5f 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.rs +++ b/tests/ui/lint/dead-code/lint-dead-code-1.rs @@ -46,10 +46,11 @@ struct SemiUsedStruct; impl SemiUsedStruct { fn la_la_la() {} } -struct StructUsedAsField; //~ ERROR struct `StructUsedAsField` is never constructed +struct StructUsedAsField; pub struct StructUsedInEnum; struct StructUsedInGeneric; -pub struct PubStruct2 { //~ ERROR struct `PubStruct2` is never constructed +pub struct PubStruct2 { + #[allow(dead_code)] struct_used_as_field: *const StructUsedAsField } diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.stderr b/tests/ui/lint/dead-code/lint-dead-code-1.stderr index b0163df8855cd..eb728b5b93055 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.stderr +++ b/tests/ui/lint/dead-code/lint-dead-code-1.stderr @@ -22,26 +22,14 @@ error: struct `PrivStruct` is never constructed LL | struct PrivStruct; | ^^^^^^^^^^ -error: struct `StructUsedAsField` is never constructed - --> $DIR/lint-dead-code-1.rs:49:8 - | -LL | struct StructUsedAsField; - | ^^^^^^^^^^^^^^^^^ - -error: struct `PubStruct2` is never constructed - --> $DIR/lint-dead-code-1.rs:52:12 - | -LL | pub struct PubStruct2 { - | ^^^^^^^^^^ - error: enum `priv_enum` is never used - --> $DIR/lint-dead-code-1.rs:63:6 + --> $DIR/lint-dead-code-1.rs:64:6 | LL | enum priv_enum { foo2, bar2 } | ^^^^^^^^^ error: variant `bar3` is never constructed - --> $DIR/lint-dead-code-1.rs:66:5 + --> $DIR/lint-dead-code-1.rs:67:5 | LL | enum used_enum { | --------- variant in this enum @@ -50,25 +38,25 @@ LL | bar3 | ^^^^ error: function `priv_fn` is never used - --> $DIR/lint-dead-code-1.rs:87:4 + --> $DIR/lint-dead-code-1.rs:88:4 | LL | fn priv_fn() { | ^^^^^^^ error: function `foo` is never used - --> $DIR/lint-dead-code-1.rs:92:4 + --> $DIR/lint-dead-code-1.rs:93:4 | LL | fn foo() { | ^^^ error: function `bar` is never used - --> $DIR/lint-dead-code-1.rs:97:4 + --> $DIR/lint-dead-code-1.rs:98:4 | LL | fn bar() { | ^^^ error: function `baz` is never used - --> $DIR/lint-dead-code-1.rs:101:4 + --> $DIR/lint-dead-code-1.rs:102:4 | LL | fn baz() -> impl Copy { | ^^^ @@ -79,5 +67,5 @@ error: struct `Bar` is never constructed LL | pub struct Bar; | ^^^ -error: aborting due to 12 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/lint/dead-code/unused-assoc-const.rs b/tests/ui/lint/dead-code/unused-assoc-const.rs deleted file mode 100644 index 36e8315ad3605..0000000000000 --- a/tests/ui/lint/dead-code/unused-assoc-const.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![deny(dead_code)] - -trait Trait { - const UNUSED_CONST: i32; //~ ERROR associated constant `UNUSED_CONST` is never used - const USED_CONST: i32; - - fn foo(&self) {} -} - -pub struct T(()); - -impl Trait for T { - const UNUSED_CONST: i32 = 0; - const USED_CONST: i32 = 1; -} - -fn main() { - T(()).foo(); - T::USED_CONST; -} diff --git a/tests/ui/lint/dead-code/unused-assoc-const.stderr b/tests/ui/lint/dead-code/unused-assoc-const.stderr deleted file mode 100644 index 78296d706638b..0000000000000 --- a/tests/ui/lint/dead-code/unused-assoc-const.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: associated constant `UNUSED_CONST` is never used - --> $DIR/unused-assoc-const.rs:4:11 - | -LL | trait Trait { - | ----- associated constant in this trait -LL | const UNUSED_CONST: i32; - | ^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/unused-assoc-const.rs:1:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/lint/dead-code/unused-pub-struct.rs b/tests/ui/lint/dead-code/unused-pub-struct.rs deleted file mode 100644 index aaf4dd612de4d..0000000000000 --- a/tests/ui/lint/dead-code/unused-pub-struct.rs +++ /dev/null @@ -1,48 +0,0 @@ -#![deny(dead_code)] - -pub struct NotLint1(()); -pub struct NotLint2(std::marker::PhantomData); - -pub struct NeverConstructed(i32); //~ ERROR struct `NeverConstructed` is never constructed - -impl NeverConstructed { - pub fn not_construct_self(&self) {} -} - -impl Clone for NeverConstructed { - fn clone(&self) -> NeverConstructed { - NeverConstructed(0) - } -} - -pub trait Trait { - fn not_construct_self(&self); -} - -impl Trait for NeverConstructed { - fn not_construct_self(&self) { - self.0; - } -} - -pub struct Constructed(i32); - -impl Constructed { - pub fn construct_self() -> Self { - Constructed(0) - } -} - -impl Clone for Constructed { - fn clone(&self) -> Constructed { - Constructed(0) - } -} - -impl Trait for Constructed { - fn not_construct_self(&self) { - self.0; - } -} - -fn main() {} diff --git a/tests/ui/lint/dead-code/unused-pub-struct.stderr b/tests/ui/lint/dead-code/unused-pub-struct.stderr deleted file mode 100644 index 3667ddb97bd3e..0000000000000 --- a/tests/ui/lint/dead-code/unused-pub-struct.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: struct `NeverConstructed` is never constructed - --> $DIR/unused-pub-struct.rs:6:12 - | -LL | pub struct NeverConstructed(i32); - | ^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/unused-pub-struct.rs:1:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed index e9c89807fa566..a851300a9828e 100644 --- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed @@ -1,8 +1,7 @@ // Regression test for issues #100790 and #106439. //@ run-rustfix -#[allow(dead_code)] -pub struct Example(usize) +pub struct Example(#[allow(dead_code)] usize) where (): Sized; //~^^^ ERROR where clauses are not allowed before tuple struct bodies diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs index 3bd0f51ec2cbe..10f435859f15a 100644 --- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs @@ -1,11 +1,10 @@ // Regression test for issues #100790 and #106439. //@ run-rustfix -#[allow(dead_code)] pub struct Example where (): Sized, -(usize); +(#[allow(dead_code)] usize); //~^^^ ERROR where clauses are not allowed before tuple struct bodies struct _Demo diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr index 77eafa6bea336..ddbf237e8662b 100644 --- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr @@ -1,23 +1,23 @@ error: where clauses are not allowed before tuple struct bodies - --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:6:1 + --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:5:1 | LL | pub struct Example | ------- while parsing this tuple struct LL | / where LL | | (): Sized, | |______________^ unexpected where clause -LL | (usize); - | ------- the struct body +LL | (#[allow(dead_code)] usize); + | --------------------------- the struct body | help: move the body before the where clause | -LL ~ pub struct Example(usize) +LL ~ pub struct Example(#[allow(dead_code)] usize) LL | where LL ~ (): Sized; | error: where clauses are not allowed before tuple struct bodies - --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:12:1 + --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:11:1 | LL | struct _Demo | ----- while parsing this tuple struct diff --git a/tests/ui/pub/pub-ident-struct-4.fixed b/tests/ui/pub/pub-ident-struct-4.fixed index a62ece43eced3..5fedbb7243749 100644 --- a/tests/ui/pub/pub-ident-struct-4.fixed +++ b/tests/ui/pub/pub-ident-struct-4.fixed @@ -1,7 +1,6 @@ //@ run-rustfix -#[allow(dead_code)] -pub struct T(String); +pub struct T(#[allow(dead_code)] String); //~^ ERROR missing `struct` for struct definition fn main() {} diff --git a/tests/ui/pub/pub-ident-struct-4.rs b/tests/ui/pub/pub-ident-struct-4.rs index 0d56a31beaf1c..5c721c25a7815 100644 --- a/tests/ui/pub/pub-ident-struct-4.rs +++ b/tests/ui/pub/pub-ident-struct-4.rs @@ -1,7 +1,6 @@ //@ run-rustfix -#[allow(dead_code)] -pub T(String); +pub T(#[allow(dead_code)] String); //~^ ERROR missing `struct` for struct definition fn main() {} diff --git a/tests/ui/pub/pub-ident-struct-4.stderr b/tests/ui/pub/pub-ident-struct-4.stderr index d3072464e05cd..e04fff1ebf4a9 100644 --- a/tests/ui/pub/pub-ident-struct-4.stderr +++ b/tests/ui/pub/pub-ident-struct-4.stderr @@ -1,12 +1,12 @@ error: missing `struct` for struct definition - --> $DIR/pub-ident-struct-4.rs:4:1 + --> $DIR/pub-ident-struct-4.rs:3:4 | -LL | pub T(String); - | ^^^^^ +LL | pub T(#[allow(dead_code)] String); + | ^ | help: add `struct` here to parse `T` as a struct | -LL | pub struct T(String); +LL | pub struct T(#[allow(dead_code)] String); | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/regions/regions-issue-21422.rs b/tests/ui/regions/regions-issue-21422.rs index 67852a6f5de0d..54beed9b3ac2b 100644 --- a/tests/ui/regions/regions-issue-21422.rs +++ b/tests/ui/regions/regions-issue-21422.rs @@ -5,7 +5,6 @@ //@ pretty-expanded FIXME #23616 -#[allow(dead_code)] pub struct P<'a> { _ptr: *const &'a u8, } diff --git a/tests/ui/structs-enums/newtype-struct-with-dtor.rs b/tests/ui/structs-enums/newtype-struct-with-dtor.rs index 16439a7fedd3e..19672e41c9a30 100644 --- a/tests/ui/structs-enums/newtype-struct-with-dtor.rs +++ b/tests/ui/structs-enums/newtype-struct-with-dtor.rs @@ -3,10 +3,8 @@ #![allow(unused_variables)] //@ pretty-expanded FIXME #23616 -#[allow(dead_code)] pub struct Fd(u32); -#[allow(dead_code)] fn foo(a: u32) {} impl Drop for Fd { diff --git a/tests/ui/structs-enums/uninstantiable-struct.rs b/tests/ui/structs-enums/uninstantiable-struct.rs index 1074dbcd6e6b5..97bc7d8414e7f 100644 --- a/tests/ui/structs-enums/uninstantiable-struct.rs +++ b/tests/ui/structs-enums/uninstantiable-struct.rs @@ -1,5 +1,4 @@ //@ run-pass -#[allow(dead_code)] -pub struct Z(&'static Z); +pub struct Z(#[allow(dead_code)] &'static Z); pub fn main() {} diff --git a/tests/ui/suggestions/derive-clone-for-eq.fixed b/tests/ui/suggestions/derive-clone-for-eq.fixed index cf800c6e47d87..4dc362f947875 100644 --- a/tests/ui/suggestions/derive-clone-for-eq.fixed +++ b/tests/ui/suggestions/derive-clone-for-eq.fixed @@ -1,7 +1,6 @@ //@ run-rustfix // https://github.com/rust-lang/rust/issues/79076 -#[allow(dead_code)] #[derive(Clone, Eq)] //~ ERROR [E0277] pub struct Struct(T); diff --git a/tests/ui/suggestions/derive-clone-for-eq.rs b/tests/ui/suggestions/derive-clone-for-eq.rs index 84736426bac0d..b3635000f1658 100644 --- a/tests/ui/suggestions/derive-clone-for-eq.rs +++ b/tests/ui/suggestions/derive-clone-for-eq.rs @@ -1,7 +1,6 @@ //@ run-rustfix // https://github.com/rust-lang/rust/issues/79076 -#[allow(dead_code)] #[derive(Clone, Eq)] //~ ERROR [E0277] pub struct Struct(T); diff --git a/tests/ui/suggestions/derive-clone-for-eq.stderr b/tests/ui/suggestions/derive-clone-for-eq.stderr index 54670fbffcfb9..6fae6e1316df0 100644 --- a/tests/ui/suggestions/derive-clone-for-eq.stderr +++ b/tests/ui/suggestions/derive-clone-for-eq.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `T: Clone` is not satisfied - --> $DIR/derive-clone-for-eq.rs:5:17 + --> $DIR/derive-clone-for-eq.rs:4:17 | LL | #[derive(Clone, Eq)] | ^^ the trait `Clone` is not implemented for `T`, which is required by `Struct: PartialEq` | note: required for `Struct` to implement `PartialEq` - --> $DIR/derive-clone-for-eq.rs:8:19 + --> $DIR/derive-clone-for-eq.rs:7:19 | LL | impl PartialEq for Struct | ----- ^^^^^^^^^^^^ ^^^^^^^^^ diff --git a/tests/ui/suggestions/option-content-move.fixed b/tests/ui/suggestions/option-content-move.fixed index ef07d55871e7e..4a5a9483c20c8 100644 --- a/tests/ui/suggestions/option-content-move.fixed +++ b/tests/ui/suggestions/option-content-move.fixed @@ -1,5 +1,4 @@ //@ run-rustfix -#[allow(dead_code)] pub struct LipogramCorpora { selections: Vec<(char, Option)>, } @@ -18,7 +17,6 @@ impl LipogramCorpora { } } -#[allow(dead_code)] pub struct LipogramCorpora2 { selections: Vec<(char, Result)>, } diff --git a/tests/ui/suggestions/option-content-move.rs b/tests/ui/suggestions/option-content-move.rs index 5be6358fd6a57..90d05c7439970 100644 --- a/tests/ui/suggestions/option-content-move.rs +++ b/tests/ui/suggestions/option-content-move.rs @@ -1,5 +1,4 @@ //@ run-rustfix -#[allow(dead_code)] pub struct LipogramCorpora { selections: Vec<(char, Option)>, } @@ -18,7 +17,6 @@ impl LipogramCorpora { } } -#[allow(dead_code)] pub struct LipogramCorpora2 { selections: Vec<(char, Result)>, } diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr index b4ec5b180d210..a382a04344aeb 100644 --- a/tests/ui/suggestions/option-content-move.stderr +++ b/tests/ui/suggestions/option-content-move.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of `selection.1` which is behind a shared reference - --> $DIR/option-content-move.rs:11:20 + --> $DIR/option-content-move.rs:10:20 | LL | if selection.1.unwrap().contains(selection.0) { | ^^^^^^^^^^^ -------- `selection.1` moved due to this method call @@ -19,7 +19,7 @@ LL | if selection.1.clone().unwrap().contains(selection.0) { | ++++++++ error[E0507]: cannot move out of `selection.1` which is behind a shared reference - --> $DIR/option-content-move.rs:30:20 + --> $DIR/option-content-move.rs:28:20 | LL | if selection.1.unwrap().contains(selection.0) { | ^^^^^^^^^^^ -------- `selection.1` moved due to this method call diff --git a/tests/ui/traits/object/generics.rs b/tests/ui/traits/object/generics.rs index 0ae562c0d30a0..462b0bc5bb77f 100644 --- a/tests/ui/traits/object/generics.rs +++ b/tests/ui/traits/object/generics.rs @@ -7,7 +7,6 @@ pub trait Trait2 { fn doit(&self) -> A; } -#[allow(dead_code)] pub struct Impl { m1: marker::PhantomData<(A1,A2,A3)>, /* From 4e84989020b465f073b3a6f1a05cde42aa956ff1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Jul 2024 12:16:33 -0400 Subject: [PATCH 09/18] Suppress new false-negatives that were masked by dead code analysis changes --- library/std/src/sys/pal/unix/pipe.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/std/src/sys/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs index 8762af614f17e..f0ebc767badad 100644 --- a/library/std/src/sys/pal/unix/pipe.rs +++ b/library/std/src/sys/pal/unix/pipe.rs @@ -47,6 +47,8 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { } impl AnonPipe { + #[allow(dead_code)] + // FIXME: This function seems legitimately unused. pub fn try_clone(&self) -> io::Result { self.0.duplicate().map(Self) } @@ -85,6 +87,8 @@ impl AnonPipe { self.0.is_write_vectored() } + #[allow(dead_code)] + // FIXME: This function seems legitimately unused. pub fn as_file_desc(&self) -> &FileDesc { &self.0 } From 72844ee4e1fdd22f90e57a046db709415fc8dc93 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Jul 2024 12:23:41 -0400 Subject: [PATCH 10/18] Bless test fallout --- .../unused-adt-impl-pub-trait-with-assoc-const.rs | 2 +- .../unused-adt-impl-pub-trait-with-assoc-const.stderr | 10 +++++++--- tests/ui/pub/pub-ident-struct-4.stderr | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs index bf2fc243e8194..5b755d62a0598 100644 --- a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs +++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs @@ -1,7 +1,7 @@ #![deny(dead_code)] struct T1; //~ ERROR struct `T1` is never constructed -pub struct T2(i32); //~ ERROR struct `T2` is never constructed +pub struct T2(i32); //~ ERROR field `0` is never read struct T3; trait Trait1 { //~ ERROR trait `Trait1` is never used diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr index 174096d939883..2441a3f868dc2 100644 --- a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr +++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr @@ -10,11 +10,15 @@ note: the lint level is defined here LL | #![deny(dead_code)] | ^^^^^^^^^ -error: struct `T2` is never constructed - --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:4:12 +error: field `0` is never read + --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:4:15 | LL | pub struct T2(i32); - | ^^ + | -- ^^^ + | | + | field in this struct + | + = help: consider removing this field error: trait `Trait1` is never used --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:7:7 diff --git a/tests/ui/pub/pub-ident-struct-4.stderr b/tests/ui/pub/pub-ident-struct-4.stderr index e04fff1ebf4a9..04965a1a73720 100644 --- a/tests/ui/pub/pub-ident-struct-4.stderr +++ b/tests/ui/pub/pub-ident-struct-4.stderr @@ -1,8 +1,8 @@ error: missing `struct` for struct definition - --> $DIR/pub-ident-struct-4.rs:3:4 + --> $DIR/pub-ident-struct-4.rs:3:1 | LL | pub T(#[allow(dead_code)] String); - | ^ + | ^^^^^ | help: add `struct` here to parse `T` as a struct | From a75d2f9d384135c5f0f6bd6d26ec2ba9aa76745a Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Wed, 24 Jul 2024 22:02:48 +0000 Subject: [PATCH 11/18] Cleanup sys module to match house style --- .../std/src/{sys/anonymous_pipe => pipe}/tests.rs | 1 + library/std/src/sys/anonymous_pipe/mod.rs | 14 +++++--------- library/std/src/sys/anonymous_pipe/unix.rs | 8 ++++---- library/std/src/sys/anonymous_pipe/unsupported.rs | 2 +- library/std/src/sys/anonymous_pipe/windows.rs | 8 ++++---- library/std/src/sys/mod.rs | 1 - 6 files changed, 15 insertions(+), 19 deletions(-) rename library/std/src/{sys/anonymous_pipe => pipe}/tests.rs (90%) diff --git a/library/std/src/sys/anonymous_pipe/tests.rs b/library/std/src/pipe/tests.rs similarity index 90% rename from library/std/src/sys/anonymous_pipe/tests.rs rename to library/std/src/pipe/tests.rs index 865d24ec85596..9c38e10678752 100644 --- a/library/std/src/sys/anonymous_pipe/tests.rs +++ b/library/std/src/pipe/tests.rs @@ -2,6 +2,7 @@ use crate::io::{Read, Write}; use crate::pipe::pipe; #[test] +#[cfg(all(windows, unix, not(miri)))] fn pipe_creation_clone_and_rw() { let (rx, tx) = pipe().unwrap(); diff --git a/library/std/src/sys/anonymous_pipe/mod.rs b/library/std/src/sys/anonymous_pipe/mod.rs index 74875677cf3e7..aa14c8b650d34 100644 --- a/library/std/src/sys/anonymous_pipe/mod.rs +++ b/library/std/src/sys/anonymous_pipe/mod.rs @@ -1,18 +1,14 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + cfg_if::cfg_if! { if #[cfg(unix)] { mod unix; - pub(crate) use unix::{AnonPipe, pipe}; - - #[cfg(all(test, not(miri)))] - mod tests; + pub use unix::{AnonPipe, pipe}; } else if #[cfg(windows)] { mod windows; - pub(crate) use windows::{AnonPipe, pipe}; - - #[cfg(all(test, not(miri)))] - mod tests; + pub use windows::{AnonPipe, pipe}; } else { mod unsupported; - pub(crate) use unsupported::{AnonPipe, pipe}; + pub use unsupported::{AnonPipe, pipe}; } } diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs index 7c0e75208c43c..9168024730e67 100644 --- a/library/std/src/sys/anonymous_pipe/unix.rs +++ b/library/std/src/sys/anonymous_pipe/unix.rs @@ -6,10 +6,10 @@ use crate::sys::fd::FileDesc; use crate::sys::pipe::anon_pipe; use crate::sys_common::{FromInner, IntoInner}; -pub(crate) type AnonPipe = FileDesc; +pub type AnonPipe = FileDesc; #[inline] -pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { +pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) } @@ -34,7 +34,7 @@ impl From for OwnedFd { #[unstable(feature = "anonymous_pipe", issue = "127154")] impl FromRawFd for PipeReader { unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FileDesc::from_raw_fd(raw_fd)) + unsafe { Self(FileDesc::from_raw_fd(raw_fd)) } } } #[unstable(feature = "anonymous_pipe", issue = "127154")] @@ -71,7 +71,7 @@ impl From for OwnedFd { #[unstable(feature = "anonymous_pipe", issue = "127154")] impl FromRawFd for PipeWriter { unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FileDesc::from_raw_fd(raw_fd)) + unsafe { Self(FileDesc::from_raw_fd(raw_fd)) } } } #[unstable(feature = "anonymous_pipe", issue = "127154")] diff --git a/library/std/src/sys/anonymous_pipe/unsupported.rs b/library/std/src/sys/anonymous_pipe/unsupported.rs index f3c826d2f8dfb..da04e8e6b25af 100644 --- a/library/std/src/sys/anonymous_pipe/unsupported.rs +++ b/library/std/src/sys/anonymous_pipe/unsupported.rs @@ -4,7 +4,7 @@ use crate::process::Stdio; pub(crate) use crate::sys::pipe::AnonPipe; #[inline] -pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { +pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { Err(io::Error::UNSUPPORTED_PLATFORM) } diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/anonymous_pipe/windows.rs index 0f746b1082baf..1b63e9a9b920b 100644 --- a/library/std/src/sys/anonymous_pipe/windows.rs +++ b/library/std/src/sys/anonymous_pipe/windows.rs @@ -8,10 +8,10 @@ use crate::sys::handle::Handle; use crate::sys::pipe::unnamed_anon_pipe; use crate::sys_common::{FromInner, IntoInner}; -pub(crate) type AnonPipe = Handle; +pub type AnonPipe = Handle; #[inline] -pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { +pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { unnamed_anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) } @@ -31,7 +31,7 @@ impl AsRawHandle for PipeReader { #[unstable(feature = "anonymous_pipe", issue = "127154")] impl FromRawHandle for PipeReader { unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { - Self(Handle::from_raw_handle(raw_handle)) + unsafe { Self(Handle::from_raw_handle(raw_handle)) } } } #[unstable(feature = "anonymous_pipe", issue = "127154")] @@ -70,7 +70,7 @@ impl AsRawHandle for PipeWriter { #[unstable(feature = "anonymous_pipe", issue = "127154")] impl FromRawHandle for PipeWriter { unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { - Self(Handle::from_raw_handle(raw_handle)) + unsafe { Self(Handle::from_raw_handle(raw_handle)) } } } #[unstable(feature = "anonymous_pipe", issue = "127154")] diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 202997b749513..a86b3628f249a 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -7,7 +7,6 @@ mod pal; mod personality; -#[unstable(feature = "anonymous_pipe", issue = "127154")] pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; From 9169622027f1dcb702f789076da1b8b98c01a215 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Wed, 24 Jul 2024 22:22:09 +0000 Subject: [PATCH 12/18] Move Windows implementation of anon pipe --- library/std/src/sys/anonymous_pipe/windows.rs | 16 ++++++++++++---- library/std/src/sys/pal/windows/pipe.rs | 17 ----------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/anonymous_pipe/windows.rs index 1b63e9a9b920b..a48198f8a812b 100644 --- a/library/std/src/sys/anonymous_pipe/windows.rs +++ b/library/std/src/sys/anonymous_pipe/windows.rs @@ -1,18 +1,26 @@ -use crate::io; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, }; use crate::pipe::{PipeReader, PipeWriter}; use crate::process::Stdio; +use crate::sys::c; use crate::sys::handle::Handle; -use crate::sys::pipe::unnamed_anon_pipe; use crate::sys_common::{FromInner, IntoInner}; +use crate::{io, ptr}; pub type AnonPipe = Handle; -#[inline] pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { - unnamed_anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) + let mut read_pipe = c::INVALID_HANDLE_VALUE; + let mut write_pipe = c::INVALID_HANDLE_VALUE; + + let ret = unsafe { c::CreatePipe(&mut read_pipe, &mut write_pipe, ptr::null_mut(), 0) }; + + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + unsafe { Ok((Handle::from_raw_handle(read_pipe), Handle::from_raw_handle(write_pipe))) } + } } #[unstable(feature = "anonymous_pipe", issue = "127154")] diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 9eb19fcf2d7a2..7d1b5aca1d5fe 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -36,23 +36,6 @@ pub struct Pipes { pub theirs: AnonPipe, } -/// Create true unnamed anonymous pipe. -pub fn unnamed_anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - let mut read_pipe = c::INVALID_HANDLE_VALUE; - let mut write_pipe = c::INVALID_HANDLE_VALUE; - - let ret = unsafe { c::CreatePipe(&mut read_pipe, &mut write_pipe, ptr::null_mut(), 0) }; - - if ret == 0 { - Err(io::Error::last_os_error()) - } else { - Ok(( - AnonPipe::from_inner(unsafe { Handle::from_raw_handle(read_pipe) }), - AnonPipe::from_inner(unsafe { Handle::from_raw_handle(write_pipe) }), - )) - } -} - /// Although this looks similar to `anon_pipe` in the Unix module it's actually /// subtly different. Here we'll return two pipes in the `Pipes` return value, /// but one is intended for "us" where as the other is intended for "someone From c586eb6906f26f458a0081850b58728072e3b51a Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 12 Jul 2024 03:54:54 -0400 Subject: [PATCH 13/18] Specify the integer type of the `powi` LLVM intrinsic Since LLVM (4c7f820b2b20, "Update @llvm.powi to handle different int sizes for the exponent"), the size of the integer can be specified for the `powi` intrinsic. Make use of this so it is more obvious that integer size is consistent across all float types. This feature is available since LLVM 13 (October 2021). Based on bootstrap we currently support >= 17.0, so there should be no support problems. --- compiler/rustc_codegen_llvm/src/context.rs | 8 ++++---- compiler/rustc_codegen_llvm/src/intrinsic.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index ea930421b5869..14540d41e7c62 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -775,10 +775,10 @@ impl<'ll> CodegenCx<'ll, '_> { ifn!("llvm.debugtrap", fn() -> void); ifn!("llvm.frameaddress", fn(t_i32) -> ptr); - ifn!("llvm.powi.f16", fn(t_f16, t_i32) -> t_f16); - ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); - ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); - ifn!("llvm.powi.f128", fn(t_f128, t_i32) -> t_f128); + ifn!("llvm.powi.f16.i32", fn(t_f16, t_i32) -> t_f16); + ifn!("llvm.powi.f32.i32", fn(t_f32, t_i32) -> t_f32); + ifn!("llvm.powi.f64.i32", fn(t_f64, t_i32) -> t_f64); + ifn!("llvm.powi.f128.i32", fn(t_f128, t_i32) -> t_f128); ifn!("llvm.pow.f16", fn(t_f16, t_f16) -> t_f16); ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 040de1c7dd715..57d5f6fdf503f 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -35,10 +35,10 @@ fn get_simple_intrinsic<'ll>( sym::sqrtf64 => "llvm.sqrt.f64", sym::sqrtf128 => "llvm.sqrt.f128", - sym::powif16 => "llvm.powi.f16", - sym::powif32 => "llvm.powi.f32", - sym::powif64 => "llvm.powi.f64", - sym::powif128 => "llvm.powi.f128", + sym::powif16 => "llvm.powi.f16.i32", + sym::powif32 => "llvm.powi.f32.i32", + sym::powif64 => "llvm.powi.f64.i32", + sym::powif128 => "llvm.powi.f128.i32", sym::sinf16 => "llvm.sin.f16", sym::sinf32 => "llvm.sin.f32", From b6d05171707c727e1be75ca74d44919695a2641b Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 18 Jun 2024 21:39:00 -0500 Subject: [PATCH 14/18] Add math intrinsics for `f16` and `f128` These already exist in the compiler. Expose them in core so we can add their library functions. --- library/core/src/intrinsics.rs | 289 +++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 13c7f0855d8f0..f230ca612cfe8 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1528,6 +1528,12 @@ extern "rust-intrinsic" { #[rustc_diagnostic_item = "intrinsics_unaligned_volatile_store"] pub fn unaligned_volatile_store(dst: *mut T, val: T); + /// Returns the square root of an `f16` + /// + /// The stabilized version of this intrinsic is + /// [`f16::sqrt`](../../std/primitive.f16.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf16(x: f16) -> f16; /// Returns the square root of an `f32` /// /// The stabilized version of this intrinsic is @@ -1540,6 +1546,12 @@ extern "rust-intrinsic" { /// [`f64::sqrt`](../../std/primitive.f64.html#method.sqrt) #[rustc_nounwind] pub fn sqrtf64(x: f64) -> f64; + /// Returns the square root of an `f128` + /// + /// The stabilized version of this intrinsic is + /// [`f128::sqrt`](../../std/primitive.f128.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf128(x: f128) -> f128; /// Raises an `f16` to an integer power. /// @@ -1566,6 +1578,12 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn powif128(a: f128, x: i32) -> f128; + /// Returns the sine of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::sin`](../../std/primitive.f16.html#method.sin) + #[rustc_nounwind] + pub fn sinf16(x: f16) -> f16; /// Returns the sine of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1578,7 +1596,19 @@ extern "rust-intrinsic" { /// [`f64::sin`](../../std/primitive.f64.html#method.sin) #[rustc_nounwind] pub fn sinf64(x: f64) -> f64; + /// Returns the sine of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::sin`](../../std/primitive.f128.html#method.sin) + #[rustc_nounwind] + pub fn sinf128(x: f128) -> f128; + /// Returns the cosine of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::cos`](../../std/primitive.f16.html#method.cos) + #[rustc_nounwind] + pub fn cosf16(x: f16) -> f16; /// Returns the cosine of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1591,7 +1621,19 @@ extern "rust-intrinsic" { /// [`f64::cos`](../../std/primitive.f64.html#method.cos) #[rustc_nounwind] pub fn cosf64(x: f64) -> f64; + /// Returns the cosine of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::cos`](../../std/primitive.f128.html#method.cos) + #[rustc_nounwind] + pub fn cosf128(x: f128) -> f128; + /// Raises an `f16` to an `f16` power. + /// + /// The stabilized version of this intrinsic is + /// [`f16::powf`](../../std/primitive.f16.html#method.powf) + #[rustc_nounwind] + pub fn powf16(a: f16, x: f16) -> f16; /// Raises an `f32` to an `f32` power. /// /// The stabilized version of this intrinsic is @@ -1604,7 +1646,19 @@ extern "rust-intrinsic" { /// [`f64::powf`](../../std/primitive.f64.html#method.powf) #[rustc_nounwind] pub fn powf64(a: f64, x: f64) -> f64; + /// Raises an `f128` to an `f128` power. + /// + /// The stabilized version of this intrinsic is + /// [`f128::powf`](../../std/primitive.f128.html#method.powf) + #[rustc_nounwind] + pub fn powf128(a: f128, x: f128) -> f128; + /// Returns the exponential of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::exp`](../../std/primitive.f16.html#method.exp) + #[rustc_nounwind] + pub fn expf16(x: f16) -> f16; /// Returns the exponential of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1617,7 +1671,19 @@ extern "rust-intrinsic" { /// [`f64::exp`](../../std/primitive.f64.html#method.exp) #[rustc_nounwind] pub fn expf64(x: f64) -> f64; + /// Returns the exponential of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::exp`](../../std/primitive.f128.html#method.exp) + #[rustc_nounwind] + pub fn expf128(x: f128) -> f128; + /// Returns 2 raised to the power of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f16(x: f16) -> f16; /// Returns 2 raised to the power of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1630,7 +1696,19 @@ extern "rust-intrinsic" { /// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) #[rustc_nounwind] pub fn exp2f64(x: f64) -> f64; + /// Returns 2 raised to the power of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f128(x: f128) -> f128; + /// Returns the natural logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::ln`](../../std/primitive.f16.html#method.ln) + #[rustc_nounwind] + pub fn logf16(x: f16) -> f16; /// Returns the natural logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1643,7 +1721,19 @@ extern "rust-intrinsic" { /// [`f64::ln`](../../std/primitive.f64.html#method.ln) #[rustc_nounwind] pub fn logf64(x: f64) -> f64; + /// Returns the natural logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::ln`](../../std/primitive.f128.html#method.ln) + #[rustc_nounwind] + pub fn logf128(x: f128) -> f128; + /// Returns the base 10 logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::log10`](../../std/primitive.f16.html#method.log10) + #[rustc_nounwind] + pub fn log10f16(x: f16) -> f16; /// Returns the base 10 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1656,7 +1746,19 @@ extern "rust-intrinsic" { /// [`f64::log10`](../../std/primitive.f64.html#method.log10) #[rustc_nounwind] pub fn log10f64(x: f64) -> f64; + /// Returns the base 10 logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::log10`](../../std/primitive.f128.html#method.log10) + #[rustc_nounwind] + pub fn log10f128(x: f128) -> f128; + /// Returns the base 2 logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::log2`](../../std/primitive.f16.html#method.log2) + #[rustc_nounwind] + pub fn log2f16(x: f16) -> f16; /// Returns the base 2 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1669,7 +1771,19 @@ extern "rust-intrinsic" { /// [`f64::log2`](../../std/primitive.f64.html#method.log2) #[rustc_nounwind] pub fn log2f64(x: f64) -> f64; + /// Returns the base 2 logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::log2`](../../std/primitive.f128.html#method.log2) + #[rustc_nounwind] + pub fn log2f128(x: f128) -> f128; + /// Returns `a * b + c` for `f16` values. + /// + /// The stabilized version of this intrinsic is + /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf16(a: f16, b: f16, c: f16) -> f16; /// Returns `a * b + c` for `f32` values. /// /// The stabilized version of this intrinsic is @@ -1682,7 +1796,19 @@ extern "rust-intrinsic" { /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) #[rustc_nounwind] pub fn fmaf64(a: f64, b: f64, c: f64) -> f64; + /// Returns `a * b + c` for `f128` values. + /// + /// The stabilized version of this intrinsic is + /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf128(a: f128, b: f128, c: f128) -> f128; + /// Returns the absolute value of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::abs`](../../std/primitive.f16.html#method.abs) + #[rustc_nounwind] + pub fn fabsf16(x: f16) -> f16; /// Returns the absolute value of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1695,7 +1821,25 @@ extern "rust-intrinsic" { /// [`f64::abs`](../../std/primitive.f64.html#method.abs) #[rustc_nounwind] pub fn fabsf64(x: f64) -> f64; + /// Returns the absolute value of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::abs`](../../std/primitive.f128.html#method.abs) + #[rustc_nounwind] + pub fn fabsf128(x: f128) -> f128; + /// Returns the minimum of two `f16` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f16::min`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn minnumf16(x: f16, y: f16) -> f16; /// Returns the minimum of two `f32` values. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -1720,6 +1864,31 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn minnumf64(x: f64, y: f64) -> f64; + /// Returns the minimum of two `f128` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f128::min`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn minnumf128(x: f128, y: f128) -> f128; + + /// Returns the maximum of two `f16` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f16::max`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn maxnumf16(x: f16, y: f16) -> f16; /// Returns the maximum of two `f32` values. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -1744,7 +1913,25 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn maxnumf64(x: f64, y: f64) -> f64; + /// Returns the maximum of two `f128` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f128::max`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn maxnumf128(x: f128, y: f128) -> f128; + /// Copies the sign from `y` to `x` for `f16` values. + /// + /// The stabilized version of this intrinsic is + /// [`f16::copysign`](../../std/primitive.f16.html#method.copysign) + #[rustc_nounwind] + pub fn copysignf16(x: f16, y: f16) -> f16; /// Copies the sign from `y` to `x` for `f32` values. /// /// The stabilized version of this intrinsic is @@ -1757,7 +1944,19 @@ extern "rust-intrinsic" { /// [`f64::copysign`](../../std/primitive.f64.html#method.copysign) #[rustc_nounwind] pub fn copysignf64(x: f64, y: f64) -> f64; + /// Copies the sign from `y` to `x` for `f128` values. + /// + /// The stabilized version of this intrinsic is + /// [`f128::copysign`](../../std/primitive.f128.html#method.copysign) + #[rustc_nounwind] + pub fn copysignf128(x: f128, y: f128) -> f128; + /// Returns the largest integer less than or equal to an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::floor`](../../std/primitive.f16.html#method.floor) + #[rustc_nounwind] + pub fn floorf16(x: f16) -> f16; /// Returns the largest integer less than or equal to an `f32`. /// /// The stabilized version of this intrinsic is @@ -1770,7 +1969,19 @@ extern "rust-intrinsic" { /// [`f64::floor`](../../std/primitive.f64.html#method.floor) #[rustc_nounwind] pub fn floorf64(x: f64) -> f64; + /// Returns the largest integer less than or equal to an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::floor`](../../std/primitive.f128.html#method.floor) + #[rustc_nounwind] + pub fn floorf128(x: f128) -> f128; + /// Returns the smallest integer greater than or equal to an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::ceil`](../../std/primitive.f16.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf16(x: f16) -> f16; /// Returns the smallest integer greater than or equal to an `f32`. /// /// The stabilized version of this intrinsic is @@ -1783,7 +1994,19 @@ extern "rust-intrinsic" { /// [`f64::ceil`](../../std/primitive.f64.html#method.ceil) #[rustc_nounwind] pub fn ceilf64(x: f64) -> f64; + /// Returns the smallest integer greater than or equal to an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::ceil`](../../std/primitive.f128.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf128(x: f128) -> f128; + /// Returns the integer part of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::trunc`](../../std/primitive.f16.html#method.trunc) + #[rustc_nounwind] + pub fn truncf16(x: f16) -> f16; /// Returns the integer part of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1796,7 +2019,25 @@ extern "rust-intrinsic" { /// [`f64::trunc`](../../std/primitive.f64.html#method.trunc) #[rustc_nounwind] pub fn truncf64(x: f64) -> f64; + /// Returns the integer part of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::trunc`](../../std/primitive.f128.html#method.trunc) + #[rustc_nounwind] + pub fn truncf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf16` and `roundevenf16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::round_ties_even`](../../std/primitive.f16.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, /// so this rounds half-way cases to the number with an even least significant digit. /// @@ -1821,7 +2062,25 @@ extern "rust-intrinsic" { /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) #[rustc_nounwind] pub fn rintf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf128` and `roundevenf128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::round_ties_even`](../../std/primitive.f128.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, /// so this rounds half-way cases to the number with an even least significant digit. /// @@ -1834,7 +2093,19 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] pub fn nearbyintf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f16::round`](../../std/primitive.f16.html#method.round) + #[rustc_nounwind] + pub fn roundf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is @@ -1847,7 +2118,19 @@ extern "rust-intrinsic" { /// [`f64::round`](../../std/primitive.f64.html#method.round) #[rustc_nounwind] pub fn roundf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f128::round`](../../std/primitive.f128.html#method.round) + #[rustc_nounwind] + pub fn roundf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases to the number /// with an even least significant digit. /// @@ -1860,6 +2143,12 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] pub fn roundevenf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf128(x: f128) -> f128; /// Float addition that allows optimizations based on algebraic rules. /// May assume inputs are finite. From 90e63ddebba9b0d5a0b03073508867d7878b90f2 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 27 Jun 2024 04:13:25 -0500 Subject: [PATCH 15/18] Add math functions for `f16` and `f128` This adds missing functions for math operations on the new float types. Platform support is pretty spotty at this point, since even platforms with generally good support can be missing math functions. `std/build.rs` is updated to reflect this. --- library/std/build.rs | 37 + library/std/src/f128.rs | 1300 ++++++++++++++++++++++- library/std/src/f128/tests.rs | 450 +++++++- library/std/src/f16.rs | 1296 +++++++++++++++++++++- library/std/src/f16/tests.rs | 444 +++++++- library/std/src/macros.rs | 2 +- library/std/src/sys/cmath.rs | 15 + library/std/src/sys/pal/mod.rs | 12 + library/std/src/sys/pal/unix/android.rs | 10 + 9 files changed, 3474 insertions(+), 92 deletions(-) diff --git a/library/std/build.rs b/library/std/build.rs index c542ba81eedc1..256face7aeff6 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -85,6 +85,11 @@ fn main() { println!("cargo:rustc-check-cfg=cfg(reliable_f16)"); println!("cargo:rustc-check-cfg=cfg(reliable_f128)"); + // This is a step beyond only having the types and basic functions available. Math functions + // aren't consistently available or correct. + println!("cargo:rustc-check-cfg=cfg(reliable_f16_math)"); + println!("cargo:rustc-check-cfg=cfg(reliable_f128_math)"); + let has_reliable_f16 = match (target_arch.as_str(), target_os.as_str()) { // Selection failure until recent LLVM // FIXME(llvm19): can probably be removed at the version bump @@ -128,10 +133,42 @@ fn main() { _ => false, }; + // These are currently empty, but will fill up as some platforms move from completely + // unreliable to reliable basics but unreliable math. + + // LLVM is currenlty adding missing routines, + let has_reliable_f16_math = has_reliable_f16 + && match (target_arch.as_str(), target_os.as_str()) { + // Currently nothing special. Hooray! + // This will change as platforms gain better better support for standard ops but math + // lags behind. + _ => true, + }; + + let has_reliable_f128_math = has_reliable_f128 + && match (target_arch.as_str(), target_os.as_str()) { + // LLVM lowers `fp128` math to `long double` symbols even on platforms where + // `long double` is not IEEE binary128. See + // . + // + // This rules out anything that doesn't have `long double` = `binary128`; <= 32 bits + // (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86` + // (ld is 80-bit extended precision). + ("x86_64", _) => false, + (_, "linux") if target_pointer_width == 64 => true, + _ => false, + }; + if has_reliable_f16 { println!("cargo:rustc-cfg=reliable_f16"); } if has_reliable_f128 { println!("cargo:rustc-cfg=reliable_f128"); } + if has_reliable_f16_math { + println!("cargo:rustc-cfg=reliable_f16_math"); + } + if has_reliable_f128_math { + println!("cargo:rustc-cfg=reliable_f128_math"); + } } diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index a5b00d57cefdd..97a57f2f460e9 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -12,25 +12,180 @@ pub use core::f128::consts; #[cfg(not(test))] use crate::intrinsics; +#[cfg(not(test))] +use crate::sys::cmath; #[cfg(not(test))] impl f128 { - /// Raises a number to an integer power. + /// Returns the largest integer less than or equal to `self`. /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. + /// This function always returns the precise result. /// - /// # Unspecified precision + /// # Examples /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f128 { - unsafe { intrinsics::powif128(self, n) } + pub fn floor(self) -> f128 { + unsafe { intrinsics::floorf128(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.01_f128; + /// let g = 4.0_f128; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f128 { + unsafe { intrinsics::ceilf128(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = -3.7_f128; + /// let i = 3.5_f128; + /// let j = 4.5_f128; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f128 { + unsafe { intrinsics::roundf128(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = 3.5_f128; + /// let i = 4.5_f128; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f128 { + unsafe { intrinsics::rintf128(self) } + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f128 { + unsafe { intrinsics::truncf128(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 3.6_f128; + /// let y = -3.6_f128; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f128::EPSILON); + /// assert!(abs_difference_y <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f128 { + self - self.trunc() } /// Computes the absolute value of `self`. @@ -41,7 +196,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128)] { // FIXME(f16_f128): reliable_f128 + /// # #[cfg(reliable_f128)] { /// /// let x = 3.5_f128; /// let y = -3.5_f128; @@ -61,4 +216,1129 @@ impl f128 { // We don't do this now because LLVM has lowering bugs for f128 math. Self::from_bits(self.to_bits() & !(1 << 127)) } + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `INFINITY` + /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// - NaN if the number is NaN + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.5_f128; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f128::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f128::NAN.signum().is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn signum(self) -> f128 { + if self.is_nan() { Self::NAN } else { 1.0_f128.copysign(self) } + } + + /// Returns a number composed of the magnitude of `self` and the sign of + /// `sign`. + /// + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise + /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of + /// `sign` is returned. Note, however, that conserving the sign bit on NaN + /// across arithmetical operations is not generally guaranteed. + /// See [explanation of NaN as a special value](primitive@f128) for more info. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.5_f128; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f128); + /// assert_eq!(f.copysign(-0.42), -3.5_f128); + /// assert_eq!((-f).copysign(0.42), 3.5_f128); + /// assert_eq!((-f).copysign(-0.42), -3.5_f128); + /// + /// assert!(f128::NAN.copysign(1.0).is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn copysign(self, sign: f128) -> f128 { + unsafe { intrinsics::copysignf128(self, sign) } + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let m = 10.0_f128; + /// let x = 4.0_f128; + /// let b = 60.0_f128; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f128 + f128::EPSILON; + /// let one_minus_eps = 1.0_f128 - f128::EPSILON; + /// let minus_one = -1.0_f128; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f128::EPSILON * f128::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f128, b: f128) -> f128 { + unsafe { intrinsics::fmaf128(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f128) -> f128 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f128::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f128) -> f128 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f128 { + unsafe { intrinsics::powif128(self, n) } + } + + /// Raises a number to a floating point power. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powf(self, n: f128) -> f128 { + unsafe { intrinsics::powf128(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let positive = 4.0_f128; + /// let negative = -4.0_f128; + /// let negative_zero = -0.0_f128; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f128 { + unsafe { intrinsics::sqrtf128(self) } + } + + /// Returns `e^(self)`, (the exponential function). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let one = 1.0f128; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp(self) -> f128 { + unsafe { intrinsics::expf128(self) } + } + + /// Returns `2^(self)`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 2.0f128; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp2(self) -> f128 { + unsafe { intrinsics::exp2f128(self) } + } + + /// Returns the natural logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let one = 1.0f128; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln(self) -> f128 { + unsafe { intrinsics::logf128(self) } + } + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result might not be correctly rounded owing to implementation details; + /// `self.log2()` can produce more accurate results for base 2, and + /// `self.log10()` can produce more accurate results for base 10. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let five = 5.0f128; + /// + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log(self, base: f128) -> f128 { + self.ln() / base.ln() + } + + /// Returns the base 2 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let two = 2.0f128; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log2(self) -> f128 { + crate::sys::log2f128(self) + } + + /// Returns the base 10 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let ten = 10.0f128; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log10(self) -> f128 { + unsafe { intrinsics::log10f128(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// + /// This function currently corresponds to the `cbrtf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 8.0f128; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(self) -> f128 { + unsafe { cmath::cbrtf128(self) } + } + + /// Compute the distance between the origin and a point (`x`, `y`) on the + /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a + /// right-angle triangle with other sides having length `x.abs()` and + /// `y.abs()`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// + /// This function currently corresponds to the `hypotf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0f128; + /// let y = 3.0f128; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn hypot(self, other: f128) -> f128 { + unsafe { cmath::hypotf128(self, other) } + } + + /// Computes the sine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_2; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sin(self) -> f128 { + unsafe { intrinsics::sinf128(self) } + } + + /// Computes the cosine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0 * std::f128::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cos(self) -> f128 { + unsafe { intrinsics::cosf128(self) } + } + + /// Computes the tangent of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanf128` from libc on Unix and + /// Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_4; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tan(self) -> f128 { + unsafe { cmath::tanf128(self) } + } + + /// Computes the arcsine of a number. Return value is in radians in + /// the range [-pi/2, pi/2] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `asinf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = std::f128::consts::FRAC_PI_2; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - std::f128::consts::FRAC_PI_2).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsin")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asin(self) -> f128 { + unsafe { cmath::asinf128(self) } + } + + /// Computes the arccosine of a number. Return value is in radians in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `acosf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = std::f128::consts::FRAC_PI_4; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - std::f128::consts::FRAC_PI_4).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acos(self) -> f128 { + unsafe { cmath::acosf128(self) } + } + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atanf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 1.0f128; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctan")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan(self) -> f128 { + unsafe { cmath::atanf128(self) } + } + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. + /// + /// * `x = 0`, `y = 0`: `0` + /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` + /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` + /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atan2f128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// // Positive angles measured counter-clockwise + /// // from positive x axis + /// // -pi/4 radians (45 deg clockwise) + /// let x1 = 3.0f128; + /// let y1 = -3.0f128; + /// + /// // 3pi/4 radians (135 deg counter-clockwise) + /// let x2 = -3.0f128; + /// let y2 = 3.0f128; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f128::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f128::consts::FRAC_PI_4)).abs(); + /// + /// assert!(abs_difference_1 <= f128::EPSILON); + /// assert!(abs_difference_2 <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan2(self, other: f128) -> f128 { + unsafe { cmath::atan2f128(self, other) } + } + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `(f128::sin(x), + /// f128::cos(x))`. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_4; + /// let f = x.sin_cos(); + /// + /// let abs_difference_0 = (f.0 - x.sin()).abs(); + /// let abs_difference_1 = (f.1 - x.cos()).abs(); + /// + /// assert!(abs_difference_0 <= f128::EPSILON); + /// assert!(abs_difference_1 <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "sincos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + pub fn sin_cos(self) -> (f128, f128) { + (self.sin(), self.cos()) + } + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `expm1f128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1e-8_f128; + /// + /// // for very small x, e^x is approximately 1 + x + x^2 / 2 + /// let approx = x + x * x / 2.0; + /// let abs_difference = (x.exp_m1() - approx).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp_m1(self) -> f128 { + unsafe { cmath::expm1f128(self) } + } + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `log1pf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1e-8_f128; + /// + /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 + /// let approx = x - x * x / 2.0; + /// let abs_difference = (x.ln_1p() - approx).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// # } + /// ``` + #[inline] + #[doc(alias = "log1p")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + pub fn ln_1p(self) -> f128 { + unsafe { cmath::log1pf128(self) } + } + + /// Hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `sinhf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// + /// let f = x.sinh(); + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// let g = ((e * e) - 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sinh(self) -> f128 { + unsafe { cmath::sinhf128(self) } + } + + /// Hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `coshf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// let f = x.cosh(); + /// // Solving cosh() at 1 gives this result + /// let g = ((e * e) + 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// // Same result + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cosh(self) -> f128 { + unsafe { cmath::coshf128(self) } + } + + /// Hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanhf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// + /// let f = x.tanh(); + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tanh(self) -> f128 { + unsafe { cmath::tanhf128(self) } + } + + /// Inverse hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1.0f128; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsinh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asinh(self) -> f128 { + let ax = self.abs(); + let ix = 1.0 / ax; + (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + } + + /// Inverse hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1.0f128; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccosh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acosh(self) -> f128 { + if self < 1.0 { + Self::NAN + } else { + (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() + } + } + + /// Inverse hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference <= 1e-5); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctanh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atanh(self) -> f128 { + 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() + } + + /// Gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tgammaf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 5.0f128; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn gamma(self) -> f128 { + unsafe { cmath::tgammaf128(self) } + } + + /// Natural logarithm of the absolute value of the gamma function + /// + /// The integer part of the tuple indicates the sign of the gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `lgammaf128_r` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0f128; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln_gamma(self) -> (f128, i32) { + let mut signgamp: i32 = 0; + let x = unsafe { cmath::lgammaf128_r(self, &mut signgamp) }; + (x, signgamp) + } } diff --git a/library/std/src/f128/tests.rs b/library/std/src/f128/tests.rs index 162c8dbad81a1..df806a639f620 100644 --- a/library/std/src/f128/tests.rs +++ b/library/std/src/f128/tests.rs @@ -4,6 +4,21 @@ use crate::f128::consts; use crate::num::{FpCategory as Fp, *}; +// Note these tolerances make sense around zero, but not for more extreme exponents. + +/// For operations that are near exact, usually not involving math of different +/// signs. +const TOL_PRECISE: f128 = 1e-28; + +/// Default tolerances. Works for values that should be near precise but not exact. Roughly +/// the precision carried by `100 * 100`. +const TOL: f128 = 1e-12; + +/// Tolerances for math that is allowed to be imprecise, usually due to multiple chained +/// operations. +#[cfg(reliable_f128_math)] +const TOL_IMPR: f128 = 1e-10; + /// Smallest number const TINY_BITS: u128 = 0x1; @@ -191,9 +206,100 @@ fn test_classify() { assert_eq!(1e-4932f128.classify(), Fp::Subnormal); } -// FIXME(f16_f128): add missing math functions when available +#[test] +#[cfg(reliable_f128_math)] +fn test_floor() { + assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ceil() { + assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_round() { + assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_trunc() { + assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_fract() { + assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE); +} #[test] +#[cfg(reliable_f128_math)] fn test_abs() { assert_eq!(f128::INFINITY.abs(), f128::INFINITY); assert_eq!(1f128.abs(), 1f128); @@ -293,6 +399,24 @@ fn test_next_down() { } #[test] +#[cfg(reliable_f128_math)] +fn test_mul_add() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE); + assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE); + assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE); + assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f128.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] fn test_recip() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; @@ -301,11 +425,161 @@ fn test_recip() { assert_eq!(2.0f128.recip(), 0.5); assert_eq!((-0.4f128).recip(), -2.5); assert_eq!(0.0f128.recip(), inf); + assert_approx_eq!( + f128::MAX.recip(), + 8.40525785778023376565669454330438228902076605e-4933, + 1e-4900 + ); assert!(nan.recip().is_nan()); assert_eq!(inf.recip(), 0.0); assert_eq!(neg_inf.recip(), 0.0); } +// Many math functions allow for less accurate results, so the next tolerance up is used + +#[test] +#[cfg(reliable_f128_math)] +fn test_powi() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powi(1), 1.0); + assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL); + assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL); + assert_eq!(8.3f128.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_powf() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powf(1.0), 1.0); + assert_approx_eq!(3.4f128.powf(4.5), 246.40818323761892815995637964326426756, TOL_IMPR); + assert_approx_eq!(2.7f128.powf(-3.2), 0.041652009108526178281070304373500889273, TOL_IMPR); + assert_approx_eq!((-3.1f128).powf(2.0), 9.6100000000000005506706202140776519387, TOL_IMPR); + assert_approx_eq!(5.9f128.powf(-2.0), 0.028727377190462507313100483690639638451, TOL_IMPR); + assert_eq!(8.3f128.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_sqrt_domain() { + assert!(f128::NAN.sqrt().is_nan()); + assert!(f128::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f128).sqrt().is_nan()); + assert_eq!((-0.0f128).sqrt(), -0.0); + assert_eq!(0.0f128.sqrt(), 0.0); + assert_eq!(1.0f128.sqrt(), 1.0); + assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f128.exp()); + assert_approx_eq!(consts::E, 1.0f128.exp(), TOL); + assert_approx_eq!(148.41315910257660342111558004055227962348775, 5.0f128.exp(), TOL); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f128.exp2()); + assert_eq!(1.0, 0.0f128.exp2()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ln() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(1.0f128.exp().ln(), 1.0, TOL); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f128).ln().is_nan()); + assert_eq!((-0.0f128).ln(), neg_inf); + assert_eq!(0.0f128.ln(), neg_inf); + assert_approx_eq!(4.0f128.ln(), 1.3862943611198906188344642429163531366, TOL); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log(10.0), 1.0); + assert_approx_eq!(2.3f128.log(3.5), 0.66485771361478710036766645911922010272, TOL); + assert_eq!(1.0f128.exp().log(1.0f128.exp()), 1.0); + assert!(1.0f128.log(1.0).is_nan()); + assert!(1.0f128.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f128).log(0.1).is_nan()); + assert_eq!((-0.0f128).log(2.0), neg_inf); + assert_eq!(0.0f128.log(7.0), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log2() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(10.0f128.log2(), 3.32192809488736234787031942948939017, TOL); + assert_approx_eq!(2.3f128.log2(), 1.2016338611696504130002982471978765921, TOL); + assert_approx_eq!(1.0f128.exp().log2(), 1.4426950408889634073599246810018921381, TOL); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f128).log2().is_nan()); + assert_eq!((-0.0f128).log2(), neg_inf); + assert_eq!(0.0f128.log2(), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log10() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log10(), 1.0); + assert_approx_eq!(2.3f128.log10(), 0.36172783601759284532595218865859309898, TOL); + assert_approx_eq!(1.0f128.exp().log10(), 0.43429448190325182765112891891660508222, TOL); + assert_eq!(1.0f128.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f128).log10().is_nan()); + assert_eq!((-0.0f128).log10(), neg_inf); + assert_eq!(0.0f128.log10(), neg_inf); +} + #[test] fn test_to_degrees() { let pi: f128 = consts::PI; @@ -313,8 +587,8 @@ fn test_to_degrees() { let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; assert_eq!(0.0f128.to_degrees(), 0.0); - assert_approx_eq!((-5.8f128).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); + assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL); assert!(nan.to_degrees().is_nan()); assert_eq!(inf.to_degrees(), inf); assert_eq!(neg_inf.to_degrees(), neg_inf); @@ -328,19 +602,122 @@ fn test_to_radians() { let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; assert_eq!(0.0f128.to_radians(), 0.0); - assert_approx_eq!(154.6f128.to_radians(), 2.698279); - assert_approx_eq!((-332.31f128).to_radians(), -5.799903); + assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL); + assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL); // check approx rather than exact because round trip for pi doesn't fall on an exactly // representable value (unlike `f32` and `f64`). - assert_approx_eq!(180.0f128.to_radians(), pi); + assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE); assert!(nan.to_radians().is_nan()); assert_eq!(inf.to_radians(), inf); assert_eq!(neg_inf.to_radians(), neg_inf); } +#[test] +#[cfg(reliable_f128_math)] +fn test_asinh() { + // Lower accuracy results are allowed, use increased tolerances + assert_eq!(0.0f128.asinh(), 0.0f128); + assert_eq!((-0.0f128).asinh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f128).asinh().is_sign_negative()); + + // issue 63271 + assert_approx_eq!(2.0f128.asinh(), 1.443635475178810342493276740273105f128, TOL_IMPR); + assert_approx_eq!((-2.0f128).asinh(), -1.443635475178810342493276740273105f128, TOL_IMPR); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!( + (-67452098.07139316f128).asinh(), + -18.720075426274544393985484294000831757220, + TOL_IMPR + ); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.sinh().asinh(), TOL_IMPR); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f128, 1e-15f128.sinh().asinh() * 1e15f128, TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_acosh() { + assert_eq!(1.0f128.acosh(), 0.0f128); + assert!(0.999f128.acosh().is_nan()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f128.acosh(), 1.31695789692481670862504634730796844f128, TOL_IMPR); + assert_approx_eq!(3.0f128.acosh(), 1.76274717403908605046521864995958461f128, TOL_IMPR); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.cosh().acosh(), TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_atanh() { + assert_eq!(0.0f128.atanh(), 0.0f128); + assert_eq!((-0.0f128).atanh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(1.0f128.atanh(), inf); + assert_eq!((-1.0f128).atanh(), neg_inf); + assert!(2f128.atanh().atanh().is_nan()); + assert!((-2f128).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f128.atanh(), 0.54930614433405484569762261846126285f128, TOL_IMPR); + assert_approx_eq!((-0.5f128).atanh(), -0.54930614433405484569762261846126285f128, TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(2.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(3.0f128.gamma(), 2.0f128, TOL_IMPR); + assert_approx_eq!(4.0f128.gamma(), 6.0f128, TOL_IMPR); + assert_approx_eq!(5.0f128.gamma(), 24.0f128, TOL_IMPR); + assert_approx_eq!(0.5f128.gamma(), consts::PI.sqrt(), TOL_IMPR); + assert_approx_eq!((-0.5f128).gamma(), -2.0 * consts::PI.sqrt(), TOL_IMPR); + assert_eq!(0.0f128.gamma(), f128::INFINITY); + assert_eq!((-0.0f128).gamma(), f128::NEG_INFINITY); + assert!((-1.0f128).gamma().is_nan()); + assert!((-2.0f128).gamma().is_nan()); + assert!(f128::NAN.gamma().is_nan()); + assert!(f128::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f128::INFINITY.gamma(), f128::INFINITY); + assert_eq!(1760.9f128.gamma(), f128::INFINITY); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(1.0f128.ln_gamma().1, 1); + assert_approx_eq!(2.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(2.0f128.ln_gamma().1, 1); + assert_approx_eq!(3.0f128.ln_gamma().0, 2.0f128.ln(), TOL_IMPR); + assert_eq!(3.0f128.ln_gamma().1, 1); + assert_approx_eq!((-0.5f128).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_IMPR); + assert_eq!((-0.5f128).ln_gamma().1, -1); +} + #[test] fn test_real_consts() { - // FIXME(f16_f128): add math tests when available use super::consts; let pi: f128 = consts::PI; @@ -351,29 +728,34 @@ fn test_real_consts() { let frac_pi_8: f128 = consts::FRAC_PI_8; let frac_1_pi: f128 = consts::FRAC_1_PI; let frac_2_pi: f128 = consts::FRAC_2_PI; - // let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; - // let sqrt2: f128 = consts::SQRT_2; - // let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; - // let e: f128 = consts::E; - // let log2_e: f128 = consts::LOG2_E; - // let log10_e: f128 = consts::LOG10_E; - // let ln_2: f128 = consts::LN_2; - // let ln_10: f128 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f128); - assert_approx_eq!(frac_pi_3, pi / 3f128); - assert_approx_eq!(frac_pi_4, pi / 4f128); - assert_approx_eq!(frac_pi_6, pi / 6f128); - assert_approx_eq!(frac_pi_8, pi / 8f128); - assert_approx_eq!(frac_1_pi, 1f128 / pi); - assert_approx_eq!(frac_2_pi, 2f128 / pi); - // assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt()); - // assert_approx_eq!(sqrt2, 2f128.sqrt()); - // assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt()); - // assert_approx_eq!(log2_e, e.log2()); - // assert_approx_eq!(log10_e, e.log10()); - // assert_approx_eq!(ln_2, 2f128.ln()); - // assert_approx_eq!(ln_10, 10f128.ln()); + + assert_approx_eq!(frac_pi_2, pi / 2f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_3, pi / 3f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_4, pi / 4f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_6, pi / 6f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_8, pi / 8f128, TOL_PRECISE); + assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE); + assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE); + + #[cfg(reliable_f128_math)] + { + let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; + let sqrt2: f128 = consts::SQRT_2; + let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; + let e: f128 = consts::E; + let log2_e: f128 = consts::LOG2_E; + let log10_e: f128 = consts::LOG10_E; + let ln_2: f128 = consts::LN_2; + let ln_10: f128 = consts::LN_10; + + assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt(), TOL_PRECISE); + assert_approx_eq!(sqrt2, 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(log2_e, e.log2(), TOL_PRECISE); + assert_approx_eq!(log10_e, e.log10(), TOL_PRECISE); + assert_approx_eq!(ln_2, 2f128.ln(), TOL_PRECISE); + assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); + } } #[test] @@ -382,10 +764,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); - assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); - assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5); - assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); - assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); + assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE); // Check that NaNs roundtrip their bits regardless of signaling-ness // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index e3024defed734..55877316e5b98 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -12,25 +12,180 @@ pub use core::f16::consts; #[cfg(not(test))] use crate::intrinsics; +#[cfg(not(test))] +use crate::sys::cmath; #[cfg(not(test))] impl f16 { - /// Raises a number to an integer power. + /// Returns the largest integer less than or equal to `self`. /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. + /// This function always returns the precise result. /// - /// # Unspecified precision + /// # Examples /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f16 { - unsafe { intrinsics::powif16(self, n) } + pub fn floor(self) -> f16 { + unsafe { intrinsics::floorf16(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.01_f16; + /// let g = 4.0_f16; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f16 { + unsafe { intrinsics::ceilf16(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = -3.7_f16; + /// let i = 3.5_f16; + /// let j = 4.5_f16; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f16 { + unsafe { intrinsics::roundf16(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = 3.5_f16; + /// let i = 4.5_f16; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f16 { + unsafe { intrinsics::rintf16(self) } + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f16 { + unsafe { intrinsics::truncf16(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 3.6_f16; + /// let y = -3.6_f16; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f16::EPSILON); + /// assert!(abs_difference_y <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f16 { + self - self.trunc() } /// Computes the absolute value of `self`. @@ -60,4 +215,1127 @@ impl f16 { // FIXME(f16_f128): replace with `intrinsics::fabsf16` when available Self::from_bits(self.to_bits() & !(1 << 15)) } + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `INFINITY` + /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// - NaN if the number is NaN + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.5_f16; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f16::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f16::NAN.signum().is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn signum(self) -> f16 { + if self.is_nan() { Self::NAN } else { 1.0_f16.copysign(self) } + } + + /// Returns a number composed of the magnitude of `self` and the sign of + /// `sign`. + /// + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise + /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of + /// `sign` is returned. Note, however, that conserving the sign bit on NaN + /// across arithmetical operations is not generally guaranteed. + /// See [explanation of NaN as a special value](primitive@f16) for more info. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.5_f16; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f16); + /// assert_eq!(f.copysign(-0.42), -3.5_f16); + /// assert_eq!((-f).copysign(0.42), 3.5_f16); + /// assert_eq!((-f).copysign(-0.42), -3.5_f16); + /// + /// assert!(f16::NAN.copysign(1.0).is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn copysign(self, sign: f16) -> f16 { + unsafe { intrinsics::copysignf16(self, sign) } + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let m = 10.0_f16; + /// let x = 4.0_f16; + /// let b = 60.0_f16; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f16 + f16::EPSILON; + /// let one_minus_eps = 1.0_f16 - f16::EPSILON; + /// let minus_one = -1.0_f16; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f16::EPSILON * f16::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f16, b: f16) -> f16 { + unsafe { intrinsics::fmaf16(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f16) -> f16 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f16::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f16) -> f16 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f16 { + unsafe { intrinsics::powif16(self, n) } + } + + /// Raises a number to a floating point power. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powf(self, n: f16) -> f16 { + unsafe { intrinsics::powf16(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let positive = 4.0_f16; + /// let negative = -4.0_f16; + /// let negative_zero = -0.0_f16; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f16 { + unsafe { intrinsics::sqrtf16(self) } + } + + /// Returns `e^(self)`, (the exponential function). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let one = 1.0f16; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp(self) -> f16 { + unsafe { intrinsics::expf16(self) } + } + + /// Returns `2^(self)`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 2.0f16; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp2(self) -> f16 { + unsafe { intrinsics::exp2f16(self) } + } + + /// Returns the natural logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let one = 1.0f16; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln(self) -> f16 { + unsafe { intrinsics::logf16(self) } + } + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result might not be correctly rounded owing to implementation details; + /// `self.log2()` can produce more accurate results for base 2, and + /// `self.log10()` can produce more accurate results for base 10. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let five = 5.0f16; + /// + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log(self, base: f16) -> f16 { + self.ln() / base.ln() + } + + /// Returns the base 2 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let two = 2.0f16; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log2(self) -> f16 { + crate::sys::log2f16(self) + } + + /// Returns the base 10 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let ten = 10.0f16; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log10(self) -> f16 { + unsafe { intrinsics::log10f16(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `cbrtf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 8.0f16; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(self) -> f16 { + (unsafe { cmath::cbrtf(self as f32) }) as f16 + } + + /// Compute the distance between the origin and a point (`x`, `y`) on the + /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a + /// right-angle triangle with other sides having length `x.abs()` and + /// `y.abs()`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `hypotf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0f16; + /// let y = 3.0f16; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn hypot(self, other: f16) -> f16 { + (unsafe { cmath::hypotf(self as f32, other as f32) }) as f16 + } + + /// Computes the sine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_2; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sin(self) -> f16 { + unsafe { intrinsics::sinf16(self) } + } + + /// Computes the cosine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0 * std::f16::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cos(self) -> f16 { + unsafe { intrinsics::cosf16(self) } + } + + /// Computes the tangent of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanf` from libc on Unix and + /// Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_4; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tan(self) -> f16 { + (unsafe { cmath::tanf(self as f32) }) as f16 + } + + /// Computes the arcsine of a number. Return value is in radians in + /// the range [-pi/2, pi/2] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `asinf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = std::f16::consts::FRAC_PI_2; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - std::f16::consts::FRAC_PI_2).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsin")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asin(self) -> f16 { + (unsafe { cmath::asinf(self as f32) }) as f16 + } + + /// Computes the arccosine of a number. Return value is in radians in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `acosf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = std::f16::consts::FRAC_PI_4; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - std::f16::consts::FRAC_PI_4).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acos(self) -> f16 { + (unsafe { cmath::acosf(self as f32) }) as f16 + } + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atanf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 1.0f16; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctan")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan(self) -> f16 { + (unsafe { cmath::atanf(self as f32) }) as f16 + } + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. + /// + /// * `x = 0`, `y = 0`: `0` + /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` + /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` + /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atan2f` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// // Positive angles measured counter-clockwise + /// // from positive x axis + /// // -pi/4 radians (45 deg clockwise) + /// let x1 = 3.0f16; + /// let y1 = -3.0f16; + /// + /// // 3pi/4 radians (135 deg counter-clockwise) + /// let x2 = -3.0f16; + /// let y2 = 3.0f16; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f16::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f16::consts::FRAC_PI_4)).abs(); + /// + /// assert!(abs_difference_1 <= f16::EPSILON); + /// assert!(abs_difference_2 <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan2(self, other: f16) -> f16 { + (unsafe { cmath::atan2f(self as f32, other as f32) }) as f16 + } + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `(f16::sin(x), + /// f16::cos(x))`. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_4; + /// let f = x.sin_cos(); + /// + /// let abs_difference_0 = (f.0 - x.sin()).abs(); + /// let abs_difference_1 = (f.1 - x.cos()).abs(); + /// + /// assert!(abs_difference_0 <= f16::EPSILON); + /// assert!(abs_difference_1 <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "sincos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + pub fn sin_cos(self) -> (f16, f16) { + (self.sin(), self.cos()) + } + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `expm1f` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1e-4_f16; + /// + /// // for very small x, e^x is approximately 1 + x + x^2 / 2 + /// let approx = x + x * x / 2.0; + /// let abs_difference = (x.exp_m1() - approx).abs(); + /// + /// assert!(abs_difference < 1e-4); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp_m1(self) -> f16 { + (unsafe { cmath::expm1f(self as f32) }) as f16 + } + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `log1pf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1e-4_f16; + /// + /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 + /// let approx = x - x * x / 2.0; + /// let abs_difference = (x.ln_1p() - approx).abs(); + /// + /// assert!(abs_difference < 1e-4); + /// # } + /// ``` + #[inline] + #[doc(alias = "log1p")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln_1p(self) -> f16 { + (unsafe { cmath::log1pf(self as f32) }) as f16 + } + + /// Hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `sinhf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// + /// let f = x.sinh(); + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// let g = ((e * e) - 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sinh(self) -> f16 { + (unsafe { cmath::sinhf(self as f32) }) as f16 + } + + /// Hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `coshf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// let f = x.cosh(); + /// // Solving cosh() at 1 gives this result + /// let g = ((e * e) + 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// // Same result + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cosh(self) -> f16 { + (unsafe { cmath::coshf(self as f32) }) as f16 + } + + /// Hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanhf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// + /// let f = x.tanh(); + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tanh(self) -> f16 { + (unsafe { cmath::tanhf(self as f32) }) as f16 + } + + /// Inverse hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1.0f16; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsinh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asinh(self) -> f16 { + let ax = self.abs(); + let ix = 1.0 / ax; + (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + } + + /// Inverse hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1.0f16; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccosh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acosh(self) -> f16 { + if self < 1.0 { + Self::NAN + } else { + (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() + } + } + + /// Inverse hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference <= 0.01); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctanh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atanh(self) -> f16 { + 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() + } + + /// Gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tgammaf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 5.0f16; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn gamma(self) -> f16 { + (unsafe { cmath::tgammaf(self as f32) }) as f16 + } + + /// Natural logarithm of the absolute value of the gamma function + /// + /// The integer part of the tuple indicates the sign of the gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `lgamma_r` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0f16; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln_gamma(self) -> (f16, i32) { + let mut signgamp: i32 = 0; + let x = (unsafe { cmath::lgammaf_r(self as f32, &mut signgamp) }) as f16; + (x, signgamp) + } } diff --git a/library/std/src/f16/tests.rs b/library/std/src/f16/tests.rs index f73bdf68e8295..f0ef807dac141 100644 --- a/library/std/src/f16/tests.rs +++ b/library/std/src/f16/tests.rs @@ -4,11 +4,21 @@ use crate::f16::consts; use crate::num::{FpCategory as Fp, *}; -// We run out of precision pretty quickly with f16 -// const F16_APPROX_L1: f16 = 0.001; -const F16_APPROX_L2: f16 = 0.01; -// const F16_APPROX_L3: f16 = 0.1; -const F16_APPROX_L4: f16 = 0.5; +/// Tolerance for results on the order of 10.0e-2; +#[cfg(reliable_f16_math)] +const TOL_N2: f16 = 0.0001; + +/// Tolerance for results on the order of 10.0e+0 +#[cfg(reliable_f16_math)] +const TOL_0: f16 = 0.01; + +/// Tolerance for results on the order of 10.0e+2 +#[cfg(reliable_f16_math)] +const TOL_P2: f16 = 0.5; + +/// Tolerance for results on the order of 10.0e+4 +#[cfg(reliable_f16_math)] +const TOL_P4: f16 = 10.0; /// Smallest number const TINY_BITS: u16 = 0x1; @@ -197,9 +207,100 @@ fn test_classify() { assert_eq!(1e-5f16.classify(), Fp::Subnormal); } -// FIXME(f16_f128): add missing math functions when available +#[test] +#[cfg(reliable_f16_math)] +fn test_floor() { + assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ceil() { + assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_round() { + assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0); + assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0); +} #[test] +#[cfg(reliable_f16_math)] +fn test_trunc() { + assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_fract() { + assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0); + assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0); + assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0); + assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0); + assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0); + assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] fn test_abs() { assert_eq!(f16::INFINITY.abs(), f16::INFINITY); assert_eq!(1f16.abs(), 1f16); @@ -299,6 +400,24 @@ fn test_next_down() { } #[test] +#[cfg(reliable_f16_math)] +fn test_mul_add() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2); + assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2); + assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0); + assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f16.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] fn test_recip() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; @@ -307,11 +426,157 @@ fn test_recip() { assert_eq!(2.0f16.recip(), 0.5); assert_eq!((-0.4f16).recip(), -2.5); assert_eq!(0.0f16.recip(), inf); + assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); assert!(nan.recip().is_nan()); assert_eq!(inf.recip(), 0.0); assert_eq!(neg_inf.recip(), 0.0); } +#[test] +#[cfg(reliable_f16_math)] +fn test_powi() { + // FIXME(llvm19): LLVM misoptimizes `powi.f16` + // + // let nan: f16 = f16::NAN; + // let inf: f16 = f16::INFINITY; + // let neg_inf: f16 = f16::NEG_INFINITY; + // assert_eq!(1.0f16.powi(1), 1.0); + // assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0); + // assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2); + // assert_eq!(8.3f16.powi(0), 1.0); + // assert!(nan.powi(2).is_nan()); + // assert_eq!(inf.powi(3), inf); + // assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_powf() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.powf(1.0), 1.0); + assert_approx_eq!(3.4f16.powf(4.5), 246.408183, TOL_P2); + assert_approx_eq!(2.7f16.powf(-3.2), 0.041652, TOL_N2); + assert_approx_eq!((-3.1f16).powf(2.0), 9.61, TOL_P2); + assert_approx_eq!(5.9f16.powf(-2.0), 0.028727, TOL_N2); + assert_eq!(8.3f16.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_sqrt_domain() { + assert!(f16::NAN.sqrt().is_nan()); + assert!(f16::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f16).sqrt().is_nan()); + assert_eq!((-0.0f16).sqrt(), -0.0); + assert_eq!(0.0f16.sqrt(), 0.0); + assert_eq!(1.0f16.sqrt(), 1.0); + assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f16.exp()); + assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0); + assert_approx_eq!(148.413159, 5.0f16.exp(), TOL_0); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f16.exp2()); + assert_eq!(1.0, 0.0f16.exp2()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ln() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(1.0f16.exp().ln(), 1.0, TOL_0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f16).ln().is_nan()); + assert_eq!((-0.0f16).ln(), neg_inf); + assert_eq!(0.0f16.ln(), neg_inf); + assert_approx_eq!(4.0f16.ln(), 1.386294, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log(10.0), 1.0); + assert_approx_eq!(2.3f16.log(3.5), 0.664858, TOL_0); + assert_eq!(1.0f16.exp().log(1.0f16.exp()), 1.0); + assert!(1.0f16.log(1.0).is_nan()); + assert!(1.0f16.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f16).log(0.1).is_nan()); + assert_eq!((-0.0f16).log(2.0), neg_inf); + assert_eq!(0.0f16.log(7.0), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log2() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(10.0f16.log2(), 3.321928, TOL_0); + assert_approx_eq!(2.3f16.log2(), 1.201634, TOL_0); + assert_approx_eq!(1.0f16.exp().log2(), 1.442695, TOL_0); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f16).log2().is_nan()); + assert_eq!((-0.0f16).log2(), neg_inf); + assert_eq!(0.0f16.log2(), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log10() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log10(), 1.0); + assert_approx_eq!(2.3f16.log10(), 0.361728, TOL_0); + assert_approx_eq!(1.0f16.exp().log10(), 0.434294, TOL_0); + assert_eq!(1.0f16.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f16).log10().is_nan()); + assert_eq!((-0.0f16).log10(), neg_inf); + assert_eq!(0.0f16.log10(), neg_inf); +} + #[test] fn test_to_degrees() { let pi: f16 = consts::PI; @@ -319,8 +584,8 @@ fn test_to_degrees() { let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; assert_eq!(0.0f16.to_degrees(), 0.0); - assert_approx_eq!((-5.8f16).to_degrees(), -332.315521); - assert_approx_eq!(pi.to_degrees(), 180.0, F16_APPROX_L4); + assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2); assert!(nan.to_degrees().is_nan()); assert_eq!(inf.to_degrees(), inf); assert_eq!(neg_inf.to_degrees(), neg_inf); @@ -334,14 +599,112 @@ fn test_to_radians() { let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; assert_eq!(0.0f16.to_radians(), 0.0); - assert_approx_eq!(154.6f16.to_radians(), 2.698279); - assert_approx_eq!((-332.31f16).to_radians(), -5.799903); - assert_approx_eq!(180.0f16.to_radians(), pi, F16_APPROX_L2); + assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0); + assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0); + assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0); assert!(nan.to_radians().is_nan()); assert_eq!(inf.to_radians(), inf); assert_eq!(neg_inf.to_radians(), neg_inf); } +#[test] +#[cfg(reliable_f16_math)] +fn test_asinh() { + assert_eq!(0.0f16.asinh(), 0.0f16); + assert_eq!((-0.0f16).asinh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f16).asinh().is_sign_negative()); + // issue 63271 + assert_approx_eq!(2.0f16.asinh(), 1.443635475178810342493276740273105f16, TOL_0); + assert_approx_eq!((-2.0f16).asinh(), -1.443635475178810342493276740273105f16, TOL_0); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-200.0f16).asinh(), -5.991470797049389, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.sinh().asinh(), TOL_0); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f16, 1e-3f16.sinh().asinh() * 1e3f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_acosh() { + assert_eq!(1.0f16.acosh(), 0.0f16); + assert!(0.999f16.acosh().is_nan()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f16.acosh(), 1.31695789692481670862504634730796844f16, TOL_0); + assert_approx_eq!(3.0f16.acosh(), 1.76274717403908605046521864995958461f16, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.cosh().acosh(), TOL_P2); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_atanh() { + assert_eq!(0.0f16.atanh(), 0.0f16); + assert_eq!((-0.0f16).atanh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(1.0f16.atanh(), inf); + assert_eq!((-1.0f16).atanh(), neg_inf); + assert!(2f16.atanh().atanh().is_nan()); + assert!((-2f16).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f16.atanh(), 0.54930614433405484569762261846126285f16, TOL_0); + assert_approx_eq!((-0.5f16).atanh(), -0.54930614433405484569762261846126285f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(2.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(3.0f16.gamma(), 2.0f16, TOL_0); + assert_approx_eq!(4.0f16.gamma(), 6.0f16, TOL_0); + assert_approx_eq!(5.0f16.gamma(), 24.0f16, TOL_0); + assert_approx_eq!(0.5f16.gamma(), consts::PI.sqrt(), TOL_0); + assert_approx_eq!((-0.5f16).gamma(), -2.0 * consts::PI.sqrt(), TOL_0); + assert_eq!(0.0f16.gamma(), f16::INFINITY); + assert_eq!((-0.0f16).gamma(), f16::NEG_INFINITY); + assert!((-1.0f16).gamma().is_nan()); + assert!((-2.0f16).gamma().is_nan()); + assert!(f16::NAN.gamma().is_nan()); + assert!(f16::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f16::INFINITY.gamma(), f16::INFINITY); + assert_eq!(171.71f16.gamma(), f16::INFINITY); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(1.0f16.ln_gamma().1, 1); + assert_approx_eq!(2.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(2.0f16.ln_gamma().1, 1); + assert_approx_eq!(3.0f16.ln_gamma().0, 2.0f16.ln(), TOL_0); + assert_eq!(3.0f16.ln_gamma().1, 1); + assert_approx_eq!((-0.5f16).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_0); + assert_eq!((-0.5f16).ln_gamma().1, -1); +} + #[test] fn test_real_consts() { // FIXME(f16_f128): add math tests when available @@ -355,29 +718,34 @@ fn test_real_consts() { let frac_pi_8: f16 = consts::FRAC_PI_8; let frac_1_pi: f16 = consts::FRAC_1_PI; let frac_2_pi: f16 = consts::FRAC_2_PI; - // let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; - // let sqrt2: f16 = consts::SQRT_2; - // let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; - // let e: f16 = consts::E; - // let log2_e: f16 = consts::LOG2_E; - // let log10_e: f16 = consts::LOG10_E; - // let ln_2: f16 = consts::LN_2; - // let ln_10: f16 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f16); - assert_approx_eq!(frac_pi_3, pi / 3f16); - assert_approx_eq!(frac_pi_4, pi / 4f16); - assert_approx_eq!(frac_pi_6, pi / 6f16); - assert_approx_eq!(frac_pi_8, pi / 8f16); - assert_approx_eq!(frac_1_pi, 1f16 / pi); - assert_approx_eq!(frac_2_pi, 2f16 / pi); - // assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt()); - // assert_approx_eq!(sqrt2, 2f16.sqrt()); - // assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt()); - // assert_approx_eq!(log2_e, e.log2()); - // assert_approx_eq!(log10_e, e.log10()); - // assert_approx_eq!(ln_2, 2f16.ln()); - // assert_approx_eq!(ln_10, 10f16.ln()); + + assert_approx_eq!(frac_pi_2, pi / 2f16, TOL_0); + assert_approx_eq!(frac_pi_3, pi / 3f16, TOL_0); + assert_approx_eq!(frac_pi_4, pi / 4f16, TOL_0); + assert_approx_eq!(frac_pi_6, pi / 6f16, TOL_0); + assert_approx_eq!(frac_pi_8, pi / 8f16, TOL_0); + assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0); + assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0); + + #[cfg(reliable_f16_math)] + { + let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; + let sqrt2: f16 = consts::SQRT_2; + let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; + let e: f16 = consts::E; + let log2_e: f16 = consts::LOG2_E; + let log10_e: f16 = consts::LOG10_E; + let ln_2: f16 = consts::LN_2; + let ln_10: f16 = consts::LN_10; + + assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt(), TOL_0); + assert_approx_eq!(sqrt2, 2f16.sqrt(), TOL_0); + assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt(), TOL_0); + assert_approx_eq!(log2_e, e.log2(), TOL_0); + assert_approx_eq!(log10_e, e.log10(), TOL_0); + assert_approx_eq!(ln_2, 2f16.ln(), TOL_0); + assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); + } } #[test] @@ -386,10 +754,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f16).to_bits(), 0x4a40); assert_eq!((1337f16).to_bits(), 0x6539); assert_eq!((-14.25f16).to_bits(), 0xcb20); - assert_approx_eq!(f16::from_bits(0x3c00), 1.0); - assert_approx_eq!(f16::from_bits(0x4a40), 12.5); - assert_approx_eq!(f16::from_bits(0x6539), 1337.0); - assert_approx_eq!(f16::from_bits(0xcb20), -14.25); + assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0); + assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0); + assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4); + assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0); // Check that NaNs roundtrip their bits regardless of signaling-ness let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index ba519afc62b07..1b0d7f3dbf2c9 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -382,7 +382,7 @@ macro_rules! assert_approx_eq { let diff = (*a - *b).abs(); assert!( diff < $lim, - "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, actual {diff:?})", + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", lim = $lim ); }}; diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index 99df503b82de2..2997e908fa1b2 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -28,6 +28,21 @@ extern "C" { pub fn lgamma_r(n: f64, s: &mut i32) -> f64; pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; + pub fn acosf128(n: f128) -> f128; + pub fn asinf128(n: f128) -> f128; + pub fn atanf128(n: f128) -> f128; + pub fn atan2f128(a: f128, b: f128) -> f128; + pub fn cbrtf128(n: f128) -> f128; + pub fn coshf128(n: f128) -> f128; + pub fn expm1f128(n: f128) -> f128; + pub fn hypotf128(x: f128, y: f128) -> f128; + pub fn log1pf128(n: f128) -> f128; + pub fn sinhf128(n: f128) -> f128; + pub fn tanf128(n: f128) -> f128; + pub fn tanhf128(n: f128) -> f128; + pub fn tgammaf128(n: f128) -> f128; + pub fn lgammaf128_r(n: f128, s: &mut i32) -> f128; + cfg_if::cfg_if! { if #[cfg(not(all(target_os = "windows", target_env = "msvc", target_arch = "x86")))] { pub fn acosf(n: f32) -> f32; diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index df0176244489a..48de5bab8776a 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -79,9 +79,16 @@ cfg_if::cfg_if! { #[cfg(not(test))] cfg_if::cfg_if! { if #[cfg(target_os = "android")] { + pub use self::android::log2f16; pub use self::android::log2f32; pub use self::android::log2f64; + pub use self::android::log2f128; } else { + #[inline] + pub fn log2f16(n: f16) -> f16 { + unsafe { crate::intrinsics::log2f16(n) } + } + #[inline] pub fn log2f32(n: f32) -> f32 { unsafe { crate::intrinsics::log2f32(n) } @@ -91,6 +98,11 @@ cfg_if::cfg_if! { pub fn log2f64(n: f64) -> f64 { unsafe { crate::intrinsics::log2f64(n) } } + + #[inline] + pub fn log2f128(n: f128) -> f128 { + unsafe { crate::intrinsics::log2f128(n) } + } } } diff --git a/library/std/src/sys/pal/unix/android.rs b/library/std/src/sys/pal/unix/android.rs index 0f704994f550a..62b82c567eec2 100644 --- a/library/std/src/sys/pal/unix/android.rs +++ b/library/std/src/sys/pal/unix/android.rs @@ -45,6 +45,11 @@ use super::weak::weak; // // log_2(x) = ln(x) * log_2(e) +#[cfg(not(test))] +pub fn log2f16(f: f16) -> f16 { + f.ln() * crate::f16::consts::LOG2_E +} + #[cfg(not(test))] pub fn log2f32(f: f32) -> f32 { f.ln() * crate::f32::consts::LOG2_E @@ -55,6 +60,11 @@ pub fn log2f64(f: f64) -> f64 { f.ln() * crate::f64::consts::LOG2_E } +#[cfg(not(test))] +pub fn log2f128(f: f128) -> f128 { + f.ln() * crate::f128::consts::LOG2_E +} + // Back in the day [1] the `signal` function was just an inline wrapper // around `bsd_signal`, but starting in API level android-20 the `signal` // symbols was introduced [2]. Finally, in android-21 the API `bsd_signal` was From a4657ddd4d935f748f14fd535610dce332bdfa10 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 17 Jul 2024 16:17:09 -0400 Subject: [PATCH 16/18] Add `core` functions for `f16` and `f128` that require math routines `min`, `max`, and similar functions require external math routines. Add these under the same gates as `std` math functions (`reliable_f16_math` and `reliable_f128_math`). --- library/core/src/num/f128.rs | 176 ++++++++++++++++++++++++++++++++++ library/core/src/num/f16.rs | 171 +++++++++++++++++++++++++++++++++ library/std/src/f128/tests.rs | 28 +++++- library/std/src/f16/tests.rs | 28 +++++- 4 files changed, 401 insertions(+), 2 deletions(-) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 6a24748fd9e87..cfe3a20df532e 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -686,6 +686,182 @@ impl f128 { self * RADS_PER_DEG } + /// Returns the maximum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. + /// This also matches the behavior of libm’s fmax. + /// + /// ``` + /// #![feature(f128)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.max(y), y); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn max(self, other: f128) -> f128 { + intrinsics::maxnumf128(self, other) + } + + /// Returns the minimum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids minNum's problems with associativity. + /// This also matches the behavior of libm’s fmin. + /// + /// ``` + /// #![feature(f128)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.min(y), x); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn min(self, other: f128) -> f128 { + intrinsics::minnumf128(self, other) + } + + /// Returns the maximum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f128::max`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_minimum_maximum)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.maximum(y), y); + /// assert!(x.maximum(f128::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f128) for more info. + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn maximum(self, other: f128) -> f128 { + if self > other { + self + } else if other > self { + other + } else if self == other { + if self.is_sign_positive() && other.is_sign_negative() { self } else { other } + } else { + self + other + } + } + + /// Returns the minimum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f128::min`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_minimum_maximum)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.minimum(y), x); + /// assert!(x.minimum(f128::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f128) for more info. + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn minimum(self, other: f128) -> f128 { + if self < other { + self + } else if other < self { + other + } else if self == other { + if self.is_sign_negative() && other.is_sign_positive() { self } else { other } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + self + other + } + } + + /// Calculates the middle point of `self` and `rhs`. + /// + /// This returns NaN when *either* argument is NaN or if a combination of + /// +inf and -inf is provided as arguments. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(num_midpoint)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// assert_eq!(1f128.midpoint(4.0), 2.5); + /// assert_eq!((-5.5f128).midpoint(8.0), 1.25); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f128) -> f128 { + const LO: f128 = f128::MIN_POSITIVE * 2.; + const HI: f128 = f128::MAX / 2.; + + let (a, b) = (self, other); + let abs_a = a.abs_private(); + let abs_b = b.abs_private(); + + if abs_a <= HI && abs_b <= HI { + // Overflow is impossible + (a + b) / 2. + } else if abs_a < LO { + // Not safe to halve a + a + (b / 2.) + } else if abs_b < LO { + // Not safe to halve b + (a / 2.) + b + } else { + // Not safe to halve a and b + (a / 2.) + (b / 2.) + } + } + /// Rounds toward zero and converts to any primitive integer type, /// assuming that the value is finite and fits in that type. /// diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 054897b3c96bc..8947bf0d84826 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -720,6 +720,177 @@ impl f16 { self * RADS_PER_DEG } + /// Returns the maximum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. + /// This also matches the behavior of libm’s fmax. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.max(y), y); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn max(self, other: f16) -> f16 { + intrinsics::maxnumf16(self, other) + } + + /// Returns the minimum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids minNum's problems with associativity. + /// This also matches the behavior of libm’s fmin. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.min(y), x); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn min(self, other: f16) -> f16 { + intrinsics::minnumf16(self, other) + } + + /// Returns the maximum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f16::max`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_minimum_maximum)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.maximum(y), y); + /// assert!(x.maximum(f16::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f16) for more info. + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn maximum(self, other: f16) -> f16 { + if self > other { + self + } else if other > self { + other + } else if self == other { + if self.is_sign_positive() && other.is_sign_negative() { self } else { other } + } else { + self + other + } + } + + /// Returns the minimum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f16::min`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_minimum_maximum)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.minimum(y), x); + /// assert!(x.minimum(f16::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f16) for more info. + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn minimum(self, other: f16) -> f16 { + if self < other { + self + } else if other < self { + other + } else if self == other { + if self.is_sign_negative() && other.is_sign_positive() { self } else { other } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + self + other + } + } + + /// Calculates the middle point of `self` and `rhs`. + /// + /// This returns NaN when *either* argument is NaN or if a combination of + /// +inf and -inf is provided as arguments. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(num_midpoint)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// assert_eq!(1f16.midpoint(4.0), 2.5); + /// assert_eq!((-5.5f16).midpoint(8.0), 1.25); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f16) -> f16 { + const LO: f16 = f16::MIN_POSITIVE * 2.; + const HI: f16 = f16::MAX / 2.; + + let (a, b) = (self, other); + let abs_a = a.abs_private(); + let abs_b = b.abs_private(); + + if abs_a <= HI && abs_b <= HI { + // Overflow is impossible + (a + b) / 2. + } else if abs_a < LO { + // Not safe to halve a + a + (b / 2.) + } else if abs_b < LO { + // Not safe to halve b + (a / 2.) + b + } else { + // Not safe to halve a and b + (a / 2.) + (b / 2.) + } + } + /// Rounds toward zero and converts to any primitive integer type, /// assuming that the value is finite and fits in that type. /// diff --git a/library/std/src/f128/tests.rs b/library/std/src/f128/tests.rs index df806a639f620..7051c051bf723 100644 --- a/library/std/src/f128/tests.rs +++ b/library/std/src/f128/tests.rs @@ -56,7 +56,33 @@ fn test_num_f128() { test_num(10f128, 2f128); } -// FIXME(f16_f128): add min and max tests when available +#[test] +#[cfg(reliable_f128_math)] +fn test_min_nan() { + assert_eq!(f128::NAN.min(2.0), 2.0); + assert_eq!(2.0f128.min(f128::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_max_nan() { + assert_eq!(f128::NAN.max(2.0), 2.0); + assert_eq!(2.0f128.max(f128::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_minimum() { + assert!(f128::NAN.minimum(2.0).is_nan()); + assert!(2.0f128.minimum(f128::NAN).is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_maximum() { + assert!(f128::NAN.maximum(2.0).is_nan()); + assert!(2.0f128.maximum(f128::NAN).is_nan()); +} #[test] fn test_nan() { diff --git a/library/std/src/f16/tests.rs b/library/std/src/f16/tests.rs index f0ef807dac141..50504e7ffd94f 100644 --- a/library/std/src/f16/tests.rs +++ b/library/std/src/f16/tests.rs @@ -57,7 +57,33 @@ fn test_num_f16() { test_num(10f16, 2f16); } -// FIXME(f16_f128): add min and max tests when available +#[test] +#[cfg(reliable_f16_math)] +fn test_min_nan() { + assert_eq!(f16::NAN.min(2.0), 2.0); + assert_eq!(2.0f16.min(f16::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_max_nan() { + assert_eq!(f16::NAN.max(2.0), 2.0); + assert_eq!(2.0f16.max(f16::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_minimum() { + assert!(f16::NAN.minimum(2.0).is_nan()); + assert!(2.0f16.minimum(f16::NAN).is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_maximum() { + assert!(f16::NAN.maximum(2.0).is_nan()); + assert!(2.0f16.maximum(f16::NAN).is_nan()); +} #[test] fn test_nan() { From e1d23c9fa2714177196ca3e43f751d4cdc634915 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 17 Jul 2024 20:45:13 -0400 Subject: [PATCH 17/18] Update comments for `{f16, f32, f64, f128}::midpoint` Clarify what makes some operations not safe, and correct comment in the default branch ("not safe" -> "safe"). --- library/core/src/num/f128.rs | 6 +++--- library/core/src/num/f16.rs | 6 +++--- library/core/src/num/f32.rs | 6 +++--- library/core/src/num/f64.rs | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index cfe3a20df532e..0c04f47fe7df1 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -851,13 +851,13 @@ impl f128 { // Overflow is impossible (a + b) / 2. } else if abs_a < LO { - // Not safe to halve a + // Not safe to halve `a` (would underflow) a + (b / 2.) } else if abs_b < LO { - // Not safe to halve b + // Not safe to halve `b` (would underflow) (a / 2.) + b } else { - // Not safe to halve a and b + // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 8947bf0d84826..e5b1148e19215 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -880,13 +880,13 @@ impl f16 { // Overflow is impossible (a + b) / 2. } else if abs_a < LO { - // Not safe to halve a + // Not safe to halve `a` (would underflow) a + (b / 2.) } else if abs_b < LO { - // Not safe to halve b + // Not safe to halve `b` (would underflow) (a / 2.) + b } else { - // Not safe to halve a and b + // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 08d863f17caf7..e65c982b17227 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1070,13 +1070,13 @@ impl f32 { // Overflow is impossible (a + b) / 2. } else if abs_a < LO { - // Not safe to halve a + // Not safe to halve `a` (would underflow) a + (b / 2.) } else if abs_b < LO { - // Not safe to halve b + // Not safe to halve `b` (would underflow) (a / 2.) + b } else { - // Not safe to halve a and b + // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 5d33eea6d011f..b27d47b07d544 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1064,13 +1064,13 @@ impl f64 { // Overflow is impossible (a + b) / 2. } else if abs_a < LO { - // Not safe to halve a + // Not safe to halve `a` (would underflow) a + (b / 2.) } else if abs_b < LO { - // Not safe to halve b + // Not safe to halve `b` (would underflow) (a / 2.) + b } else { - // Not safe to halve a and b + // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } From d64bbb127ac920d6058d8f308d0c6670ff07dac7 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 30 Jul 2024 18:11:46 -0400 Subject: [PATCH 18/18] Add a disclaimer about x86 `f128` math functions Due to a LLVM bug, `f128` math functions link successfully but LLVM chooses the wrong symbols (`long double` symbols rather than those for binary128). Since this is a notable problem that may surprise a number of users, add a note about it. Link: https://github.com/llvm/llvm-project/issues/44744 --- library/core/src/primitive_docs.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 5989bcbcc5201..09ebef89fb0c2 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1244,6 +1244,9 @@ mod prim_f64 {} /// actually implement it. For x86-64 and AArch64, ISA support is not even specified, /// so it will always be a software implementation significantly slower than `f64`. /// +/// _Note: `f128` support is incomplete. Many platforms will not be able to link math functions. On +/// x86 in particular, these functions do link but their results are always incorrect._ +/// /// *[See also the `std::f128::consts` module](crate::f128::consts).* /// /// [wikipedia]: https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format