From f83ecd82701923c38f847d290a4e0859d8cf0126 Mon Sep 17 00:00:00 2001 From: Mu001999 Date: Fri, 23 May 2025 03:51:15 +0800 Subject: [PATCH 01/17] Refactor the two-phase check for impls and impl items --- compiler/rustc_middle/src/query/mod.rs | 2 +- compiler/rustc_passes/src/dead.rs | 250 +++++++++--------- tests/ui/derives/clone-debug-dead-code.stderr | 2 +- tests/ui/deriving/deriving-in-macro.rs | 3 +- tests/ui/lint/dead-code/issue-41883.stderr | 2 + ...tiple-dead-codes-in-the-same-struct.stderr | 2 + 6 files changed, 128 insertions(+), 133 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 30245bc82d427..279033ee0724c 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1137,7 +1137,7 @@ rustc_queries! { /// their respective impl (i.e., part of the derive macro) query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx ( LocalDefIdSet, - LocalDefIdMap> + LocalDefIdMap> ) { arena_cache desc { "finding live symbols in crate" } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 6e5357d800743..f83c7471770ab 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -8,12 +8,13 @@ use std::mem; use hir::ItemKind; use hir::def_id::{LocalDefIdMap, LocalDefIdSet}; use rustc_abi::FieldIdx; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::unord::UnordSet; use rustc_errors::MultiSpan; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Node, PatKind, TyKind}; +use rustc_hir::{self as hir, Node, PatKind, QPath, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; @@ -44,15 +45,20 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { ) } -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) - { - tcx.visibility(def_id).is_public() - } else { - true +/// Returns the local def id of the ADT if the given ty refers to a local one. +fn local_adt_def_of_ty<'tcx>(ty: &hir::Ty<'tcx>) -> Option { + match ty.kind { + TyKind::Path(QPath::Resolved(_, path)) => { + if 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) + { + Some(local_def_id) + } else { + None + } + } + _ => None, } } @@ -78,7 +84,7 @@ struct MarkSymbolVisitor<'tcx> { // maps from ADTs to ignored derived traits (e.g. Debug and Clone) // and the span of their respective impl (i.e., part of the derive // macro) - ignored_derived_traits: LocalDefIdMap>, + ignored_derived_traits: LocalDefIdMap>, } impl<'tcx> MarkSymbolVisitor<'tcx> { @@ -360,7 +366,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && 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)) = + && let TyKind::Path(QPath::Resolved(_, path)) = self.tcx.hir_expect_item(local_impl_of).expect_impl().self_ty.kind && let Res::Def(def_kind, did) = path.res { @@ -388,7 +394,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { self.ignored_derived_traits .entry(adt_def_id) .or_default() - .push((trait_of, impl_of)); + .insert((trait_of, impl_of)); } return true; } @@ -420,51 +426,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_item(self, item) } hir::ItemKind::ForeignMod { .. } => {} - hir::ItemKind::Trait(..) => { - for &impl_def_id in self.tcx.local_trait_impls(item.owner_id.def_id) { - if let ItemKind::Impl(impl_ref) = self.tcx.hir_expect_item(impl_def_id).kind - { - // skip items - // mark dependent traits live - intravisit::walk_generics(self, impl_ref.generics); - // mark dependent parameters live - intravisit::walk_path(self, impl_ref.of_trait.unwrap().path); + hir::ItemKind::Trait(.., trait_item_refs) => { + // mark assoc ty live if the trait is live + for trait_item in trait_item_refs { + if matches!(trait_item.kind, hir::AssocItemKind::Type) { + self.check_def_id(trait_item.id.owner_id.to_def_id()); } } - intravisit::walk_item(self, item) } _ => intravisit::walk_item(self, item), }, Node::TraitItem(trait_item) => { - // mark corresponding ImplTerm live + // mark the trait live let trait_item_id = trait_item.owner_id.to_def_id(); if let Some(trait_id) = self.tcx.trait_of_item(trait_item_id) { - // mark the trait live self.check_def_id(trait_id); - - for impl_id in self.tcx.all_impls(trait_id) { - if let Some(local_impl_id) = impl_id.as_local() - && let ItemKind::Impl(impl_ref) = - self.tcx.hir_expect_item(local_impl_id).kind - { - if !matches!(trait_item.kind, hir::TraitItemKind::Type(..)) - && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) - { - // skip methods of private ty, - // they would be solved in `solve_rest_impl_items` - continue; - } - - // mark self_ty live - intravisit::walk_unambig_ty(self, impl_ref.self_ty); - if let Some(&impl_item_id) = - self.tcx.impl_item_implementor_ids(impl_id).get(&trait_item_id) - { - self.check_def_id(impl_item_id); - } - } - } } intravisit::walk_trait_item(self, trait_item); } @@ -508,48 +485,58 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } } - fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) { - let mut ready; - (ready, unsolved_impl_items) = - unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| { - self.impl_item_with_used_self(impl_id, impl_item_id) - }); - - while !ready.is_empty() { - self.worklist = - ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect(); - self.mark_live_symbols(); - - (ready, unsolved_impl_items) = - unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| { - self.impl_item_with_used_self(impl_id, impl_item_id) - }); + /// Returns whether `local_def_id` is potentially alive or not. + /// `local_def_id` points to an impl or an impl item, + /// both impl and impl item that may be passed to this function are of a trait, + /// and added into the unsolved_items during `create_and_seed_worklist` + fn check_impl_or_impl_item_live( + &mut self, + impl_id: hir::ItemId, + local_def_id: LocalDefId, + ) -> bool { + if self.should_ignore_item(local_def_id.to_def_id()) { + return false; } - } - fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool { - 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 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); + let trait_def_id = match self.tcx.def_kind(local_def_id) { + // assoc impl items of traits are live if the corresponding trait items are live + DefKind::AssocFn => self.tcx.associated_item(local_def_id).trait_item_def_id, + // impl items are live if the corresponding traits are live + DefKind::Impl { of_trait: true } => self + .tcx + .impl_trait_ref(impl_id.owner_id.def_id) + .and_then(|trait_ref| Some(trait_ref.skip_binder().def_id)), + _ => None, + }; + + if let Some(trait_def_id) = trait_def_id { + if let Some(trait_def_id) = trait_def_id.as_local() + && !self.live_symbols.contains(&trait_def_id) + { + return false; } - 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() + // FIXME: legacy logic to check whether the function may construct `Self`, + // this can be removed after supporting marking ADTs appearing in patterns + // as live, then we can check private impls of public traits directly + if 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) + && self.tcx.visibility(trait_def_id).is_public() { - // 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 - return self.live_symbols.contains(&local_id) - && self.live_symbols.contains(&local_def_id); + return true; } } - false + + // The impl or impl item is used if the corresponding trait or trait item is used and the ty is used. + if let Some(local_def_id) = + local_adt_def_of_ty(self.tcx.hir_item(impl_id).expect_impl().self_ty) + && !self.live_symbols.contains(&local_def_id) + { + return false; + } + + true } } @@ -584,7 +571,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { match expr.kind { - hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => { + hir::ExprKind::Path(ref qpath @ QPath::TypeRelative(..)) => { let res = self.typeck_results().qpath_res(qpath, expr.hir_id); self.handle_res(res); } @@ -738,7 +725,7 @@ fn check_item<'tcx>( tcx: TyCtxt<'tcx>, worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>, struct_constructors: &mut LocalDefIdMap, - unsolved_impl_items: &mut Vec<(hir::ItemId, LocalDefId)>, + unsolved_items: &mut Vec<(hir::ItemId, LocalDefId)>, id: hir::ItemId, ) { let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id); @@ -764,41 +751,33 @@ fn check_item<'tcx>( } } DefKind::Impl { of_trait } => { - // get DefIds from another query - let local_def_ids = tcx - .associated_item_def_ids(id.owner_id) - .iter() - .filter_map(|def_id| def_id.as_local()); - - 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 { - // check the function may construct Self - let mut may_construct_self = false; - if let Some(fn_sig) = - tcx.hir_fn_sig_by_hir_id(tcx.local_def_id_to_hir_id(local_def_id)) - { - may_construct_self = - matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None); - } + if let Some(comes_from_allow) = + has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id) + { + worklist.push((id.owner_id.def_id, comes_from_allow)); + } else if of_trait { + unsolved_items.push((id, id.owner_id.def_id)); + } - // 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::AssocFn) - || tcx.visibility(local_def_id).is_public() - && (ty_is_pub || may_construct_self)) - { - 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) + for def_id in tcx.associated_item_def_ids(id.owner_id) { + let local_def_id = def_id.expect_local(); + + 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 { - // private method || public method not constructs self - unsolved_impl_items.push((id, local_def_id)); + // FIXME: This condition can be removed + // if we support dead check for assoc consts and tys. + if !matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) { + worklist.push((local_def_id, ComesFromAllowExpect::No)); + } else { + // We only care about associated items of traits, + // because they cannot be visited directly, + // so we later mark them as live if their corresponding traits + // or trait items and self types are both live, + // but inherent associated items can be visited and marked directly. + unsolved_items.push((id, local_def_id)); + } } } } @@ -892,8 +871,8 @@ fn create_and_seed_worklist( fn live_symbols_and_ignored_derived_traits( tcx: TyCtxt<'_>, (): (), -) -> (LocalDefIdSet, LocalDefIdMap>) { - let (worklist, struct_constructors, unsolved_impl_items) = create_and_seed_worklist(tcx); +) -> (LocalDefIdSet, LocalDefIdMap>) { + let (worklist, struct_constructors, mut unsolved_items) = create_and_seed_worklist(tcx); let mut symbol_visitor = MarkSymbolVisitor { worklist, tcx, @@ -907,7 +886,22 @@ fn live_symbols_and_ignored_derived_traits( ignored_derived_traits: Default::default(), }; symbol_visitor.mark_live_symbols(); - symbol_visitor.solve_rest_impl_items(unsolved_impl_items); + let mut items_to_check; + (items_to_check, unsolved_items) = + unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| { + symbol_visitor.check_impl_or_impl_item_live(impl_id, local_def_id) + }); + + while !items_to_check.is_empty() { + symbol_visitor.worklist = + items_to_check.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect(); + symbol_visitor.mark_live_symbols(); + + (items_to_check, unsolved_items) = + unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| { + symbol_visitor.check_impl_or_impl_item_live(impl_id, local_def_id) + }); + } (symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits) } @@ -921,7 +915,7 @@ struct DeadItem { struct DeadVisitor<'tcx> { tcx: TyCtxt<'tcx>, live_symbols: &'tcx LocalDefIdSet, - ignored_derived_traits: &'tcx LocalDefIdMap>, + ignored_derived_traits: &'tcx LocalDefIdMap>, } enum ShouldWarnAboutField { @@ -1188,19 +1182,15 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { let def_kind = tcx.def_kind(item.owner_id); let mut dead_codes = Vec::new(); - // if we have diagnosed the trait, do not diagnose unused methods - if matches!(def_kind, DefKind::Impl { .. }) + // Only diagnose unused assoc items in inherient impl and used trait, + // for unused assoc items in impls of trait, + // we have diagnosed them in the trait if they are unused, + // for unused assoc items in unused trait, + // we have diagnosed the unused trait. + if matches!(def_kind, DefKind::Impl { of_trait: false }) || (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 methods in traits - if matches!(def_kind, DefKind::Impl { of_trait: true }) - && tcx.def_kind(def_id) == DefKind::AssocFn - || def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn - { - continue; - } - if let Some(local_def_id) = def_id.as_local() && !visitor.is_live_code(local_def_id) { diff --git a/tests/ui/derives/clone-debug-dead-code.stderr b/tests/ui/derives/clone-debug-dead-code.stderr index 38be486e33207..34b7f929ec5ec 100644 --- a/tests/ui/derives/clone-debug-dead-code.stderr +++ b/tests/ui/derives/clone-debug-dead-code.stderr @@ -40,7 +40,7 @@ LL | struct D { f: () } | | | field in this struct | - = note: `D` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis + = note: `D` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis error: field `f` is never read --> $DIR/clone-debug-dead-code.rs:21:12 diff --git a/tests/ui/deriving/deriving-in-macro.rs b/tests/ui/deriving/deriving-in-macro.rs index 493c1415c7fa2..739d9b3068226 100644 --- a/tests/ui/deriving/deriving-in-macro.rs +++ b/tests/ui/deriving/deriving-in-macro.rs @@ -1,5 +1,6 @@ -//@ run-pass +//@ check-pass #![allow(non_camel_case_types)] +#![allow(dead_code)] macro_rules! define_vec { () => ( diff --git a/tests/ui/lint/dead-code/issue-41883.stderr b/tests/ui/lint/dead-code/issue-41883.stderr index 47ccef9a53069..cf079e4dda33a 100644 --- a/tests/ui/lint/dead-code/issue-41883.stderr +++ b/tests/ui/lint/dead-code/issue-41883.stderr @@ -29,6 +29,8 @@ error: struct `UnusedStruct` is never constructed | LL | struct UnusedStruct; | ^^^^^^^^^^^^ + | + = note: `UnusedStruct` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis error: aborting due to 4 previous errors diff --git a/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr b/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr index 25a7d96cb8978..b992005318f2e 100644 --- a/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr +++ b/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr @@ -56,6 +56,8 @@ warning: struct `Foo` is never constructed | LL | struct Foo(usize, #[allow(unused)] usize); | ^^^ + | + = note: `Foo` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis error: aborting due to 2 previous errors; 2 warnings emitted From 65bdb31a97af553c4fd932a171b74eaad76c1c53 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Wed, 14 May 2025 10:43:39 +0000 Subject: [PATCH 02/17] Report text_direction_codepoint_in_literal when parsing - The lint is now reported in code that gets removed/modified/duplicated by macro expansion. - Spans are more accurate - Fixes #140281 --- compiler/rustc_lint/src/early/diagnostics.rs | 21 +++ .../src/hidden_unicode_codepoints.rs | 136 ------------------ compiler/rustc_lint/src/lib.rs | 3 - compiler/rustc_lint_defs/src/builtin.rs | 36 ++++- compiler/rustc_lint_defs/src/lib.rs | 8 ++ compiler/rustc_parse/src/lexer/mod.rs | 89 +++++++++++- .../parser/macro/auxiliary/unicode-control.rs | 19 +++ .../unicode-control-codepoints-macros.rs | 49 +++++++ .../unicode-control-codepoints-macros.stderr | 57 ++++++++ tests/ui/parser/unicode-control-codepoints.rs | 2 +- .../parser/unicode-control-codepoints.stderr | 34 ++--- 11 files changed, 294 insertions(+), 160 deletions(-) delete mode 100644 compiler/rustc_lint/src/hidden_unicode_codepoints.rs create mode 100644 tests/ui/parser/macro/auxiliary/unicode-control.rs create mode 100644 tests/ui/parser/macro/unicode-control-codepoints-macros.rs create mode 100644 tests/ui/parser/macro/unicode-control-codepoints-macros.stderr diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 40ca9e05d95d6..3acbe3ea61b84 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -187,6 +187,27 @@ pub(super) fn decorate_lint( lints::ReservedMultihash { suggestion }.decorate_lint(diag); } } + BuiltinLintDiag::HiddenUnicodeCodepoints { + label, + count, + span_label, + labels, + escape, + spans, + } => { + lints::HiddenUnicodeCodepointsDiag { + label: &label, + count, + span_label, + labels: labels.map(|spans| lints::HiddenUnicodeCodepointsDiagLabels { spans }), + sub: if escape { + lints::HiddenUnicodeCodepointsDiagSub::Escape { spans } + } else { + lints::HiddenUnicodeCodepointsDiagSub::NoEscape { spans } + }, + } + .decorate_lint(diag); + } BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => { lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag); } diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs deleted file mode 100644 index 491c2826baaa1..0000000000000 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ /dev/null @@ -1,136 +0,0 @@ -use ast::util::unicode::{TEXT_FLOW_CONTROL_CHARS, contains_text_flow_control_chars}; -use rustc_ast as ast; -use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::{BytePos, Span, Symbol}; - -use crate::lints::{ - HiddenUnicodeCodepointsDiag, HiddenUnicodeCodepointsDiagLabels, HiddenUnicodeCodepointsDiagSub, -}; -use crate::{EarlyContext, EarlyLintPass, LintContext}; - -declare_lint! { - #[allow(text_direction_codepoint_in_literal)] - /// The `text_direction_codepoint_in_literal` lint detects Unicode codepoints that change the - /// visual representation of text on screen in a way that does not correspond to their on - /// memory representation. - /// - /// ### Explanation - /// - /// The unicode characters `\u{202A}`, `\u{202B}`, `\u{202D}`, `\u{202E}`, `\u{2066}`, - /// `\u{2067}`, `\u{2068}`, `\u{202C}` and `\u{2069}` make the flow of text on screen change - /// its direction on software that supports these codepoints. This makes the text "abc" display - /// as "cba" on screen. By leveraging software that supports these, people can write specially - /// crafted literals that make the surrounding code seem like it's performing one action, when - /// in reality it is performing another. Because of this, we proactively lint against their - /// presence to avoid surprises. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![deny(text_direction_codepoint_in_literal)] - /// fn main() { - /// println!("{:?}", '‮'); - /// } - /// ``` - /// - /// {{produces}} - /// - pub TEXT_DIRECTION_CODEPOINT_IN_LITERAL, - Deny, - "detect special Unicode codepoints that affect the visual representation of text on screen, \ - changing the direction in which text flows", -} - -declare_lint_pass!(HiddenUnicodeCodepoints => [TEXT_DIRECTION_CODEPOINT_IN_LITERAL]); - -impl HiddenUnicodeCodepoints { - fn lint_text_direction_codepoint( - &self, - cx: &EarlyContext<'_>, - text: Symbol, - span: Span, - padding: u32, - point_at_inner_spans: bool, - label: &str, - ) { - // Obtain the `Span`s for each of the forbidden chars. - let spans: Vec<_> = text - .as_str() - .char_indices() - .filter_map(|(i, c)| { - TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { - let lo = span.lo() + BytePos(i as u32 + padding); - (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) - }) - }) - .collect(); - - let count = spans.len(); - let labels = point_at_inner_spans - .then_some(HiddenUnicodeCodepointsDiagLabels { spans: spans.clone() }); - let sub = if point_at_inner_spans && !spans.is_empty() { - HiddenUnicodeCodepointsDiagSub::Escape { spans } - } else { - HiddenUnicodeCodepointsDiagSub::NoEscape { spans } - }; - - cx.emit_span_lint( - TEXT_DIRECTION_CODEPOINT_IN_LITERAL, - span, - HiddenUnicodeCodepointsDiag { label, count, span_label: span, labels, sub }, - ); - } - - fn check_literal( - &mut self, - cx: &EarlyContext<'_>, - text: Symbol, - lit_kind: ast::token::LitKind, - span: Span, - label: &'static str, - ) { - if !contains_text_flow_control_chars(text.as_str()) { - return; - } - let (padding, point_at_inner_spans) = match lit_kind { - // account for `"` or `'` - ast::token::LitKind::Str | ast::token::LitKind::Char => (1, true), - // account for `c"` - ast::token::LitKind::CStr => (2, true), - // account for `r###"` - ast::token::LitKind::StrRaw(n) => (n as u32 + 2, true), - // account for `cr###"` - ast::token::LitKind::CStrRaw(n) => (n as u32 + 3, true), - // suppress bad literals. - ast::token::LitKind::Err(_) => return, - // Be conservative just in case new literals do support these. - _ => (0, false), - }; - self.lint_text_direction_codepoint(cx, text, span, padding, point_at_inner_spans, label); - } -} - -impl EarlyLintPass for HiddenUnicodeCodepoints { - fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { - if let ast::AttrKind::DocComment(_, comment) = attr.kind { - if contains_text_flow_control_chars(comment.as_str()) { - self.lint_text_direction_codepoint(cx, comment, attr.span, 0, false, "doc comment"); - } - } - } - - #[inline] - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - // byte strings are already handled well enough by `EscapeError::NonAsciiCharInByteString` - match &expr.kind { - ast::ExprKind::Lit(token_lit) => { - self.check_literal(cx, token_lit.symbol, token_lit.kind, expr.span, "literal"); - } - ast::ExprKind::FormatArgs(args) => { - let (lit_kind, text) = args.uncooked_fmt_str; - self.check_literal(cx, text, lit_kind, args.span, "format string"); - } - _ => {} - }; - } -} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 4ff586a79a6ec..97095aa24f4fc 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -48,7 +48,6 @@ mod errors; mod expect; mod for_loops_over_fallibles; mod foreign_modules; -pub mod hidden_unicode_codepoints; mod if_let_rescope; mod impl_trait_overcaptures; mod internal; @@ -91,7 +90,6 @@ use deref_into_dyn_supertrait::*; use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; -use hidden_unicode_codepoints::*; use if_let_rescope::IfLetRescope; use impl_trait_overcaptures::ImplTraitOvercaptures; use internal::*; @@ -174,7 +172,6 @@ early_lint_methods!( DeprecatedAttr: DeprecatedAttr::default(), WhileTrue: WhileTrue, NonAsciiIdents: NonAsciiIdents, - HiddenUnicodeCodepoints: HiddenUnicodeCodepoints, IncompleteInternalFeatures: IncompleteInternalFeatures, RedundantSemicolons: RedundantSemicolons, UnusedDocComment: UnusedDocComment, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index b8d242bad86ac..9b97e66e3f849 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -7,6 +7,8 @@ //! When removing a lint, make sure to also add a call to `register_removed` in //! compiler/rustc_lint/src/lib.rs. +#![allow(text_direction_codepoint_in_literal)] + use rustc_span::edition::Edition; use crate::{FutureIncompatibilityReason, declare_lint, declare_lint_pass}; @@ -104,6 +106,7 @@ declare_lint_pass! { TAIL_EXPR_DROP_ORDER, TEST_UNSTABLE_LINT, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, + TEXT_DIRECTION_CODEPOINT_IN_LITERAL, TRIVIAL_CASTS, TRIVIAL_NUMERIC_CASTS, TYVAR_BEHIND_RAW_POINTER, @@ -3784,7 +3787,6 @@ declare_lint! { } declare_lint! { - #[allow(text_direction_codepoint_in_literal)] /// The `text_direction_codepoint_in_comment` lint detects Unicode codepoints in comments that /// change the visual representation of text on screen in a way that does not correspond to /// their on memory representation. @@ -3812,6 +3814,38 @@ declare_lint! { "invisible directionality-changing codepoints in comment" } +declare_lint! { + /// The `text_direction_codepoint_in_literal` lint detects Unicode codepoints that change the + /// visual representation of text on screen in a way that does not correspond to their on + /// memory representation. + /// + /// ### Explanation + /// + /// The unicode characters `\u{202A}`, `\u{202B}`, `\u{202D}`, `\u{202E}`, `\u{2066}`, + /// `\u{2067}`, `\u{2068}`, `\u{202C}` and `\u{2069}` make the flow of text on screen change + /// its direction on software that supports these codepoints. This makes the text "abc" display + /// as "cba" on screen. By leveraging software that supports these, people can write specially + /// crafted literals that make the surrounding code seem like it's performing one action, when + /// in reality it is performing another. Because of this, we proactively lint against their + /// presence to avoid surprises. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(text_direction_codepoint_in_literal)] + /// fn main() { + /// println!("{:?}", '‮'); + /// } + /// ``` + /// + /// {{produces}} + /// + pub TEXT_DIRECTION_CODEPOINT_IN_LITERAL, + Deny, + "detect special Unicode codepoints that affect the visual representation of text on screen, \ + changing the direction in which text flows", +} + declare_lint! { /// The `duplicate_macro_attributes` lint detects when a `#[test]`-like built-in macro /// attribute is duplicated on an item. This lint may trigger on `bench`, `cfg_eval`, `test` diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index b4069b317bfa1..34297dcb2f521 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -698,6 +698,14 @@ pub enum BuiltinLintDiag { is_string: bool, suggestion: Span, }, + HiddenUnicodeCodepoints { + label: String, + count: usize, + span_label: Span, + labels: Option>, + escape: bool, + spans: Vec<(char, Span)>, + }, TrailingMacro(bool, Ident), BreakWithLabelAndLoop(Span), UnicodeTextFlow(Span, String), diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 78c5742414b81..2845bbed1c0ee 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -4,7 +4,7 @@ use diagnostics::make_unclosed_delims_error; use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::util::unicode::contains_text_flow_control_chars; +use rustc_ast::util::unicode::{TEXT_FLOW_CONTROL_CHARS, contains_text_flow_control_chars}; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, StashKey}; use rustc_lexer::{ @@ -14,7 +14,7 @@ use rustc_literal_escaper::{EscapeError, Mode, unescape_mixed, unescape_unicode} use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX, - TEXT_DIRECTION_CODEPOINT_IN_COMMENT, + TEXT_DIRECTION_CODEPOINT_IN_COMMENT, TEXT_DIRECTION_CODEPOINT_IN_LITERAL, }; use rustc_session::parse::ParseSess; use rustc_span::{BytePos, Pos, Span, Symbol, sym}; @@ -174,6 +174,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // Opening delimiter of the length 3 is not included into the symbol. let content_start = start + BytePos(3); let content = self.str_from(content_start); + self.lint_doc_comment_unicode_text_flow(start, content); self.cook_doc_comment(content_start, content, CommentKind::Line, doc_style) } rustc_lexer::TokenKind::BlockComment { doc_style, terminated } => { @@ -193,6 +194,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let content_start = start + BytePos(3); let content_end = self.pos - BytePos(if terminated { 2 } else { 0 }); let content = self.str_from_to(content_start, content_end); + self.lint_doc_comment_unicode_text_flow(start, content); self.cook_doc_comment(content_start, content, CommentKind::Block, doc_style) } rustc_lexer::TokenKind::Frontmatter { has_invalid_preceding_whitespace, invalid_infostring } => { @@ -287,6 +289,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } else { None }; + self.lint_literal_unicode_text_flow(symbol, kind, self.mk_sp(start, self.pos), "literal"); token::Literal(token::Lit { kind, symbol, suffix }) } rustc_lexer::TokenKind::Lifetime { starts_with_number } => { @@ -481,6 +484,88 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } } + fn lint_doc_comment_unicode_text_flow(&mut self, start: BytePos, content: &str) { + if contains_text_flow_control_chars(content) { + self.report_text_direction_codepoint( + content, + self.mk_sp(start, self.pos), + 0, + false, + "doc comment", + ); + } + } + + fn lint_literal_unicode_text_flow( + &mut self, + text: Symbol, + lit_kind: token::LitKind, + span: Span, + label: &'static str, + ) { + if !contains_text_flow_control_chars(text.as_str()) { + return; + } + let (padding, point_at_inner_spans) = match lit_kind { + // account for `"` or `'` + token::LitKind::Str | token::LitKind::Char => (1, true), + // account for `c"` + token::LitKind::CStr => (2, true), + // account for `r###"` + token::LitKind::StrRaw(n) => (n as u32 + 2, true), + // account for `cr###"` + token::LitKind::CStrRaw(n) => (n as u32 + 3, true), + // suppress bad literals. + token::LitKind::Err(_) => return, + // Be conservative just in case new literals do support these. + _ => (0, false), + }; + self.report_text_direction_codepoint( + text.as_str(), + span, + padding, + point_at_inner_spans, + label, + ); + } + + fn report_text_direction_codepoint( + &self, + text: &str, + span: Span, + padding: u32, + point_at_inner_spans: bool, + label: &str, + ) { + // Obtain the `Span`s for each of the forbidden chars. + let spans: Vec<_> = text + .char_indices() + .filter_map(|(i, c)| { + TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { + let lo = span.lo() + BytePos(i as u32 + padding); + (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) + }) + }) + .collect(); + + let count = spans.len(); + let labels = point_at_inner_spans.then_some(spans.clone()); + + self.psess.buffer_lint( + TEXT_DIRECTION_CODEPOINT_IN_LITERAL, + span, + ast::CRATE_NODE_ID, + BuiltinLintDiag::HiddenUnicodeCodepoints { + label: label.to_string(), + count, + span_label: span, + labels, + escape: point_at_inner_spans && !spans.is_empty(), + spans, + }, + ); + } + fn validate_frontmatter( &self, start: BytePos, diff --git a/tests/ui/parser/macro/auxiliary/unicode-control.rs b/tests/ui/parser/macro/auxiliary/unicode-control.rs new file mode 100644 index 0000000000000..8e73e3985ce9a --- /dev/null +++ b/tests/ui/parser/macro/auxiliary/unicode-control.rs @@ -0,0 +1,19 @@ +#![allow(text_direction_codepoint_in_literal)] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro] +pub fn create_rtl_in_string(_: TokenStream) -> TokenStream { + r#""‮test⁦ RTL in string literal""#.parse().unwrap() +} + +#[proc_macro] +pub fn forward_stream(s: TokenStream) -> TokenStream { + s +} + +#[proc_macro] +pub fn recollect_stream(s: TokenStream) -> TokenStream { + s.into_iter().collect() +} diff --git a/tests/ui/parser/macro/unicode-control-codepoints-macros.rs b/tests/ui/parser/macro/unicode-control-codepoints-macros.rs new file mode 100644 index 0000000000000..775c50779760a --- /dev/null +++ b/tests/ui/parser/macro/unicode-control-codepoints-macros.rs @@ -0,0 +1,49 @@ +// Regression test for #140281 +//@ edition: 2021 +//@ proc-macro: unicode-control.rs + +extern crate unicode_control; +use unicode_control::*; + +macro_rules! foo { + ($x:expr) => { + $x + }; +} + +macro_rules! empty { + ($x:expr) => {}; +} + +fn main() { + let t = vec![ + /// ‮test⁦ RTL in doc in vec + //~^ ERROR unicode codepoint changing visible direction of text present in doc comment + 1 + ]; + foo!( + /** + * ‮test⁦ RTL in doc in macro + */ + //~^^^ ERROR unicode codepoint changing visible direction of text present in doc comment + 1 + ); + empty!( + /** + * ‮test⁦ RTL in doc in macro + */ + //~^^^ ERROR unicode codepoint changing visible direction of text present in doc comment + 1 + ); + let x = create_rtl_in_string!(); // OK + forward_stream!( + /// ‮test⁦ RTL in doc in proc macro + //~^ ERROR unicode codepoint changing visible direction of text present in doc comment + mod a {} + ); + recollect_stream!( + /// ‮test⁦ RTL in doc in proc macro + //~^ ERROR unicode codepoint changing visible direction of text present in doc comment + mod b {} + ); +} diff --git a/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr b/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr new file mode 100644 index 0000000000000..ca813399eac27 --- /dev/null +++ b/tests/ui/parser/macro/unicode-control-codepoints-macros.stderr @@ -0,0 +1,57 @@ +error: unicode codepoint changing visible direction of text present in doc comment + --> $DIR/unicode-control-codepoints-macros.rs:20:9 + | +LL | /// �test� RTL in doc in vec + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this doc comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = note: if their presence wasn't intentional, you can remove them + = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + = note: `#[deny(text_direction_codepoint_in_literal)]` on by default + +error: unicode codepoint changing visible direction of text present in doc comment + --> $DIR/unicode-control-codepoints-macros.rs:25:9 + | +LL | / /** +LL | | * �test� RTL in doc in macro +LL | | */ + | |___________^ this doc comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = note: if their presence wasn't intentional, you can remove them + = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + +error: unicode codepoint changing visible direction of text present in doc comment + --> $DIR/unicode-control-codepoints-macros.rs:32:9 + | +LL | / /** +LL | | * �test� RTL in doc in macro +LL | | */ + | |___________^ this doc comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = note: if their presence wasn't intentional, you can remove them + = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + +error: unicode codepoint changing visible direction of text present in doc comment + --> $DIR/unicode-control-codepoints-macros.rs:40:9 + | +LL | /// �test� RTL in doc in proc macro + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this doc comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = note: if their presence wasn't intentional, you can remove them + = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + +error: unicode codepoint changing visible direction of text present in doc comment + --> $DIR/unicode-control-codepoints-macros.rs:45:9 + | +LL | /// �test� RTL in doc in proc macro + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this doc comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = note: if their presence wasn't intentional, you can remove them + = note: if you want to keep them but make them visible in your source code, you can escape them: '\u{202e}', '\u{2066}' + +error: aborting due to 5 previous errors + diff --git a/tests/ui/parser/unicode-control-codepoints.rs b/tests/ui/parser/unicode-control-codepoints.rs index 14e1cfe59d39a..e3c906063c473 100644 --- a/tests/ui/parser/unicode-control-codepoints.rs +++ b/tests/ui/parser/unicode-control-codepoints.rs @@ -34,7 +34,7 @@ fn main() { //~^ ERROR unicode codepoint changing visible direction of text present in literal println!("{{‮}}"); - //~^ ERROR unicode codepoint changing visible direction of text present in format string + //~^ ERROR unicode codepoint changing visible direction of text present in literal } //"/*‮ } ⁦if isAdmin⁩ ⁦ begin admins only */" diff --git a/tests/ui/parser/unicode-control-codepoints.stderr b/tests/ui/parser/unicode-control-codepoints.stderr index 27b95f9ac6115..7978c1435f609 100644 --- a/tests/ui/parser/unicode-control-codepoints.stderr +++ b/tests/ui/parser/unicode-control-codepoints.stderr @@ -100,21 +100,6 @@ LL | // if access_level != "us�e�r" { // Check if admin = note: `#[deny(text_direction_codepoint_in_comment)]` on by default = help: if their presence wasn't intentional, you can remove them -error: unicode codepoint changing visible direction of text present in comment - --> $DIR/unicode-control-codepoints.rs:40:1 - | -LL | //"/*� } �if isAdmin� � begin admins only */" - | ^^^^^-^^^-^^^^^^^^^^-^-^^^^^^^^^^^^^^^^^^^^^^ - | | | | | | - | | | | | '\u{2066}' - | | | | '\u{2069}' - | | | '\u{2066}' - | | '\u{202e}' - | this comment contains invisible unicode text flow control codepoints - | - = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen - = help: if their presence wasn't intentional, you can remove them - error: unicode codepoint changing visible direction of text present in literal --> $DIR/unicode-control-codepoints.rs:13:22 | @@ -207,14 +192,14 @@ LL - let _ = cr#"�"#; LL + let _ = cr#"\u{202e}"#; | -error: unicode codepoint changing visible direction of text present in format string +error: unicode codepoint changing visible direction of text present in literal --> $DIR/unicode-control-codepoints.rs:36:14 | LL | println!("{{�}}"); | ^^^-^^^ | | | | | '\u{202e}' - | this format string contains an invisible unicode text flow control codepoint + | this literal contains an invisible unicode text flow control codepoint | = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen = help: if their presence wasn't intentional, you can remove them @@ -224,6 +209,21 @@ LL - println!("{{�}}"); LL + println!("{{\u{202e}}}"); | +error: unicode codepoint changing visible direction of text present in comment + --> $DIR/unicode-control-codepoints.rs:40:1 + | +LL | //"/*� } �if isAdmin� � begin admins only */" + | ^^^^^-^^^-^^^^^^^^^^-^-^^^^^^^^^^^^^^^^^^^^^^ + | | | | | | + | | | | | '\u{2066}' + | | | | '\u{2069}' + | | | '\u{2066}' + | | '\u{202e}' + | this comment contains invisible unicode text flow control codepoints + | + = note: these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + = help: if their presence wasn't intentional, you can remove them + error: unicode codepoint changing visible direction of text present in doc comment --> $DIR/unicode-control-codepoints.rs:43:1 | From e5bfd02c5e8c9176be0bf279a8669d56abe66a5f Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Wed, 14 May 2025 16:48:05 +0000 Subject: [PATCH 03/17] Avoid including text direction codepoints in lint messages --- Cargo.lock | 1 + compiler/rustc_lint_defs/src/builtin.rs | 8 ++++---- src/tools/lint-docs/Cargo.toml | 1 + src/tools/lint-docs/src/lib.rs | 11 +++++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 177ff6594e244..7b117130683ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2170,6 +2170,7 @@ dependencies = [ name = "lint-docs" version = "0.1.0" dependencies = [ + "rustc-literal-escaper", "serde_json", "tempfile", "walkdir", diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9b97e66e3f849..9c550a12ce119 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -7,8 +7,6 @@ //! When removing a lint, make sure to also add a call to `register_removed` in //! compiler/rustc_lint/src/lib.rs. -#![allow(text_direction_codepoint_in_literal)] - use rustc_span::edition::Edition; use crate::{FutureIncompatibilityReason, declare_lint, declare_lint_pass}; @@ -3796,7 +3794,7 @@ declare_lint! { /// ```rust,compile_fail /// #![deny(text_direction_codepoint_in_comment)] /// fn main() { - /// println!("{:?}"); // '‮'); + #[doc = " println!(\"{:?}\"); // '\u{202E}');"] /// } /// ``` /// @@ -3834,7 +3832,9 @@ declare_lint! { /// ```rust,compile_fail /// #![deny(text_direction_codepoint_in_literal)] /// fn main() { - /// println!("{:?}", '‮'); + // ` - convince tidy that backticks match + #[doc = " println!(\"{:?}\", '\u{202E}');"] + // ` /// } /// ``` /// diff --git a/src/tools/lint-docs/Cargo.toml b/src/tools/lint-docs/Cargo.toml index 3578bda8276e7..f1ffda75ac0f7 100644 --- a/src/tools/lint-docs/Cargo.toml +++ b/src/tools/lint-docs/Cargo.toml @@ -7,6 +7,7 @@ description = "A script to extract the lint documentation for the rustc book." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +rustc-literal-escaper = "0.0.2" serde_json = "1.0.57" tempfile = "3.1.0" walkdir = "2.3.1" diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index cacce01675fe2..6bb18c2bced70 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -4,6 +4,7 @@ use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; +use rustc_literal_escaper::{Mode, unescape_unicode}; use walkdir::WalkDir; mod groups; @@ -214,6 +215,16 @@ impl<'a> LintExtractor<'a> { let line = line.trim(); if let Some(text) = line.strip_prefix("/// ") { doc_lines.push(text.to_string()); + } else if let Some(text) = line.strip_prefix("#[doc = \"") { + let escaped = text.strip_suffix("\"]").unwrap(); + let mut buf = String::new(); + unescape_unicode(escaped, Mode::Str, &mut |_, c| match c { + Ok(c) => buf.push(c), + Err(err) => { + assert!(!err.is_fatal(), "failed to unescape string literal") + } + }); + doc_lines.push(buf); } else if line == "///" { doc_lines.push("".to_string()); } else if line.starts_with("// ") { From f6520673fc28815f5beee5fbd48d4363462fb0f6 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Tue, 27 May 2025 16:03:35 +0000 Subject: [PATCH 04/17] Warn on non-crate level text direction lints --- compiler/rustc_lint_defs/src/builtin.rs | 4 +++- tests/crashes/140281.rs | 18 ------------------ 2 files changed, 3 insertions(+), 19 deletions(-) delete mode 100644 tests/crashes/140281.rs diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9c550a12ce119..56b9891ae0d1f 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3809,7 +3809,8 @@ declare_lint! { /// their use. pub TEXT_DIRECTION_CODEPOINT_IN_COMMENT, Deny, - "invisible directionality-changing codepoints in comment" + "invisible directionality-changing codepoints in comment", + crate_level_only } declare_lint! { @@ -3844,6 +3845,7 @@ declare_lint! { Deny, "detect special Unicode codepoints that affect the visual representation of text on screen, \ changing the direction in which text flows", + crate_level_only } declare_lint! { diff --git a/tests/crashes/140281.rs b/tests/crashes/140281.rs deleted file mode 100644 index 76858cfc74a55..0000000000000 --- a/tests/crashes/140281.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@ known-bug: #140281 - -macro_rules! foo { - ($x:expr) => { $x } -} - -fn main() { - let t = vec![ - /// ‮test⁦ RTL in doc in vec! - // ICE (Sadly) - 1 - ]; - - foo!( - /// ‮test⁦ RTL in doc in macro - 1 - ); -} From 419897c13242001ce3e6642a1bb2db48606f68fb Mon Sep 17 00:00:00 2001 From: Mu001999 Date: Wed, 28 May 2025 22:59:17 +0800 Subject: [PATCH 05/17] Add cfg for FormatShortCmd --- src/bootstrap/src/utils/exec.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index d07300e21d003..64e46f105638f 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -332,16 +332,19 @@ impl Default for CommandOutput { /// Helper trait to format both Command and BootstrapCommand as a short execution line, /// without all the other details (e.g. environment variables). +#[cfg(feature = "tracing")] pub trait FormatShortCmd { fn format_short_cmd(&self) -> String; } +#[cfg(feature = "tracing")] impl FormatShortCmd for BootstrapCommand { fn format_short_cmd(&self) -> String { self.command.format_short_cmd() } } +#[cfg(feature = "tracing")] impl FormatShortCmd for Command { fn format_short_cmd(&self) -> String { let program = Path::new(self.get_program()); From 4794ea176be0d61f3ac08c367971c032e7abe7af Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 24 May 2025 16:35:20 +0200 Subject: [PATCH 06/17] atomic_load intrinsic: use const generic parameter for ordering --- .../src/intrinsics/mod.rs | 3 +- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 93 ++++++++++++------- .../rustc_hir_analysis/src/check/intrinsic.rs | 13 +-- compiler/rustc_middle/src/ty/consts/int.rs | 34 ++++++- compiler/rustc_middle/src/ty/mod.rs | 4 +- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mod.rs | 30 +++++- library/core/src/sync/atomic.rs | 18 ++++ src/tools/miri/src/intrinsics/atomic.rs | 21 ++++- src/tools/miri/src/intrinsics/mod.rs | 2 +- .../unaligned_pointers/atomic_unaligned.rs | 3 +- .../atomic_unaligned.stderr | 4 +- tests/ui/intrinsics/intrinsic-atomics.rs | 6 +- tests/ui/intrinsics/non-integer-atomic.rs | 16 ++-- tests/ui/intrinsics/non-integer-atomic.stderr | 24 ++--- 15 files changed, 198 insertions(+), 74 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index b21ca32c9a2e3..0de23e55e8174 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -870,11 +870,12 @@ fn codegen_regular_intrinsic_call<'tcx>( // FIXME use a compiler fence once Cranelift supports it fx.bcx.ins().fence(); } - _ if intrinsic.as_str().starts_with("atomic_load") => { + sym::atomic_load => { intrinsic_args!(fx, args => (ptr); intrinsic); let ptr = ptr.load_scalar(fx); let ty = generic_args.type_at(0); + let _ord = generic_args.const_at(1).to_value(); // FIXME: forward this to cranelift once they support that match ty.kind() { ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => { // FIXME implement 128bit atomics diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index a6d159c51e132..1047082df42c2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -99,6 +99,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let llret_ty = bx.backend_type(bx.layout_of(ret_ty)); + let ret_llval = |bx: &mut Bx, llval| { + if result.layout.ty.is_bool() { + OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) + .val + .store(bx, result); + } else if !result.layout.ty.is_unit() { + bx.store_to_place(llval, result.val); + } + Ok(()) + }; + let llval = match name { sym::abort => { bx.abort(); @@ -337,6 +348,53 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { use crate::common::AtomicOrdering::*; use crate::common::{AtomicRmwBinOp, SynchronizationScope}; + let invalid_monomorphization = |ty| { + bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { + span, + name, + ty, + }); + }; + + let parse_const_generic_ordering = |ord: ty::Value<'tcx>| { + let discr = ord.valtree.unwrap_branch()[0].unwrap_leaf(); + let ord = discr.to_atomic_ordering(); + // We have to translate from the intrinsic ordering to the backend ordering. + use rustc_middle::ty::AtomicOrdering; + match ord { + AtomicOrdering::Relaxed => Relaxed, + AtomicOrdering::Release => Release, + AtomicOrdering::Acquire => Acquire, + AtomicOrdering::AcqRel => AcquireRelease, + AtomicOrdering::SeqCst => SequentiallyConsistent, + } + }; + + // Some intrinsics have the ordering already converted to a const generic parameter, we handle those first. + match name { + sym::atomic_load => { + let ty = fn_args.type_at(0); + let ordering = fn_args.const_at(1).to_value(); + if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) { + invalid_monomorphization(ty); + return Ok(()); + } + let layout = bx.layout_of(ty); + let source = args[0].immediate(); + let llval = bx.atomic_load( + bx.backend_type(layout), + source, + parse_const_generic_ordering(ordering), + layout.size, + ); + + return ret_llval(bx, llval); + } + + // The rest falls back to below. + _ => {} + } + let Some((instruction, ordering)) = atomic.split_once('_') else { bx.sess().dcx().emit_fatal(errors::MissingMemoryOrdering); }; @@ -350,14 +408,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => bx.sess().dcx().emit_fatal(errors::UnknownAtomicOrdering), }; - let invalid_monomorphization = |ty| { - bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { - span, - name, - ty, - }); - }; - match instruction { "cxchg" | "cxchgweak" => { let Some((success, failure)) = ordering.split_once('_') else { @@ -390,24 +440,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return Ok(()); } - "load" => { - let ty = fn_args.type_at(0); - if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() { - let layout = bx.layout_of(ty); - let size = layout.size; - let source = args[0].immediate(); - bx.atomic_load( - bx.backend_type(layout), - source, - parse_ordering(bx, ordering), - size, - ) - } else { - invalid_monomorphization(ty); - return Ok(()); - } - } - "store" => { let ty = fn_args.type_at(0); if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() { @@ -538,14 +570,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } }; - if result.layout.ty.is_bool() { - OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) - .val - .store(bx, result); - } else if !result.layout.ty.is_unit() { - bx.store_to_place(llval, result.val); - } - Ok(()) + ret_llval(bx, llval) } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 9fd158ad154d8..54bb3ac411304 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -204,24 +204,25 @@ pub(crate) fn check_intrinsic_type( // Each atomic op has variants with different suffixes (`_seq_cst`, `_acquire`, etc.). Use // string ops to strip the suffixes, because the variants all get the same treatment here. - let (n_tps, inputs, output) = match split[1] { + let (n_tps, n_cts, inputs, output) = match split[1] { "cxchg" | "cxchgweak" => ( 1, + 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0), param(0)], Ty::new_tup(tcx, &[param(0), tcx.types.bool]), ), - "load" => (1, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)), - "store" => (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit), + "load" => (1, 1, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)), + "store" => (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit), "xchg" | "xadd" | "xsub" | "and" | "nand" | "or" | "xor" | "max" | "min" | "umax" - | "umin" => (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], param(0)), - "fence" | "singlethreadfence" => (0, Vec::new(), tcx.types.unit), + | "umin" => (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], param(0)), + "fence" | "singlethreadfence" => (0, 0, Vec::new(), tcx.types.unit), op => { tcx.dcx().emit_err(UnrecognizedAtomicOperation { span, op }); return; } }; - (n_tps, 0, 0, inputs, output, hir::Safety::Unsafe) + (n_tps, 0, n_cts, inputs, output, hir::Safety::Unsafe) } else if intrinsic_name == sym::contract_check_ensures { // contract_check_ensures::(Ret, C) -> Ret // where C: for<'a> Fn(&'a Ret) -> bool, diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 9c9cd6953392a..0383814cc96f1 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -26,6 +26,19 @@ impl ConstInt { } } +/// An enum to represent the compiler-side view of `intrinsics::AtomicOrdering`. +/// This lives here because there's a method in this file that needs it and it is entirely unclear +/// where else to put this... +#[derive(Debug)] +pub enum AtomicOrdering { + // These values must match `intrinsics::AtomicOrdering`! + Relaxed = 0, + Release = 1, + Acquire = 2, + AcqRel = 3, + SeqCst = 4, +} + impl std::fmt::Debug for ConstInt { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { int, signed, is_ptr_sized_integral } = *self; @@ -318,6 +331,25 @@ impl ScalarInt { self.to_uint(tcx.data_layout.pointer_size).try_into().unwrap() } + #[inline] + pub fn to_atomic_ordering(self) -> AtomicOrdering { + use AtomicOrdering::*; + let val = self.to_u32(); + if val == Relaxed as u32 { + Relaxed + } else if val == Release as u32 { + Release + } else if val == Acquire as u32 { + Acquire + } else if val == AcqRel as u32 { + AcqRel + } else if val == SeqCst as u32 { + SeqCst + } else { + panic!("not a valid atomic ordering") + } + } + /// Converts the `ScalarInt` to `bool`. /// Panics if the `size` of the `ScalarInt` is not equal to 1 byte. /// Errors if it is not a valid `bool`. @@ -488,7 +520,7 @@ from_scalar_int_for_x_signed!(i8, i16, i32, i64, i128); impl From for ScalarInt { #[inline] fn from(c: std::cmp::Ordering) -> Self { - // Here we rely on `Ordering` having the same values in host and target! + // Here we rely on `cmp::Ordering` having the same values in host and target! ScalarInt::from(c as i8) } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 78c0812b08f80..af31f7ed33b4d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -74,8 +74,8 @@ pub use self::closure::{ place_to_string_for_capture, }; pub use self::consts::{ - AnonConstKind, Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, - ValTree, ValTreeKind, Value, + AnonConstKind, AtomicOrdering, Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, + UnevaluatedConst, ValTree, ValTreeKind, Value, }; pub use self::context::{ CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8f7e72f0ae11e..cabd43ebbdc60 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -515,6 +515,7 @@ symbols! { async_iterator_poll_next, async_trait_bounds, atomic, + atomic_load, atomic_mod, atomics, att_syntax, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 32642a13b42d9..8a0f80f8ce7e0 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -30,7 +30,7 @@ //! //! The atomic intrinsics provide common atomic operations on machine //! words, with multiple possible memory orderings. See the -//! [atomic types][crate::sync::atomic] docs for details. +//! [atomic types][atomic] docs for details. //! //! # Unwinding //! @@ -50,7 +50,7 @@ )] #![allow(missing_docs)] -use crate::marker::{DiscriminantKind, Tuple}; +use crate::marker::{ConstParamTy, DiscriminantKind, Tuple}; use crate::ptr; pub mod fallback; @@ -62,6 +62,20 @@ pub mod simd; #[cfg(all(target_has_atomic = "8", target_has_atomic = "32", target_has_atomic = "ptr"))] use crate::sync::atomic::{self, AtomicBool, AtomicI32, AtomicIsize, AtomicU32, Ordering}; +/// A type for atomic ordering parameters for intrinsics. This is a separate type from +/// `atomic::Ordering` so that we can make it `ConstParamTy` and fix the values used here without a +/// risk of leaking that to stable code. +#[derive(Debug, ConstParamTy, PartialEq, Eq)] +pub enum AtomicOrdering { + // These values must match the compiler's `AtomicOrdering` defined in + // `rustc_middle/src/ty/consts/int.rs`! + Relaxed = 0, + Release = 1, + Acquire = 2, + AcqRel = 3, + SeqCst = 4, +} + // N.B., these intrinsics take raw pointers because they mutate aliased // memory, which is not valid for either `&` or `&mut`. @@ -391,6 +405,15 @@ pub unsafe fn atomic_cxchgweak_seqcst_acquire(dst: *mut T, old: T, src: #[rustc_nounwind] pub unsafe fn atomic_cxchgweak_seqcst_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); +/// Loads the current value of the pointer. +/// `T` must be an integer or pointer type. +/// +/// The stabilized version of this intrinsic is available on the +/// [`atomic`] types via the `load` method. For example, [`AtomicBool::load`]. +#[rustc_intrinsic] +#[rustc_nounwind] +#[cfg(not(bootstrap))] +pub unsafe fn atomic_load(src: *const T) -> T; /// Loads the current value of the pointer. /// `T` must be an integer or pointer type. /// @@ -399,6 +422,7 @@ pub unsafe fn atomic_cxchgweak_seqcst_seqcst(dst: *mut T, old: T, src: /// [`Ordering::SeqCst`] as the `order`. For example, [`AtomicBool::load`]. #[rustc_intrinsic] #[rustc_nounwind] +#[cfg(bootstrap)] pub unsafe fn atomic_load_seqcst(src: *const T) -> T; /// Loads the current value of the pointer. /// `T` must be an integer or pointer type. @@ -408,6 +432,7 @@ pub unsafe fn atomic_load_seqcst(src: *const T) -> T; /// [`Ordering::Acquire`] as the `order`. For example, [`AtomicBool::load`]. #[rustc_intrinsic] #[rustc_nounwind] +#[cfg(bootstrap)] pub unsafe fn atomic_load_acquire(src: *const T) -> T; /// Loads the current value of the pointer. /// `T` must be an integer or pointer type. @@ -417,6 +442,7 @@ pub unsafe fn atomic_load_acquire(src: *const T) -> T; /// [`Ordering::Relaxed`] as the `order`. For example, [`AtomicBool::load`]. #[rustc_intrinsic] #[rustc_nounwind] +#[cfg(bootstrap)] pub unsafe fn atomic_load_relaxed(src: *const T) -> T; /// Stores the value at the specified memory location. diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index bd5a58d74ba0d..a3fbc71162bbd 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -3822,6 +3822,7 @@ unsafe fn atomic_store(dst: *mut T, val: T, order: Ordering) { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[cfg(bootstrap)] unsafe fn atomic_load(dst: *const T, order: Ordering) -> T { // SAFETY: the caller must uphold the safety contract for `atomic_load`. unsafe { @@ -3835,6 +3836,23 @@ unsafe fn atomic_load(dst: *const T, order: Ordering) -> T { } } +#[inline] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[cfg(not(bootstrap))] +unsafe fn atomic_load(dst: *const T, order: Ordering) -> T { + use intrinsics::AtomicOrdering; + // SAFETY: the caller must uphold the safety contract for `atomic_load`. + unsafe { + match order { + Relaxed => intrinsics::atomic_load::(dst), + Acquire => intrinsics::atomic_load::(dst), + SeqCst => intrinsics::atomic_load::(dst), + Release => panic!("there is no such thing as a release load"), + AcqRel => panic!("there is no such thing as an acquire-release load"), + } + } +} + #[inline] #[cfg(target_has_atomic)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index 2eb8086f578f2..a61226eeed9e4 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -1,4 +1,5 @@ use rustc_middle::mir::BinOp; +use rustc_middle::ty::AtomicOrdering; use rustc_middle::{mir, ty}; use self::helpers::check_intrinsic_arg_count; @@ -19,6 +20,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_atomic_intrinsic( &mut self, intrinsic_name: &str, + generic_args: ty::GenericArgsRef<'tcx>, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { @@ -35,6 +37,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } + fn read_ord_const_generic(o: AtomicOrdering) -> AtomicReadOrd { + match o { + AtomicOrdering::SeqCst => AtomicReadOrd::SeqCst, + AtomicOrdering::Acquire => AtomicReadOrd::Acquire, + AtomicOrdering::Relaxed => AtomicReadOrd::Relaxed, + _ => panic!("invalid read ordering `{o:?}`"), + } + } + fn write_ord(ord: &str) -> AtomicWriteOrd { match ord { "seqcst" => AtomicWriteOrd::SeqCst, @@ -66,7 +77,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } match &*intrinsic_structure { - ["load", ord] => this.atomic_load(args, dest, read_ord(ord))?, + // New-style intrinsics that use const generics + ["load"] => { + let ordering = generic_args.const_at(1).to_value(); + let ordering = + ordering.valtree.unwrap_branch()[0].unwrap_leaf().to_atomic_ordering(); + this.atomic_load(args, dest, read_ord_const_generic(ordering))?; + } + + // Old-style intrinsics that have the ordering in the intrinsic name ["store", ord] => this.atomic_store(args, write_ord(ord))?, ["fence", ord] => this.atomic_fence_intrinsic(args, fence_ord(ord))?, diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 69baa472cd694..581005bc9a1ed 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -97,7 +97,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); if let Some(name) = intrinsic_name.strip_prefix("atomic_") { - return this.emulate_atomic_intrinsic(name, args, dest); + return this.emulate_atomic_intrinsic(name, generic_args, args, dest); } if let Some(name) = intrinsic_name.strip_prefix("simd_") { return this.emulate_simd_intrinsic(name, generic_args, args, dest); diff --git a/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.rs b/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.rs index 29976836b0ba6..37c64c8194485 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.rs +++ b/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.rs @@ -1,5 +1,6 @@ //@compile-flags: -Zmiri-symbolic-alignment-check -Cdebug-assertions=no #![feature(core_intrinsics)] +use std::intrinsics; fn main() { // Do a 4-aligned u64 atomic access. That should be UB on all platforms, @@ -7,7 +8,7 @@ fn main() { let z = [0u32; 2]; let zptr = &z as *const _ as *const u64; unsafe { - ::std::intrinsics::atomic_load_seqcst(zptr); + intrinsics::atomic_load::<_, { intrinsics::AtomicOrdering::SeqCst }>(zptr); //~^ERROR: accessing memory with alignment 4, but alignment 8 is required } } diff --git a/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.stderr b/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.stderr index a9da740be1db9..e0f9d011ce4ef 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.stderr +++ b/src/tools/miri/tests/fail/unaligned_pointers/atomic_unaligned.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required --> tests/fail/unaligned_pointers/atomic_unaligned.rs:LL:CC | -LL | ::std::intrinsics::atomic_load_seqcst(zptr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required +LL | intrinsics::atomic_load::<_, { intrinsics::AtomicOrdering::SeqCst }>(zptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required | = help: this usually indicates that your program performed an invalid operation and caused Undefined Behavior = help: but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives diff --git a/tests/ui/intrinsics/intrinsic-atomics.rs b/tests/ui/intrinsics/intrinsic-atomics.rs index 9127cc649e66d..f96c6dc832ee4 100644 --- a/tests/ui/intrinsics/intrinsic-atomics.rs +++ b/tests/ui/intrinsics/intrinsic-atomics.rs @@ -1,14 +1,14 @@ //@ run-pass #![feature(core_intrinsics)] -use std::intrinsics as rusti; +use std::intrinsics::{self as rusti, AtomicOrdering}; pub fn main() { unsafe { let mut x: Box<_> = Box::new(1); - assert_eq!(rusti::atomic_load_seqcst(&*x), 1); + assert_eq!(rusti::atomic_load::<_, { AtomicOrdering::SeqCst }>(&*x), 1); *x = 5; - assert_eq!(rusti::atomic_load_acquire(&*x), 5); + assert_eq!(rusti::atomic_load::<_, { AtomicOrdering::Acquire }>(&*x), 5); rusti::atomic_store_seqcst(&mut *x, 3); assert_eq!(*x, 3); diff --git a/tests/ui/intrinsics/non-integer-atomic.rs b/tests/ui/intrinsics/non-integer-atomic.rs index 2d1d088208498..dd129e5594510 100644 --- a/tests/ui/intrinsics/non-integer-atomic.rs +++ b/tests/ui/intrinsics/non-integer-atomic.rs @@ -4,7 +4,7 @@ #![allow(warnings)] #![crate_type = "rlib"] -use std::intrinsics; +use std::intrinsics::{self, AtomicOrdering}; #[derive(Copy, Clone)] pub struct Foo(i64); @@ -12,8 +12,8 @@ pub type Bar = &'static Fn(); pub type Quux = [u8; 100]; pub unsafe fn test_bool_load(p: &mut bool, v: bool) { - intrinsics::atomic_load_seqcst(p); - //~^ ERROR `atomic_load_seqcst` intrinsic: expected basic integer type, found `bool` + intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `bool` } pub unsafe fn test_bool_store(p: &mut bool, v: bool) { @@ -32,8 +32,8 @@ pub unsafe fn test_bool_cxchg(p: &mut bool, v: bool) { } pub unsafe fn test_Foo_load(p: &mut Foo, v: Foo) { - intrinsics::atomic_load_seqcst(p); - //~^ ERROR `atomic_load_seqcst` intrinsic: expected basic integer type, found `Foo` + intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `Foo` } pub unsafe fn test_Foo_store(p: &mut Foo, v: Foo) { @@ -52,7 +52,7 @@ pub unsafe fn test_Foo_cxchg(p: &mut Foo, v: Foo) { } pub unsafe fn test_Bar_load(p: &mut Bar, v: Bar) { - intrinsics::atomic_load_seqcst(p); + intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); //~^ ERROR expected basic integer type, found `&dyn Fn()` } @@ -72,8 +72,8 @@ pub unsafe fn test_Bar_cxchg(p: &mut Bar, v: Bar) { } pub unsafe fn test_Quux_load(p: &mut Quux, v: Quux) { - intrinsics::atomic_load_seqcst(p); - //~^ ERROR `atomic_load_seqcst` intrinsic: expected basic integer type, found `[u8; 100]` + intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `[u8; 100]` } pub unsafe fn test_Quux_store(p: &mut Quux, v: Quux) { diff --git a/tests/ui/intrinsics/non-integer-atomic.stderr b/tests/ui/intrinsics/non-integer-atomic.stderr index 32791a8e8b7f1..58c2dc00c66d5 100644 --- a/tests/ui/intrinsics/non-integer-atomic.stderr +++ b/tests/ui/intrinsics/non-integer-atomic.stderr @@ -1,8 +1,8 @@ -error[E0511]: invalid monomorphization of `atomic_load_seqcst` intrinsic: expected basic integer type, found `bool` +error[E0511]: invalid monomorphization of `atomic_load` intrinsic: expected basic integer type, found `bool` --> $DIR/non-integer-atomic.rs:15:5 | -LL | intrinsics::atomic_load_seqcst(p); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `atomic_store_seqcst` intrinsic: expected basic integer type, found `bool` --> $DIR/non-integer-atomic.rs:20:5 @@ -22,11 +22,11 @@ error[E0511]: invalid monomorphization of `atomic_cxchg_seqcst_seqcst` intrinsic LL | intrinsics::atomic_cxchg_seqcst_seqcst(p, v, v); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `atomic_load_seqcst` intrinsic: expected basic integer type, found `Foo` +error[E0511]: invalid monomorphization of `atomic_load` intrinsic: expected basic integer type, found `Foo` --> $DIR/non-integer-atomic.rs:35:5 | -LL | intrinsics::atomic_load_seqcst(p); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `atomic_store_seqcst` intrinsic: expected basic integer type, found `Foo` --> $DIR/non-integer-atomic.rs:40:5 @@ -46,11 +46,11 @@ error[E0511]: invalid monomorphization of `atomic_cxchg_seqcst_seqcst` intrinsic LL | intrinsics::atomic_cxchg_seqcst_seqcst(p, v, v); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `atomic_load_seqcst` intrinsic: expected basic integer type, found `&dyn Fn()` +error[E0511]: invalid monomorphization of `atomic_load` intrinsic: expected basic integer type, found `&dyn Fn()` --> $DIR/non-integer-atomic.rs:55:5 | -LL | intrinsics::atomic_load_seqcst(p); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `atomic_store_seqcst` intrinsic: expected basic integer type, found `&dyn Fn()` --> $DIR/non-integer-atomic.rs:60:5 @@ -70,11 +70,11 @@ error[E0511]: invalid monomorphization of `atomic_cxchg_seqcst_seqcst` intrinsic LL | intrinsics::atomic_cxchg_seqcst_seqcst(p, v, v); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `atomic_load_seqcst` intrinsic: expected basic integer type, found `[u8; 100]` +error[E0511]: invalid monomorphization of `atomic_load` intrinsic: expected basic integer type, found `[u8; 100]` --> $DIR/non-integer-atomic.rs:75:5 | -LL | intrinsics::atomic_load_seqcst(p); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | intrinsics::atomic_load::<_, { AtomicOrdering::SeqCst }>(p); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `atomic_store_seqcst` intrinsic: expected basic integer type, found `[u8; 100]` --> $DIR/non-integer-atomic.rs:80:5 From a387c86a929c27c102833c55bebad11cfc1c155d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 28 May 2025 12:15:04 +0200 Subject: [PATCH 07/17] get rid of rustc_codegen_ssa::common::AtomicOrdering --- compiler/rustc_codegen_gcc/src/builder.rs | 10 +++++----- compiler/rustc_codegen_llvm/src/builder.rs | 12 ++++++------ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 8 ++++---- compiler/rustc_codegen_ssa/src/common.rs | 9 --------- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 18 +++++------------- .../rustc_codegen_ssa/src/traits/builder.rs | 6 ++---- compiler/rustc_middle/src/ty/consts/int.rs | 2 +- 7 files changed, 23 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 4e2163201fd01..d1fb8d8f9d6b8 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -12,7 +12,7 @@ use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout, WrappingRange}; use rustc_apfloat::{Float, Round, Status, ieee}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::{ - AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, + AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, }; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::PlaceRef; @@ -26,7 +26,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers, }; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_middle::ty::{self, AtomicOrdering, Instance, Ty, TyCtxt}; use rustc_span::Span; use rustc_span::def_id::DefId; use rustc_target::callconv::FnAbi; @@ -75,7 +75,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let load_ordering = match order { // TODO(antoyo): does this make sense? - AtomicOrdering::AcquireRelease | AtomicOrdering::Release => AtomicOrdering::Acquire, + AtomicOrdering::AcqRel | AtomicOrdering::Release => AtomicOrdering::Acquire, _ => order, }; let previous_value = @@ -2474,8 +2474,8 @@ impl ToGccOrdering for AtomicOrdering { AtomicOrdering::Relaxed => __ATOMIC_RELAXED, // TODO(antoyo): check if that's the same. AtomicOrdering::Acquire => __ATOMIC_ACQUIRE, AtomicOrdering::Release => __ATOMIC_RELEASE, - AtomicOrdering::AcquireRelease => __ATOMIC_ACQ_REL, - AtomicOrdering::SequentiallyConsistent => __ATOMIC_SEQ_CST, + AtomicOrdering::AcqRel => __ATOMIC_ACQ_REL, + AtomicOrdering::SeqCst => __ATOMIC_SEQ_CST, }; ordering as i32 } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5238755c8eb94..fcb55a0463535 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -612,7 +612,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { &mut self, ty: &'ll Type, ptr: &'ll Value, - order: rustc_codegen_ssa::common::AtomicOrdering, + order: rustc_middle::ty::AtomicOrdering, size: Size, ) -> &'ll Value { unsafe { @@ -851,7 +851,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { &mut self, val: &'ll Value, ptr: &'ll Value, - order: rustc_codegen_ssa::common::AtomicOrdering, + order: rustc_middle::ty::AtomicOrdering, size: Size, ) { debug!("Store {:?} -> {:?}", val, ptr); @@ -1307,8 +1307,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { dst: &'ll Value, cmp: &'ll Value, src: &'ll Value, - order: rustc_codegen_ssa::common::AtomicOrdering, - failure_order: rustc_codegen_ssa::common::AtomicOrdering, + order: rustc_middle::ty::AtomicOrdering, + failure_order: rustc_middle::ty::AtomicOrdering, weak: bool, ) -> (&'ll Value, &'ll Value) { let weak = if weak { llvm::True } else { llvm::False }; @@ -1334,7 +1334,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { op: rustc_codegen_ssa::common::AtomicRmwBinOp, dst: &'ll Value, mut src: &'ll Value, - order: rustc_codegen_ssa::common::AtomicOrdering, + order: rustc_middle::ty::AtomicOrdering, ) -> &'ll Value { // The only RMW operation that LLVM supports on pointers is compare-exchange. let requires_cast_to_int = self.val_ty(src) == self.type_ptr() @@ -1360,7 +1360,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn atomic_fence( &mut self, - order: rustc_codegen_ssa::common::AtomicOrdering, + order: rustc_middle::ty::AtomicOrdering, scope: SynchronizationScope, ) { let single_threaded = match scope { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 67a66e6ec795f..e27fbf94f341d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -426,14 +426,14 @@ pub(crate) enum AtomicOrdering { } impl AtomicOrdering { - pub(crate) fn from_generic(ao: rustc_codegen_ssa::common::AtomicOrdering) -> Self { - use rustc_codegen_ssa::common::AtomicOrdering as Common; + pub(crate) fn from_generic(ao: rustc_middle::ty::AtomicOrdering) -> Self { + use rustc_middle::ty::AtomicOrdering as Common; match ao { Common::Relaxed => Self::Monotonic, Common::Acquire => Self::Acquire, Common::Release => Self::Release, - Common::AcquireRelease => Self::AcquireRelease, - Common::SequentiallyConsistent => Self::SequentiallyConsistent, + Common::AcqRel => Self::AcquireRelease, + Common::SeqCst => Self::SequentiallyConsistent, } } } diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 6d0c9d8d06645..ef0d565333e4b 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -59,15 +59,6 @@ pub enum AtomicRmwBinOp { AtomicUMin, } -#[derive(Copy, Clone, Debug)] -pub enum AtomicOrdering { - Relaxed, - Acquire, - Release, - AcquireRelease, - SequentiallyConsistent, -} - #[derive(Copy, Clone, Debug)] pub enum SynchronizationScope { SingleThread, diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 1047082df42c2..a79d67bb6cdcc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -345,7 +345,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // This requires that atomic intrinsics follow a specific naming pattern: // "atomic_[_]" name if let Some(atomic) = name_str.strip_prefix("atomic_") => { - use crate::common::AtomicOrdering::*; + use rustc_middle::ty::AtomicOrdering::*; + use crate::common::{AtomicRmwBinOp, SynchronizationScope}; let invalid_monomorphization = |ty| { @@ -358,16 +359,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let parse_const_generic_ordering = |ord: ty::Value<'tcx>| { let discr = ord.valtree.unwrap_branch()[0].unwrap_leaf(); - let ord = discr.to_atomic_ordering(); - // We have to translate from the intrinsic ordering to the backend ordering. - use rustc_middle::ty::AtomicOrdering; - match ord { - AtomicOrdering::Relaxed => Relaxed, - AtomicOrdering::Release => Release, - AtomicOrdering::Acquire => Acquire, - AtomicOrdering::AcqRel => AcquireRelease, - AtomicOrdering::SeqCst => SequentiallyConsistent, - } + discr.to_atomic_ordering() }; // Some intrinsics have the ordering already converted to a const generic parameter, we handle those first. @@ -403,8 +395,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { "relaxed" => Relaxed, "acquire" => Acquire, "release" => Release, - "acqrel" => AcquireRelease, - "seqcst" => SequentiallyConsistent, + "acqrel" => AcqRel, + "seqcst" => SeqCst, _ => bx.sess().dcx().emit_fatal(errors::UnknownAtomicOrdering), }; diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index f66309cf340cc..e35716d3afb87 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -4,7 +4,7 @@ use std::ops::Deref; use rustc_abi::{Align, Scalar, Size, WrappingRange}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; -use rustc_middle::ty::{Instance, Ty}; +use rustc_middle::ty::{AtomicOrdering, Instance, Ty}; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_target::callconv::FnAbi; @@ -19,9 +19,7 @@ use super::misc::MiscCodegenMethods; use super::type_::{ArgAbiBuilderMethods, BaseTypeCodegenMethods, LayoutTypeCodegenMethods}; use super::{CodegenMethods, StaticBuilderMethods}; use crate::MemFlags; -use crate::common::{ - AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, -}; +use crate::common::{AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind}; use crate::mir::operand::{OperandRef, OperandValue}; use crate::mir::place::{PlaceRef, PlaceValue}; diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 0383814cc96f1..b087ae25486ee 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -29,7 +29,7 @@ impl ConstInt { /// An enum to represent the compiler-side view of `intrinsics::AtomicOrdering`. /// This lives here because there's a method in this file that needs it and it is entirely unclear /// where else to put this... -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub enum AtomicOrdering { // These values must match `intrinsics::AtomicOrdering`! Relaxed = 0, From 5e7185583f2b22c177d92ff4f00abe4464298928 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 23 May 2025 17:48:08 +0800 Subject: [PATCH 08/17] remove `visit_clobber` and move `DummyAstNode` to `rustc_expand` `visit_clobber` is not really useful except for one niche purpose involving generic code. We should just use the replace logic where we can. --- compiler/rustc_ast/src/ast.rs | 15 ++- compiler/rustc_ast/src/ast_traits.rs | 6 + compiler/rustc_ast/src/mut_visit.rs | 103 ------------------ compiler/rustc_expand/src/expand.rs | 99 ++++++++++++----- tests/ui-fulldeps/pprust-expr-roundtrip.rs | 14 +-- .../pprust-parenthesis-insertion.rs | 4 +- 6 files changed, 95 insertions(+), 146 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index a16219361c051..39805649ab229 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -32,7 +32,7 @@ use rustc_data_structures::tagged_ptr::Tag; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::AttrId; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; pub use crate::format::*; @@ -1526,6 +1526,19 @@ impl Expr { | ExprKind::Struct(_) ) } + + /// Creates a dummy `P`. + /// + /// Should only be used when it will be replaced afterwards or as a return value when an error was encountered. + pub fn dummy() -> P { + P(Expr { + id: DUMMY_NODE_ID, + kind: ExprKind::Dummy, + span: DUMMY_SP, + attrs: ThinVec::new(), + tokens: None, + }) + } } #[derive(Clone, Encodable, Decodable, Debug)] diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 21de7ff7719b6..797ab297319bf 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -304,6 +304,7 @@ impl HasAttrs for Stmt { } /// A newtype around an AST node that implements the traits above if the node implements them. +#[repr(transparent)] pub struct AstNodeWrapper { pub wrapped: Wrapped, pub tag: PhantomData, @@ -313,6 +314,11 @@ impl AstNodeWrapper { pub fn new(wrapped: Wrapped, _tag: Tag) -> AstNodeWrapper { AstNodeWrapper { wrapped, tag: Default::default() } } + + pub fn from_mut(wrapped: &mut Wrapped, _tag: Tag) -> &mut AstNodeWrapper { + // SAFETY: `AstNodeWrapper` is `repr(transparent)` w.r.t `Wrapped` + unsafe { &mut *<*mut Wrapped>::cast(wrapped) } + } } impl HasNodeId for AstNodeWrapper { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index a2e5e3b57fd7f..2b0f69dda96ce 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -368,14 +368,6 @@ pub trait MutVisitor: Sized { super::common_visitor_and_walkers!((mut) MutVisitor); -/// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful -/// when using a `flat_map_*` or `filter_map_*` method within a `visit_` -/// method. -pub fn visit_clobber(t: &mut T, f: impl FnOnce(T) -> T) { - let old_t = std::mem::replace(t, T::dummy()); - *t = f(old_t); -} - #[inline] fn visit_vec(elems: &mut Vec, mut visit_elem: F) where @@ -1417,101 +1409,6 @@ fn walk_capture_by(vis: &mut T, capture_by: &mut CaptureBy) { } } -/// Some value for the AST node that is valid but possibly meaningless. Similar -/// to `Default` but not intended for wide use. The value will never be used -/// meaningfully, it exists just to support unwinding in `visit_clobber` in the -/// case where its closure panics. -pub trait DummyAstNode { - fn dummy() -> Self; -} - -impl DummyAstNode for Option { - fn dummy() -> Self { - Default::default() - } -} - -impl DummyAstNode for P { - fn dummy() -> Self { - P(DummyAstNode::dummy()) - } -} - -impl DummyAstNode for Item { - fn dummy() -> Self { - Item { - attrs: Default::default(), - id: DUMMY_NODE_ID, - span: Default::default(), - vis: Visibility { - kind: VisibilityKind::Public, - span: Default::default(), - tokens: Default::default(), - }, - kind: ItemKind::ExternCrate(None, Ident::dummy()), - tokens: Default::default(), - } - } -} - -impl DummyAstNode for Expr { - fn dummy() -> Self { - Expr { - id: DUMMY_NODE_ID, - kind: ExprKind::Dummy, - span: Default::default(), - attrs: Default::default(), - tokens: Default::default(), - } - } -} - -impl DummyAstNode for Ty { - fn dummy() -> Self { - Ty { - id: DUMMY_NODE_ID, - kind: TyKind::Dummy, - span: Default::default(), - tokens: Default::default(), - } - } -} - -impl DummyAstNode for Pat { - fn dummy() -> Self { - Pat { - id: DUMMY_NODE_ID, - kind: PatKind::Wild, - span: Default::default(), - tokens: Default::default(), - } - } -} - -impl DummyAstNode for Stmt { - fn dummy() -> Self { - Stmt { id: DUMMY_NODE_ID, kind: StmtKind::Empty, span: Default::default() } - } -} - -impl DummyAstNode for Crate { - fn dummy() -> Self { - Crate { - attrs: Default::default(), - items: Default::default(), - spans: Default::default(), - id: DUMMY_NODE_ID, - is_placeholder: Default::default(), - } - } -} - -impl DummyAstNode for crate::ast_traits::AstNodeWrapper { - fn dummy() -> Self { - crate::ast_traits::AstNodeWrapper::new(N::dummy(), T::dummy()) - } -} - #[derive(Debug)] pub enum FnKind<'a> { /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index e5749ba96a6dd..15b84532b0ee2 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -3,15 +3,14 @@ use std::rc::Rc; use std::sync::Arc; use std::{iter, mem}; -use rustc_ast as ast; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list}; use rustc_ast::{ - AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, ExprKind, ForeignItemKind, - HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind, - NodeId, PatKind, StmtKind, TyKind, token, + self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, DUMMY_NODE_ID, + ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, + MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token, }; use rustc_ast_pretty::pprust; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; @@ -131,13 +130,9 @@ macro_rules! ast_fragments { pub(crate) fn mut_visit_with(&mut self, vis: &mut F) { match self { AstFragment::OptExpr(opt_expr) => { - visit_clobber(opt_expr, |opt_expr| { - if let Some(expr) = opt_expr { - vis.filter_map_expr(expr) - } else { - None - } - }); + if let Some(expr) = opt_expr.take() { + *opt_expr = vis.filter_map_expr(expr) + } } AstFragment::MethodReceiverExpr(expr) => vis.visit_method_receiver_expr(expr), $($(AstFragment::$Kind(ast) => vis.$mut_visit_ast(ast),)?)* @@ -1782,11 +1777,7 @@ impl InvocationCollectorNode for AstNodeWrapper, OptExprTag> { /// This struct is a hack to workaround unstable of `stmt_expr_attributes`. /// It can be removed once that feature is stabilized. struct MethodReceiverTag; -impl DummyAstNode for MethodReceiverTag { - fn dummy() -> MethodReceiverTag { - MethodReceiverTag - } -} + impl InvocationCollectorNode for AstNodeWrapper, MethodReceiverTag> { type OutputTy = Self; const KIND: AstFragmentKind = AstFragmentKind::MethodReceiverExpr; @@ -1852,6 +1843,57 @@ fn build_single_delegations<'a, Node: InvocationCollectorNode>( }) } +/// Required for `visit_node` obtained an owned `Node` from `&mut Node`. +trait DummyAstNode { + fn dummy() -> Self; +} + +impl DummyAstNode for ast::Crate { + fn dummy() -> Self { + ast::Crate { + attrs: Default::default(), + items: Default::default(), + spans: Default::default(), + id: DUMMY_NODE_ID, + is_placeholder: Default::default(), + } + } +} + +impl DummyAstNode for P { + fn dummy() -> Self { + P(ast::Ty { + id: DUMMY_NODE_ID, + kind: TyKind::Dummy, + span: Default::default(), + tokens: Default::default(), + }) + } +} + +impl DummyAstNode for P { + fn dummy() -> Self { + P(ast::Pat { + id: DUMMY_NODE_ID, + kind: PatKind::Wild, + span: Default::default(), + tokens: Default::default(), + }) + } +} + +impl DummyAstNode for P { + fn dummy() -> Self { + ast::Expr::dummy() + } +} + +impl DummyAstNode for AstNodeWrapper, MethodReceiverTag> { + fn dummy() -> Self { + AstNodeWrapper::new(ast::Expr::dummy(), MethodReceiverTag) + } +} + struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, invocations: Vec<(Invocation, Option>)>, @@ -2155,18 +2197,19 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.expand_cfg_attr(node, &attr, pos); continue; } - _ => visit_clobber(node, |node| { - self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND) + _ => { + let n = mem::replace(node, Node::dummy()); + *node = self + .collect_attr((attr, pos, derives), n.to_annotatable(), Node::KIND) .make_ast::() - }), + } }, None if node.is_mac_call() => { - visit_clobber(node, |node| { - // Do not clobber unless it's actually a macro (uncommon case). - let (mac, attrs, _) = node.take_mac_call(); - self.check_attributes(&attrs, &mac); - self.collect_bang(mac, Node::KIND).make_ast::() - }) + let n = mem::replace(node, Node::dummy()); + let (mac, attrs, _) = n.take_mac_call(); + self.check_attributes(&attrs, &mac); + + *node = self.collect_bang(mac, Node::KIND).make_ast::() } None if node.delegation().is_some() => unreachable!(), None => { @@ -2293,11 +2336,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } fn visit_method_receiver_expr(&mut self, node: &mut P) { - visit_clobber(node, |node| { - let mut wrapper = AstNodeWrapper::new(node, MethodReceiverTag); - self.visit_node(&mut wrapper); - wrapper.wrapped - }) + self.visit_node(AstNodeWrapper::from_mut(node, MethodReceiverTag)) } fn filter_map_expr(&mut self, node: P) -> Option> { diff --git a/tests/ui-fulldeps/pprust-expr-roundtrip.rs b/tests/ui-fulldeps/pprust-expr-roundtrip.rs index 4a866560e798a..f5cfa9e0bccff 100644 --- a/tests/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/tests/ui-fulldeps/pprust-expr-roundtrip.rs @@ -34,7 +34,7 @@ extern crate thin_vec; extern crate rustc_driver; use parser::parse_expr; -use rustc_ast::mut_visit::{visit_clobber, MutVisitor}; +use rustc_ast::mut_visit::MutVisitor; use rustc_ast::ptr::P; use rustc_ast::*; use rustc_ast_pretty::pprust; @@ -202,15 +202,9 @@ struct AddParens; impl MutVisitor for AddParens { fn visit_expr(&mut self, e: &mut P) { mut_visit::walk_expr(self, e); - visit_clobber(e, |e| { - P(Expr { - id: DUMMY_NODE_ID, - kind: ExprKind::Paren(e), - span: DUMMY_SP, - attrs: AttrVec::new(), - tokens: None, - }) - }); + let expr = std::mem::replace(e, Expr::dummy()); + + e.kind = ExprKind::Paren(expr); } } diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs index 2b41020d3071c..c566ac459e0d2 100644 --- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs +++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs @@ -42,7 +42,7 @@ use std::process::ExitCode; use parser::parse_expr; use rustc_ast::ast::{Expr, ExprKind}; -use rustc_ast::mut_visit::{self, DummyAstNode as _, MutVisitor}; +use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast_pretty::pprust; use rustc_session::parse::ParseSess; @@ -154,7 +154,7 @@ struct Unparenthesize; impl MutVisitor for Unparenthesize { fn visit_expr(&mut self, e: &mut P) { while let ExprKind::Paren(paren) = &mut e.kind { - **e = mem::replace(&mut *paren, Expr::dummy()); + *e = mem::replace(paren, Expr::dummy()); } mut_visit::walk_expr(self, e); } From b2e9c72e72e20a785e889298e5c13ad1567d5246 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 27 May 2025 20:14:20 +0200 Subject: [PATCH 09/17] `emit_xtensa_va_arg`: use `inbounds_ptradd` instead of `inbounds_gep` --- compiler/rustc_codegen_llvm/src/va_arg.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index b91b6efed4522..3106ed813b04e 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -334,8 +334,7 @@ fn emit_xtensa_va_arg<'ll, 'tcx>( // (*va).va_ndx let va_reg_offset = 4; let va_ndx_offset = va_reg_offset + 4; - let offset_ptr = - bx.inbounds_gep(bx.type_i8(), va_list_addr, &[bx.cx.const_usize(va_ndx_offset)]); + let offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(va_ndx_offset)); let offset = bx.load(bx.type_i32(), offset_ptr, bx.tcx().data_layout.i32_align.abi); let offset = round_up_to_alignment(bx, offset, layout.align.abi); @@ -356,11 +355,10 @@ fn emit_xtensa_va_arg<'ll, 'tcx>( bx.store(offset_next, offset_ptr, bx.tcx().data_layout.pointer_align.abi); // (*va).va_reg - let regsave_area_ptr = - bx.inbounds_gep(bx.type_i8(), va_list_addr, &[bx.cx.const_usize(va_reg_offset)]); + let regsave_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(va_reg_offset)); let regsave_area = bx.load(bx.type_ptr(), regsave_area_ptr, bx.tcx().data_layout.pointer_align.abi); - let regsave_value_ptr = bx.inbounds_gep(bx.type_i8(), regsave_area, &[offset]); + let regsave_value_ptr = bx.inbounds_ptradd(regsave_area, offset); bx.br(end); bx.switch_to_block(from_stack); @@ -381,9 +379,9 @@ fn emit_xtensa_va_arg<'ll, 'tcx>( bx.store(offset_next_corrected, offset_ptr, bx.tcx().data_layout.pointer_align.abi); // let stack_value_ptr = unsafe { (*va).va_stk.byte_add(offset_corrected) }; - let stack_area_ptr = bx.inbounds_gep(bx.type_i8(), va_list_addr, &[bx.cx.const_usize(0)]); + let stack_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(0)); let stack_area = bx.load(bx.type_ptr(), stack_area_ptr, bx.tcx().data_layout.pointer_align.abi); - let stack_value_ptr = bx.inbounds_gep(bx.type_i8(), stack_area, &[offset_corrected]); + let stack_value_ptr = bx.inbounds_ptradd(stack_area, offset_corrected); bx.br(end); bx.switch_to_block(end); From f80e3ac4ed1e1df2c337cbf7b231cb72ec4e3b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 3 Dec 2024 21:18:24 +0000 Subject: [PATCH 10/17] Use `cfg_attr` AST placeholder AST `cfg_attr_trace` for diagnostics PR 138515, we insert a placeholder attribute so that checks for attributes can still know about the placement of `cfg` attributes. When we suggest removing items with `cfg_attr`s (fix Issue 56328) and make them verbose. We tweak the wording of the existing "unused `extern crate`" lint. ``` warning: unused extern crate --> $DIR/removing-extern-crate.rs:9:1 | LL | extern crate removing_extern_crate as foo; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/removing-extern-crate.rs:6:9 | LL | #![warn(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]` help: remove the unused `extern crate` | LL - #[cfg_attr(test, macro_use)] LL - extern crate removing_extern_crate as foo; LL + | ``` --- compiler/rustc_lint/messages.ftl | 3 +- compiler/rustc_lint/src/early/diagnostics.rs | 4 +- compiler/rustc_lint/src/lints.rs | 4 +- compiler/rustc_lint_defs/src/lib.rs | 1 + compiler/rustc_resolve/src/check_unused.rs | 1 + .../edition-extern-crate-allowed.stderr | 6 +- tests/ui/imports/extern-crate-used.stderr | 7 +- tests/ui/lint/unnecessary-extern-crate.stderr | 41 ++++++++-- .../unused/lint-unused-extern-crate.stderr | 15 +++- tests/ui/proc-macro/no-macro-use-attr.stderr | 6 +- .../extern-crate-idiomatic-in-2018.stderr | 6 +- ...sue-54400-unused-extern-crate-attr-span.rs | 2 +- ...54400-unused-extern-crate-attr-span.stderr | 12 +-- tests/ui/rust-2018/remove-extern-crate.stderr | 7 +- .../removing-extern-crate-malformed-cfg.fixed | 15 ++++ .../removing-extern-crate-malformed-cfg.rs | 17 +++++ ...removing-extern-crate-malformed-cfg.stderr | 76 +++++++++++++++++++ .../ui/rust-2018/removing-extern-crate.stderr | 31 +++++++- 18 files changed, 227 insertions(+), 27 deletions(-) create mode 100644 tests/ui/rust-2018/removing-extern-crate-malformed-cfg.fixed create mode 100644 tests/ui/rust-2018/removing-extern-crate-malformed-cfg.rs create mode 100644 tests/ui/rust-2018/removing-extern-crate-malformed-cfg.stderr diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 7fdf26bf3af99..ea4d263dfa53a 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -953,7 +953,8 @@ lint_unused_doc_comment = unused doc comment .help = to document an item produced by a macro, the macro must produce the documentation as part of its expansion lint_unused_extern_crate = unused extern crate - .suggestion = remove it + .label = unused + .suggestion = remove the unused `extern crate` lint_unused_import_braces = braces around {$node} is unnecessary diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 40ca9e05d95d6..279f452a305e5 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -292,8 +292,8 @@ pub(super) fn decorate_lint( BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => { lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag); } - BuiltinLintDiag::UnusedExternCrate { removal_span } => { - lints::UnusedExternCrate { removal_span }.decorate_lint(diag); + BuiltinLintDiag::UnusedExternCrate { span, removal_span } => { + lints::UnusedExternCrate { span, removal_span }.decorate_lint(diag); } BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => { let suggestion_span = vis_span.between(ident_span); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index af8fa8ffa1fa7..16f11c45eb95d 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3051,7 +3051,9 @@ pub(crate) struct ByteSliceInPackedStructWithDerive { #[derive(LintDiagnostic)] #[diag(lint_unused_extern_crate)] pub(crate) struct UnusedExternCrate { - #[suggestion(code = "", applicability = "machine-applicable")] + #[label] + pub span: Span, + #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] pub removal_span: Span, } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index b4069b317bfa1..dc30532ba1a6f 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -736,6 +736,7 @@ pub enum BuiltinLintDiag { ty: String, }, UnusedExternCrate { + span: Span, removal_span: Span, }, ExternCrateNotIdiomatic { diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index e97233e97ce55..0579e91c0d6ec 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -154,6 +154,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { extern_crate.id, span, BuiltinLintDiag::UnusedExternCrate { + span: extern_crate.span, removal_span: extern_crate.span_with_attributes, }, ); diff --git a/tests/ui/editions/edition-extern-crate-allowed.stderr b/tests/ui/editions/edition-extern-crate-allowed.stderr index dde774c520d71..4444ab79b3823 100644 --- a/tests/ui/editions/edition-extern-crate-allowed.stderr +++ b/tests/ui/editions/edition-extern-crate-allowed.stderr @@ -2,7 +2,7 @@ warning: unused extern crate --> $DIR/edition-extern-crate-allowed.rs:7:1 | LL | extern crate edition_extern_crate_allowed; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/edition-extern-crate-allowed.rs:5:9 @@ -10,6 +10,10 @@ note: the lint level is defined here LL | #![warn(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]` +help: remove the unused `extern crate` + | +LL - extern crate edition_extern_crate_allowed; + | warning: 1 warning emitted diff --git a/tests/ui/imports/extern-crate-used.stderr b/tests/ui/imports/extern-crate-used.stderr index 982da0c913ed8..08bee39141473 100644 --- a/tests/ui/imports/extern-crate-used.stderr +++ b/tests/ui/imports/extern-crate-used.stderr @@ -2,13 +2,18 @@ error: unused extern crate --> $DIR/extern-crate-used.rs:18:1 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/extern-crate-used.rs:6:9 | LL | #![deny(unused_extern_crates)] | ^^^^^^^^^^^^^^^^^^^^ +help: remove the unused `extern crate` + | +LL - extern crate core; +LL + + | error: aborting due to 1 previous error diff --git a/tests/ui/lint/unnecessary-extern-crate.stderr b/tests/ui/lint/unnecessary-extern-crate.stderr index 1fa4aa9c9a9cf..db5406bc567d4 100644 --- a/tests/ui/lint/unnecessary-extern-crate.stderr +++ b/tests/ui/lint/unnecessary-extern-crate.stderr @@ -2,43 +2,72 @@ error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:6:1 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/unnecessary-extern-crate.rs:3:9 | LL | #![deny(unused_extern_crates)] | ^^^^^^^^^^^^^^^^^^^^ +help: remove the unused `extern crate` + | +LL - extern crate core; + | error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:9:1 | LL | extern crate core as x; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core as x; + | error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:31:5 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core; + | error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:35:5 | LL | extern crate core as x; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core as x; + | error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:44:9 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core; + | error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:48:9 | LL | extern crate core as x; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core as x; + | error: aborting due to 6 previous errors diff --git a/tests/ui/lint/unused/lint-unused-extern-crate.stderr b/tests/ui/lint/unused/lint-unused-extern-crate.stderr index 46d8f3beeab42..7fcbdd813ce09 100644 --- a/tests/ui/lint/unused/lint-unused-extern-crate.stderr +++ b/tests/ui/lint/unused/lint-unused-extern-crate.stderr @@ -2,19 +2,30 @@ error: unused extern crate --> $DIR/lint-unused-extern-crate.rs:11:1 | LL | extern crate lint_unused_extern_crate5; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/lint-unused-extern-crate.rs:7:9 | LL | #![deny(unused_extern_crates)] | ^^^^^^^^^^^^^^^^^^^^ +help: remove the unused `extern crate` + | +LL - extern crate lint_unused_extern_crate5; +LL + + | error: unused extern crate --> $DIR/lint-unused-extern-crate.rs:29:5 | LL | extern crate lint_unused_extern_crate2; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate lint_unused_extern_crate2; +LL + + | error: aborting due to 2 previous errors diff --git a/tests/ui/proc-macro/no-macro-use-attr.stderr b/tests/ui/proc-macro/no-macro-use-attr.stderr index 4913672450ab9..0bef563fbb9e1 100644 --- a/tests/ui/proc-macro/no-macro-use-attr.stderr +++ b/tests/ui/proc-macro/no-macro-use-attr.stderr @@ -2,13 +2,17 @@ warning: unused extern crate --> $DIR/no-macro-use-attr.rs:6:1 | LL | extern crate test_macros; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/no-macro-use-attr.rs:4:9 | LL | #![warn(unused_extern_crates)] | ^^^^^^^^^^^^^^^^^^^^ +help: remove the unused `extern crate` + | +LL - extern crate test_macros; + | warning: 1 warning emitted diff --git a/tests/ui/rust-2018/extern-crate-idiomatic-in-2018.stderr b/tests/ui/rust-2018/extern-crate-idiomatic-in-2018.stderr index a68d99c14cead..248d42ba3f43e 100644 --- a/tests/ui/rust-2018/extern-crate-idiomatic-in-2018.stderr +++ b/tests/ui/rust-2018/extern-crate-idiomatic-in-2018.stderr @@ -2,7 +2,7 @@ error: unused extern crate --> $DIR/extern-crate-idiomatic-in-2018.rs:12:1 | LL | extern crate edition_lint_paths; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/extern-crate-idiomatic-in-2018.rs:9:9 @@ -10,6 +10,10 @@ note: the lint level is defined here LL | #![deny(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(unused_extern_crates)]` implied by `#[deny(rust_2018_idioms)]` +help: remove the unused `extern crate` + | +LL - extern crate edition_lint_paths; + | error: aborting due to 1 previous error diff --git a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs index 573942bd09552..467914d6a5e85 100644 --- a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs +++ b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs @@ -8,7 +8,7 @@ // The suggestion span should include the attribute. -#[cfg(not(FALSE))] //~ HELP remove it +#[cfg(not(FALSE))] //~ HELP remove extern crate edition_lint_paths; //~^ ERROR unused extern crate diff --git a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr index 038a9dd967b73..9efc3493d3485 100644 --- a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr +++ b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr @@ -1,11 +1,8 @@ error: unused extern crate --> $DIR/issue-54400-unused-extern-crate-attr-span.rs:12:1 | -LL | / #[cfg(not(FALSE))] -LL | | extern crate edition_lint_paths; - | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- - | |________________________________| - | help: remove it +LL | extern crate edition_lint_paths; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/issue-54400-unused-extern-crate-attr-span.rs:6:9 @@ -13,6 +10,11 @@ note: the lint level is defined here LL | #![deny(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(unused_extern_crates)]` implied by `#[deny(rust_2018_idioms)]` +help: remove the unused `extern crate` + | +LL - #[cfg(not(FALSE))] +LL - extern crate edition_lint_paths; + | error: aborting due to 1 previous error diff --git a/tests/ui/rust-2018/remove-extern-crate.stderr b/tests/ui/rust-2018/remove-extern-crate.stderr index cb090c621e9f9..a530d40188ba2 100644 --- a/tests/ui/rust-2018/remove-extern-crate.stderr +++ b/tests/ui/rust-2018/remove-extern-crate.stderr @@ -2,7 +2,7 @@ warning: unused extern crate --> $DIR/remove-extern-crate.rs:11:1 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/remove-extern-crate.rs:7:9 @@ -10,6 +10,11 @@ note: the lint level is defined here LL | #![warn(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]` +help: remove the unused `extern crate` + | +LL - extern crate core; +LL + + | warning: `extern crate` is not idiomatic in the new edition --> $DIR/remove-extern-crate.rs:35:5 diff --git a/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.fixed b/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.fixed new file mode 100644 index 0000000000000..26c1c9015da8e --- /dev/null +++ b/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.fixed @@ -0,0 +1,15 @@ +//@ edition:2018 +//@ aux-build:../removing-extern-crate.rs +//@ run-rustfix + +#![warn(rust_2018_idioms)] + + //~ WARNING unused extern crate + //~ WARNING unused extern crate + +mod another { + //~ WARNING unused extern crate + //~ WARNING unused extern crate +} + +fn main() {} diff --git a/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.rs b/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.rs new file mode 100644 index 0000000000000..c5b629fa90b7a --- /dev/null +++ b/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.rs @@ -0,0 +1,17 @@ +//@ edition:2018 +//@ aux-build:../removing-extern-crate.rs +//@ run-rustfix + +#![warn(rust_2018_idioms)] + +#[cfg_attr(test, "macro_use")] //~ ERROR expected +extern crate removing_extern_crate as foo; //~ WARNING unused extern crate +extern crate core; //~ WARNING unused extern crate + +mod another { + #[cfg_attr(test)] //~ ERROR expected + extern crate removing_extern_crate as foo; //~ WARNING unused extern crate + extern crate core; //~ WARNING unused extern crate +} + +fn main() {} diff --git a/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.stderr b/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.stderr new file mode 100644 index 0000000000000..0e834707bf9b8 --- /dev/null +++ b/tests/ui/rust-2018/removing-extern-crate-malformed-cfg.stderr @@ -0,0 +1,76 @@ +error: expected identifier, found `"macro_use"` + --> $DIR/removing-extern-crate-malformed-cfg.rs:7:18 + | +LL | #[cfg_attr(test, "macro_use")] + | ^^^^^^^^^^^ expected identifier + | + = help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]` + = note: for more information, visit + +error: expected one of `(`, `,`, `::`, or `=`, found `` + --> $DIR/removing-extern-crate-malformed-cfg.rs:12:16 + | +LL | #[cfg_attr(test)] + | ^^^^ expected one of `(`, `,`, `::`, or `=` + | + = help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]` + = note: for more information, visit + +warning: unused extern crate + --> $DIR/removing-extern-crate-malformed-cfg.rs:8:1 + | +LL | extern crate removing_extern_crate as foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused + | +note: the lint level is defined here + --> $DIR/removing-extern-crate-malformed-cfg.rs:5:9 + | +LL | #![warn(rust_2018_idioms)] + | ^^^^^^^^^^^^^^^^ + = note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]` +help: remove the unused `extern crate` + | +LL - #[cfg_attr(test, "macro_use")] +LL - extern crate removing_extern_crate as foo; +LL + + | + +warning: unused extern crate + --> $DIR/removing-extern-crate-malformed-cfg.rs:9:1 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core; +LL + + | + +warning: unused extern crate + --> $DIR/removing-extern-crate-malformed-cfg.rs:13:5 + | +LL | extern crate removing_extern_crate as foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - #[cfg_attr(test)] +LL - extern crate removing_extern_crate as foo; +LL + + | + +warning: unused extern crate + --> $DIR/removing-extern-crate-malformed-cfg.rs:14:5 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core; +LL + + | + +error: aborting due to 2 previous errors; 4 warnings emitted + diff --git a/tests/ui/rust-2018/removing-extern-crate.stderr b/tests/ui/rust-2018/removing-extern-crate.stderr index 573125426404d..b6f8314ce4b64 100644 --- a/tests/ui/rust-2018/removing-extern-crate.stderr +++ b/tests/ui/rust-2018/removing-extern-crate.stderr @@ -2,7 +2,7 @@ warning: unused extern crate --> $DIR/removing-extern-crate.rs:8:1 | LL | extern crate dummy_crate as foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/removing-extern-crate.rs:6:9 @@ -10,24 +10,47 @@ note: the lint level is defined here LL | #![warn(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]` +help: remove the unused `extern crate` + | +LL - extern crate dummy_crate as foo; +LL + + | warning: unused extern crate --> $DIR/removing-extern-crate.rs:9:1 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core; +LL + + | warning: unused extern crate --> $DIR/removing-extern-crate.rs:12:5 | LL | extern crate dummy_crate as foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate dummy_crate as foo; +LL + + | warning: unused extern crate --> $DIR/removing-extern-crate.rs:13:5 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^^^^^^^ unused + | +help: remove the unused `extern crate` + | +LL - extern crate core; +LL + + | warning: 4 warnings emitted From 6eb601036859970c93c30781f6dfd1061a0c6737 Mon Sep 17 00:00:00 2001 From: Mu001999 Date: Thu, 29 May 2025 20:51:51 +0800 Subject: [PATCH 11/17] Add test for issue 127911 and 128839 --- .../alias-type-used-as-generic-arg-in-impl.rs | 19 ++++++++++++ .../trait-only-used-as-type-bound.rs | 31 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tests/ui/lint/dead-code/alias-type-used-as-generic-arg-in-impl.rs create mode 100644 tests/ui/lint/dead-code/trait-only-used-as-type-bound.rs diff --git a/tests/ui/lint/dead-code/alias-type-used-as-generic-arg-in-impl.rs b/tests/ui/lint/dead-code/alias-type-used-as-generic-arg-in-impl.rs new file mode 100644 index 0000000000000..4857ef6a9b8bf --- /dev/null +++ b/tests/ui/lint/dead-code/alias-type-used-as-generic-arg-in-impl.rs @@ -0,0 +1,19 @@ +//@ check-pass + +#![deny(dead_code)] + +struct T(X); + +type A = T; + +trait Tr { + fn foo(); +} + +impl Tr for T> { + fn foo() {} +} + +fn main() { + T::>::foo(); +} diff --git a/tests/ui/lint/dead-code/trait-only-used-as-type-bound.rs b/tests/ui/lint/dead-code/trait-only-used-as-type-bound.rs new file mode 100644 index 0000000000000..fb994653e1b1f --- /dev/null +++ b/tests/ui/lint/dead-code/trait-only-used-as-type-bound.rs @@ -0,0 +1,31 @@ +//@ check-pass + +#![deny(dead_code)] + +trait UInt: Copy + From {} + +impl UInt for u16 {} + +trait Int: Copy { + type Unsigned: UInt; + + fn as_unsigned(self) -> Self::Unsigned; +} + +impl Int for i16 { + type Unsigned = u16; + + fn as_unsigned(self) -> u16 { + self as _ + } +} + +fn priv_func(x: u8, y: T) -> (T::Unsigned, T::Unsigned) { + (T::Unsigned::from(x), y.as_unsigned()) +} + +pub fn pub_func(x: u8, y: i16) -> (u16, u16) { + priv_func(x, y) +} + +fn main() {} From 94cc72682ee840f7330f7703af74a2eee67fbba3 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 25 May 2025 00:36:44 +0200 Subject: [PATCH 12/17] implement `va_arg` for x86_64 systemv and macOS Turns out LLVM's `va_arg` is also unreliable for this target, so we need our own implementation. --- compiler/rustc_codegen_llvm/src/va_arg.rs | 316 +++++++++++++++++- .../c-link-to-rust-va-list-fn/checkrust.rs | 9 + .../run-make/c-link-to-rust-va-list-fn/test.c | 5 +- 3 files changed, 326 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 3106ed813b04e..8eedb5392b5c9 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -1,7 +1,10 @@ -use rustc_abi::{Align, Endian, HasDataLayout, Size}; +use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size, TyAndLayout}; +use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::mir::operand::OperandRef; -use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods}; +use rustc_codegen_ssa::traits::{ + BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, LayoutTypeCodegenMethods, +}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; @@ -303,6 +306,313 @@ fn emit_s390x_va_arg<'ll, 'tcx>( bx.load(val_type, val_addr, layout.align.abi) } +fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + list: OperandRef<'tcx, &'ll Value>, + target_ty: Ty<'tcx>, +) -> &'ll Value { + let dl = bx.cx.data_layout(); + + // Implementation of the systemv x86_64 ABI calling convention for va_args, see + // https://gitlab.com/x86-psABIs/x86-64-ABI (section 3.5.7). This implementation is heavily + // based on the one in clang. + + // We're able to take some shortcuts because the return type of `va_arg` must implement the + // `VaArgSafe` trait. Currently, only pointers, f64, i32, u32, i64 and u64 implement this trait. + + // typedef struct __va_list_tag { + // unsigned int gp_offset; + // unsigned int fp_offset; + // void *overflow_arg_area; + // void *reg_save_area; + // } va_list[1]; + let va_list_addr = list.immediate(); + + // Peel off any newtype wrappers. + // + // The "C" ABI does not unwrap newtypes (see `ReprOptions::inhibit_newtype_abi_optimization`). + // Here, we do actually want the unwrapped representation, because that is how LLVM/Clang + // pass such types to variadic functions. + // + // An example of a type that must be unwrapped is `Foo` below. Without the unwrapping, it has + // `BackendRepr::Memory`, but we need it to be `BackendRepr::Scalar` to generate correct code. + // + // ``` + // #[repr(C)] + // struct Empty; + // + // #[repr(C)] + // struct Foo([Empty; 8], i32); + // ``` + let layout = { + let mut layout = bx.cx.layout_of(target_ty); + + while let Some((_, inner)) = layout.non_1zst_field(bx.cx) { + layout = inner; + } + + layout + }; + + // AMD64-ABI 3.5.7p5: Step 1. Determine whether type may be passed + // in the registers. If not go to step 7. + + // AMD64-ABI 3.5.7p5: Step 2. Compute num_gp to hold the number of + // general purpose registers needed to pass type and num_fp to hold + // the number of floating point registers needed. + + let mut num_gp_registers = 0; + let mut num_fp_registers = 0; + + let mut registers_for_primitive = |p| match p { + Primitive::Int(integer, _is_signed) => { + num_gp_registers += integer.size().bytes().div_ceil(8) as u32; + } + Primitive::Float(float) => { + num_fp_registers += float.size().bytes().div_ceil(16) as u32; + } + Primitive::Pointer(_) => { + num_gp_registers += 1; + } + }; + + match layout.layout.backend_repr() { + BackendRepr::Scalar(scalar) => { + registers_for_primitive(scalar.primitive()); + } + BackendRepr::ScalarPair(scalar1, scalar2) => { + registers_for_primitive(scalar1.primitive()); + registers_for_primitive(scalar2.primitive()); + } + BackendRepr::SimdVector { .. } => { + // Because no instance of VaArgSafe uses a non-scalar `BackendRepr`. + unreachable!( + "No x86-64 SysV va_arg implementation for {:?}", + layout.layout.backend_repr() + ) + } + BackendRepr::Memory { .. } => { + let mem_addr = x86_64_sysv64_va_arg_from_memory(bx, va_list_addr, layout); + return bx.load(layout.llvm_type(bx), mem_addr, layout.align.abi); + } + }; + + // AMD64-ABI 3.5.7p5: Step 3. Verify whether arguments fit into + // registers. In the case: l->gp_offset > 48 - num_gp * 8 or + // l->fp_offset > 176 - num_fp * 16 go to step 7. + + let unsigned_int_offset = 4; + let ptr_offset = 8; + let gp_offset_ptr = va_list_addr; + let fp_offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(unsigned_int_offset)); + + let gp_offset_v = bx.load(bx.type_i32(), gp_offset_ptr, Align::from_bytes(8).unwrap()); + let fp_offset_v = bx.load(bx.type_i32(), fp_offset_ptr, Align::from_bytes(4).unwrap()); + + let mut use_regs = bx.const_bool(false); + + if num_gp_registers > 0 { + let max_offset_val = 48u32 - num_gp_registers * 8; + let fits_in_gp = bx.icmp(IntPredicate::IntULE, gp_offset_v, bx.const_u32(max_offset_val)); + use_regs = fits_in_gp; + } + + if num_fp_registers > 0 { + let max_offset_val = 176u32 - num_fp_registers * 16; + let fits_in_fp = bx.icmp(IntPredicate::IntULE, fp_offset_v, bx.const_u32(max_offset_val)); + use_regs = if num_gp_registers > 0 { bx.and(use_regs, fits_in_fp) } else { fits_in_fp }; + } + + let in_reg = bx.append_sibling_block("va_arg.in_reg"); + let in_mem = bx.append_sibling_block("va_arg.in_mem"); + let end = bx.append_sibling_block("va_arg.end"); + + bx.cond_br(use_regs, in_reg, in_mem); + + // Emit code to load the value if it was passed in a register. + bx.switch_to_block(in_reg); + + // AMD64-ABI 3.5.7p5: Step 4. Fetch type from l->reg_save_area with + // an offset of l->gp_offset and/or l->fp_offset. This may require + // copying to a temporary location in case the parameter is passed + // in different register classes or requires an alignment greater + // than 8 for general purpose registers and 16 for XMM registers. + // + // FIXME(llvm): This really results in shameful code when we end up needing to + // collect arguments from different places; often what should result in a + // simple assembling of a structure from scattered addresses has many more + // loads than necessary. Can we clean this up? + let reg_save_area_ptr = + bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * unsigned_int_offset + ptr_offset)); + let reg_save_area_v = bx.load(bx.type_ptr(), reg_save_area_ptr, dl.pointer_align.abi); + + let reg_addr = match layout.layout.backend_repr() { + BackendRepr::Scalar(scalar) => match scalar.primitive() { + Primitive::Int(_, _) | Primitive::Pointer(_) => { + let reg_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v); + + // Copy into a temporary if the type is more aligned than the register save area. + let gp_align = Align::from_bytes(8).unwrap(); + copy_to_temporary_if_more_aligned(bx, reg_addr, layout, gp_align) + } + Primitive::Float(_) => bx.inbounds_ptradd(reg_save_area_v, fp_offset_v), + }, + BackendRepr::ScalarPair(scalar1, scalar2) => { + let ty_lo = bx.cx().scalar_pair_element_backend_type(layout, 0, false); + let ty_hi = bx.cx().scalar_pair_element_backend_type(layout, 1, false); + + let align_lo = layout.field(bx.cx, 0).layout.align().abi; + let align_hi = layout.field(bx.cx, 1).layout.align().abi; + + match (scalar1.primitive(), scalar2.primitive()) { + (Primitive::Float(_), Primitive::Float(_)) => { + // SSE registers are spaced 16 bytes apart in the register save + // area, we need to collect the two eightbytes together. + // The ABI isn't explicit about this, but it seems reasonable + // to assume that the slots are 16-byte aligned, since the stack is + // naturally 16-byte aligned and the prologue is expected to store + // all the SSE registers to the RSA. + let reg_lo_addr = bx.inbounds_ptradd(reg_save_area_v, fp_offset_v); + let reg_hi_addr = bx.inbounds_ptradd(reg_lo_addr, bx.const_i32(16)); + + let align = layout.layout.align().abi; + let tmp = bx.alloca(layout.layout.size(), align); + + let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo); + let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi); + + let offset = scalar1.size(bx.cx).align_to(align_hi).bytes(); + let field0 = tmp; + let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset as u32)); + + bx.store(reg_lo, field0, align); + bx.store(reg_hi, field1, align); + + tmp + } + (Primitive::Float(_), _) | (_, Primitive::Float(_)) => { + let gp_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v); + let fp_addr = bx.inbounds_ptradd(reg_save_area_v, fp_offset_v); + + let (reg_lo_addr, reg_hi_addr) = match scalar1.primitive() { + Primitive::Float(_) => (fp_addr, gp_addr), + Primitive::Int(_, _) | Primitive::Pointer(_) => (gp_addr, fp_addr), + }; + + let tmp = bx.alloca(layout.layout.size(), layout.layout.align().abi); + + let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo); + let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi); + + let offset = scalar1.size(bx.cx).align_to(align_hi).bytes(); + let field0 = tmp; + let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset as u32)); + + bx.store(reg_lo, field0, align_lo); + bx.store(reg_hi, field1, align_hi); + + tmp + } + (_, _) => { + // Two integer/pointer values are just contiguous in memory. + let reg_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v); + + // Copy into a temporary if the type is more aligned than the register save area. + let gp_align = Align::from_bytes(8).unwrap(); + copy_to_temporary_if_more_aligned(bx, reg_addr, layout, gp_align) + } + } + } + // The Previous match on `BackendRepr` means control flow already escaped. + BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => unreachable!(), + }; + + // AMD64-ABI 3.5.7p5: Step 5. Set: + // l->gp_offset = l->gp_offset + num_gp * 8 + if num_gp_registers > 0 { + let offset = bx.const_u32(num_gp_registers * 8); + let sum = bx.add(gp_offset_v, offset); + // An alignment of 8 because `__va_list_tag` is 8-aligned and this is its first field. + bx.store(sum, gp_offset_ptr, Align::from_bytes(8).unwrap()); + } + + // l->fp_offset = l->fp_offset + num_fp * 16. + if num_fp_registers > 0 { + let offset = bx.const_u32(num_fp_registers * 16); + let sum = bx.add(fp_offset_v, offset); + bx.store(sum, fp_offset_ptr, Align::from_bytes(4).unwrap()); + } + + bx.br(end); + + bx.switch_to_block(in_mem); + let mem_addr = x86_64_sysv64_va_arg_from_memory(bx, va_list_addr, layout); + bx.br(end); + + bx.switch_to_block(end); + + let val_type = layout.llvm_type(bx); + let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]); + + bx.load(val_type, val_addr, layout.align.abi) +} + +/// Copy into a temporary if the type is more aligned than the register save area. +fn copy_to_temporary_if_more_aligned<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + reg_addr: &'ll Value, + layout: TyAndLayout<'tcx, Ty<'tcx>>, + src_align: Align, +) -> &'ll Value { + if layout.layout.align.abi > src_align { + let tmp = bx.alloca(layout.layout.size(), layout.layout.align().abi); + bx.memcpy( + tmp, + layout.layout.align.abi, + reg_addr, + src_align, + bx.const_u32(layout.layout.size().bytes() as u32), + MemFlags::empty(), + ); + tmp + } else { + reg_addr + } +} + +fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + va_list_addr: &'ll Value, + layout: TyAndLayout<'tcx, Ty<'tcx>>, +) -> &'ll Value { + let dl = bx.cx.data_layout(); + + let overflow_arg_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(8)); + + let overflow_arg_area_v = bx.load(bx.type_ptr(), overflow_arg_area_ptr, dl.pointer_align.abi); + // AMD64-ABI 3.5.7p5: Step 7. Align l->overflow_arg_area upwards to a 16 + // byte boundary if alignment needed by type exceeds 8 byte boundary. + // It isn't stated explicitly in the standard, but in practice we use + // alignment greater than 16 where necessary. + if layout.layout.align.abi.bytes() > 8 { + unreachable!("all instances of VaArgSafe have an alignment <= 8"); + } + + // AMD64-ABI 3.5.7p5: Step 8. Fetch type from l->overflow_arg_area. + let mem_addr = overflow_arg_area_v; + + // AMD64-ABI 3.5.7p5: Step 9. Set l->overflow_arg_area to: + // l->overflow_arg_area + sizeof(type). + // AMD64-ABI 3.5.7p5: Step 10. Align l->overflow_arg_area upwards to + // an 8 byte boundary. + let size_in_bytes = layout.layout.size().bytes(); + let offset = bx.const_i32(size_in_bytes.next_multiple_of(8) as i32); + let overflow_arg_area = bx.inbounds_ptradd(overflow_arg_area_v, offset); + bx.store(overflow_arg_area, overflow_arg_area_ptr, dl.pointer_align.abi); + + mem_addr +} + fn emit_xtensa_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, @@ -447,6 +757,8 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( AllowHigherAlign::No, ) } + // This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64. + "x86_64" => emit_x86_64_sysv64_va_arg(bx, addr, target_ty), "xtensa" => emit_xtensa_va_arg(bx, addr, target_ty), // For all other architecture/OS combinations fall back to using // the LLVM va_arg instruction. diff --git a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs index 7e4344f1c69b2..36c9db106ec45 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs @@ -112,6 +112,9 @@ pub unsafe extern "C" fn check_varargs_4(_: c_double, mut ap: ...) -> usize { continue_if!(ap.arg::() == 8.0); continue_if!(ap.arg::() == 9.0); continue_if!(ap.arg::() == 10.0); + continue_if!(ap.arg::() == 11.0); + continue_if!(ap.arg::() == 12.0); + continue_if!(ap.arg::() == 13.0); 0 } @@ -137,5 +140,11 @@ pub unsafe extern "C" fn check_varargs_5(_: c_int, mut ap: ...) -> usize { continue_if!(ap.arg::() == 9.0); continue_if!(ap.arg::() == 10); continue_if!(ap.arg::() == 10.0); + continue_if!(ap.arg::() == 11); + continue_if!(ap.arg::() == 11.0); + continue_if!(ap.arg::() == 12); + continue_if!(ap.arg::() == 12.0); + continue_if!(ap.arg::() == 13); + continue_if!(ap.arg::() == 13.0); 0 } diff --git a/tests/run-make/c-link-to-rust-va-list-fn/test.c b/tests/run-make/c-link-to-rust-va-list-fn/test.c index 5bdb51680a656..b47a9357880f1 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/test.c +++ b/tests/run-make/c-link-to-rust-va-list-fn/test.c @@ -41,10 +41,11 @@ int main(int argc, char* argv[]) { assert(check_varargs_3(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 0); - assert(check_varargs_4(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0) == 0); + assert(check_varargs_4(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + 13.0) == 0); assert(check_varargs_5(0, 1.0, 1, 2.0, 2, 3.0, 3, 4.0, 4, 5, 5.0, 6, 6.0, 7, 7.0, 8, 8.0, - 9, 9.0, 10, 10.0) == 0); + 9, 9.0, 10, 10.0, 11, 11.0, 12, 12.0, 13, 13.0) == 0); return 0; } From c6eb1d95d35bcaa0e9cf0e387cd98d4310ed434c Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 29 May 2025 15:40:50 -0500 Subject: [PATCH 13/17] rustdoc: display doc(cfg(false)) properly before we had an extra 'on' that was ungramatical. fixes https://github.com/rust-lang/rust/issues/138112 --- src/librustdoc/clean/cfg.rs | 15 ++++++++++++--- tests/rustdoc/cfg-bool.rs | 13 +++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 tests/rustdoc/cfg-bool.rs diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 439777843fb06..ebc276b38fbfa 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -171,10 +171,15 @@ impl Cfg { /// Renders the configuration for long display, as a long HTML description. pub(crate) fn render_long_html(&self) -> String { - let on = if self.should_use_with_in_description() { "with" } else { "on" }; + let on = if self.omit_preposition() { + "" + } else if self.should_use_with_in_description() { + "with " + } else { + "on " + }; - let mut msg = - format!("Available {on} {}", Display(self, Format::LongHtml)); + let mut msg = format!("Available {on}{}", Display(self, Format::LongHtml)); if self.should_append_only_to_description() { msg.push_str(" only"); } @@ -244,6 +249,10 @@ impl Cfg { Some(self.clone()) } } + + fn omit_preposition(&self) -> bool { + matches!(self, Cfg::True | Cfg::False) + } } impl ops::Not for Cfg { diff --git a/tests/rustdoc/cfg-bool.rs b/tests/rustdoc/cfg-bool.rs new file mode 100644 index 0000000000000..34fdfbe930e12 --- /dev/null +++ b/tests/rustdoc/cfg-bool.rs @@ -0,0 +1,13 @@ +#![feature(doc_cfg)] +#![crate_name = "foo"] + +// regression test for https://github.com/rust-lang/rust/issues/138112 + +//@ has 'foo/fn.foo.html' '//div[@class="stab portability"]' 'Available nowhere' +#[doc(cfg(false))] +pub fn foo() {} + +// a cfg(true) will simply be ommited, as it is the same as no cfg. +//@ !has 'foo/fn.bar.html' '//div[@class="stab portability"]' '' +#[doc(cfg(true))] +pub fn bar() {} From 6a79b272ba5edbb79d57cc96de8c9b363894e3fc Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 28 May 2025 00:58:14 +0000 Subject: [PATCH 14/17] float: Use a shared `assert_biteq!` macro for tests Clean up the separate `assert_f{16,32,64,128}` macros with a single `assert_biteq!` macro that works for all float widths. --- library/coretests/tests/floats/f128.rs | 77 +++++++++++--------------- library/coretests/tests/floats/f16.rs | 77 +++++++++++--------------- library/coretests/tests/floats/f32.rs | 77 +++++++++++--------------- library/coretests/tests/floats/f64.rs | 77 +++++++++++--------------- library/coretests/tests/floats/mod.rs | 31 +++++++++++ 5 files changed, 163 insertions(+), 176 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index 12cf651f03f46..2ae10d48bf197 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -39,17 +39,6 @@ const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; /// Second pattern over the mantissa const NAN_MASK2: u128 = 0x00005555555555555555555555555555; -/// Compare by representation -#[allow(unused_macros)] -macro_rules! assert_f128_biteq { - ($a:expr, $b:expr) => { - let (l, r): (&f128, &f128) = (&$a, &$b); - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}"); - }; -} - #[test] fn test_num_f128() { // FIXME(f16_f128): replace with a `test_num` call once the required `fmodl`/`fmodf128` @@ -401,27 +390,27 @@ fn test_next_up() { let max_down = f128::from_bits(MAX_DOWN_BITS); let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); - assert_f128_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN); - assert_f128_biteq!(f128::MIN.next_up(), -max_down); - assert_f128_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0); - assert_f128_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f128_biteq!((-tiny_up).next_up(), -tiny); - assert_f128_biteq!((-tiny).next_up(), -0.0f128); - assert_f128_biteq!((-0.0f128).next_up(), tiny); - assert_f128_biteq!(0.0f128.next_up(), tiny); - assert_f128_biteq!(tiny.next_up(), tiny_up); - assert_f128_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f128_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON); - assert_f128_biteq!(f128::MAX.next_up(), f128::INFINITY); - assert_f128_biteq!(f128::INFINITY.next_up(), f128::INFINITY); + assert_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN); + assert_biteq!(f128::MIN.next_up(), -max_down); + assert_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0f128); + assert_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_biteq!((-tiny_up).next_up(), -tiny); + assert_biteq!((-tiny).next_up(), -0.0f128); + assert_biteq!((-0.0f128).next_up(), tiny); + assert_biteq!(0.0f128.next_up(), tiny); + assert_biteq!(tiny.next_up(), tiny_up); + assert_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON); + assert_biteq!(f128::MAX.next_up(), f128::INFINITY); + assert_biteq!(f128::INFINITY.next_up(), f128::INFINITY); // Check that NaNs roundtrip. let nan0 = f128::NAN; let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); - assert_f128_biteq!(nan0.next_up(), nan0); - assert_f128_biteq!(nan1.next_up(), nan1); - assert_f128_biteq!(nan2.next_up(), nan2); + assert_biteq!(nan0.next_up(), nan0); + assert_biteq!(nan1.next_up(), nan1); + assert_biteq!(nan2.next_up(), nan2); } #[test] @@ -431,28 +420,28 @@ fn test_next_down() { let max_down = f128::from_bits(MAX_DOWN_BITS); let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); - assert_f128_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY); - assert_f128_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY); - assert_f128_biteq!((-max_down).next_down(), f128::MIN); - assert_f128_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON); - assert_f128_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f128_biteq!((-tiny).next_down(), -tiny_up); - assert_f128_biteq!((-0.0f128).next_down(), -tiny); - assert_f128_biteq!((0.0f128).next_down(), -tiny); - assert_f128_biteq!(tiny.next_down(), 0.0f128); - assert_f128_biteq!(tiny_up.next_down(), tiny); - assert_f128_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f128_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128); - assert_f128_biteq!(f128::MAX.next_down(), max_down); - assert_f128_biteq!(f128::INFINITY.next_down(), f128::MAX); + assert_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY); + assert_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY); + assert_biteq!((-max_down).next_down(), f128::MIN); + assert_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON); + assert_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_biteq!((-tiny).next_down(), -tiny_up); + assert_biteq!((-0.0f128).next_down(), -tiny); + assert_biteq!((0.0f128).next_down(), -tiny); + assert_biteq!(tiny.next_down(), 0.0f128); + assert_biteq!(tiny_up.next_down(), tiny); + assert_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128); + assert_biteq!(f128::MAX.next_down(), max_down); + assert_biteq!(f128::INFINITY.next_down(), f128::MAX); // Check that NaNs roundtrip. let nan0 = f128::NAN; let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); - assert_f128_biteq!(nan0.next_down(), nan0); - assert_f128_biteq!(nan1.next_down(), nan1); - assert_f128_biteq!(nan2.next_down(), nan2); + assert_biteq!(nan0.next_down(), nan0); + assert_biteq!(nan1.next_down(), nan1); + assert_biteq!(nan2.next_down(), nan2); } #[test] diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index db98181226c85..4246cec25dda3 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -41,17 +41,6 @@ const NAN_MASK1: u16 = 0x02aa; /// Second pattern over the mantissa const NAN_MASK2: u16 = 0x0155; -/// Compare by representation -#[allow(unused_macros)] -macro_rules! assert_f16_biteq { - ($a:expr, $b:expr) => { - let (l, r): (&f16, &f16) = (&$a, &$b); - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})"); - }; -} - #[test] fn test_num_f16() { super::test_num(10f16, 2f16); @@ -387,27 +376,27 @@ fn test_next_up() { let max_down = f16::from_bits(MAX_DOWN_BITS); let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); - assert_f16_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN); - assert_f16_biteq!(f16::MIN.next_up(), -max_down); - assert_f16_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0); - assert_f16_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f16_biteq!((-tiny_up).next_up(), -tiny); - assert_f16_biteq!((-tiny).next_up(), -0.0f16); - assert_f16_biteq!((-0.0f16).next_up(), tiny); - assert_f16_biteq!(0.0f16.next_up(), tiny); - assert_f16_biteq!(tiny.next_up(), tiny_up); - assert_f16_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f16_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON); - assert_f16_biteq!(f16::MAX.next_up(), f16::INFINITY); - assert_f16_biteq!(f16::INFINITY.next_up(), f16::INFINITY); + assert_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN); + assert_biteq!(f16::MIN.next_up(), -max_down); + assert_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0f16); + assert_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_biteq!((-tiny_up).next_up(), -tiny); + assert_biteq!((-tiny).next_up(), -0.0f16); + assert_biteq!((-0.0f16).next_up(), tiny); + assert_biteq!(0.0f16.next_up(), tiny); + assert_biteq!(tiny.next_up(), tiny_up); + assert_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON); + assert_biteq!(f16::MAX.next_up(), f16::INFINITY); + assert_biteq!(f16::INFINITY.next_up(), f16::INFINITY); // Check that NaNs roundtrip. let nan0 = f16::NAN; let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); - assert_f16_biteq!(nan0.next_up(), nan0); - assert_f16_biteq!(nan1.next_up(), nan1); - assert_f16_biteq!(nan2.next_up(), nan2); + assert_biteq!(nan0.next_up(), nan0); + assert_biteq!(nan1.next_up(), nan1); + assert_biteq!(nan2.next_up(), nan2); } #[test] @@ -417,28 +406,28 @@ fn test_next_down() { let max_down = f16::from_bits(MAX_DOWN_BITS); let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); - assert_f16_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY); - assert_f16_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY); - assert_f16_biteq!((-max_down).next_down(), f16::MIN); - assert_f16_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON); - assert_f16_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f16_biteq!((-tiny).next_down(), -tiny_up); - assert_f16_biteq!((-0.0f16).next_down(), -tiny); - assert_f16_biteq!((0.0f16).next_down(), -tiny); - assert_f16_biteq!(tiny.next_down(), 0.0f16); - assert_f16_biteq!(tiny_up.next_down(), tiny); - assert_f16_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f16_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16); - assert_f16_biteq!(f16::MAX.next_down(), max_down); - assert_f16_biteq!(f16::INFINITY.next_down(), f16::MAX); + assert_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY); + assert_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY); + assert_biteq!((-max_down).next_down(), f16::MIN); + assert_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON); + assert_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_biteq!((-tiny).next_down(), -tiny_up); + assert_biteq!((-0.0f16).next_down(), -tiny); + assert_biteq!((0.0f16).next_down(), -tiny); + assert_biteq!(tiny.next_down(), 0.0f16); + assert_biteq!(tiny_up.next_down(), tiny); + assert_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16); + assert_biteq!(f16::MAX.next_down(), max_down); + assert_biteq!(f16::INFINITY.next_down(), f16::MAX); // Check that NaNs roundtrip. let nan0 = f16::NAN; let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); - assert_f16_biteq!(nan0.next_down(), nan0); - assert_f16_biteq!(nan1.next_down(), nan1); - assert_f16_biteq!(nan2.next_down(), nan2); + assert_biteq!(nan0.next_down(), nan0); + assert_biteq!(nan1.next_down(), nan1); + assert_biteq!(nan2.next_down(), nan2); } #[test] diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 36f1937bedfeb..b989a8f0b33d6 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -23,17 +23,6 @@ const NAN_MASK1: u32 = 0x002a_aaaa; /// Second pattern over the mantissa const NAN_MASK2: u32 = 0x0055_5555; -#[allow(unused_macros)] -macro_rules! assert_f32_biteq { - ($left : expr, $right : expr) => { - let l: &f32 = &$left; - let r: &f32 = &$right; - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})"); - }; -} - #[test] fn test_num_f32() { super::test_num(10f32, 2f32); @@ -356,27 +345,27 @@ fn test_next_up() { let max_down = f32::from_bits(MAX_DOWN_BITS); let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); - assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); - assert_f32_biteq!(f32::MIN.next_up(), -max_down); - assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0); - assert_f32_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f32_biteq!((-tiny_up).next_up(), -tiny); - assert_f32_biteq!((-tiny).next_up(), -0.0f32); - assert_f32_biteq!((-0.0f32).next_up(), tiny); - assert_f32_biteq!(0.0f32.next_up(), tiny); - assert_f32_biteq!(tiny.next_up(), tiny_up); - assert_f32_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f32_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON); - assert_f32_biteq!(f32::MAX.next_up(), f32::INFINITY); - assert_f32_biteq!(f32::INFINITY.next_up(), f32::INFINITY); + assert_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); + assert_biteq!(f32::MIN.next_up(), -max_down); + assert_biteq!((-1.0f32 - f32::EPSILON).next_up(), -1.0f32); + assert_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_biteq!((-tiny_up).next_up(), -tiny); + assert_biteq!((-tiny).next_up(), -0.0f32); + assert_biteq!((-0.0f32).next_up(), tiny); + assert_biteq!(0.0f32.next_up(), tiny); + assert_biteq!(tiny.next_up(), tiny_up); + assert_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON); + assert_biteq!(f32::MAX.next_up(), f32::INFINITY); + assert_biteq!(f32::INFINITY.next_up(), f32::INFINITY); // Check that NaNs roundtrip. let nan0 = f32::NAN; let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); - assert_f32_biteq!(nan0.next_up(), nan0); - assert_f32_biteq!(nan1.next_up(), nan1); - assert_f32_biteq!(nan2.next_up(), nan2); + assert_biteq!(nan0.next_up(), nan0); + assert_biteq!(nan1.next_up(), nan1); + assert_biteq!(nan2.next_up(), nan2); } #[test] @@ -386,28 +375,28 @@ fn test_next_down() { let max_down = f32::from_bits(MAX_DOWN_BITS); let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); - assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); - assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); - assert_f32_biteq!((-max_down).next_down(), f32::MIN); - assert_f32_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); - assert_f32_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f32_biteq!((-tiny).next_down(), -tiny_up); - assert_f32_biteq!((-0.0f32).next_down(), -tiny); - assert_f32_biteq!((0.0f32).next_down(), -tiny); - assert_f32_biteq!(tiny.next_down(), 0.0f32); - assert_f32_biteq!(tiny_up.next_down(), tiny); - assert_f32_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f32_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32); - assert_f32_biteq!(f32::MAX.next_down(), max_down); - assert_f32_biteq!(f32::INFINITY.next_down(), f32::MAX); + assert_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); + assert_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); + assert_biteq!((-max_down).next_down(), f32::MIN); + assert_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); + assert_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_biteq!((-tiny).next_down(), -tiny_up); + assert_biteq!((-0.0f32).next_down(), -tiny); + assert_biteq!((0.0f32).next_down(), -tiny); + assert_biteq!(tiny.next_down(), 0.0f32); + assert_biteq!(tiny_up.next_down(), tiny); + assert_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32); + assert_biteq!(f32::MAX.next_down(), max_down); + assert_biteq!(f32::INFINITY.next_down(), f32::MAX); // Check that NaNs roundtrip. let nan0 = f32::NAN; let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); - assert_f32_biteq!(nan0.next_down(), nan0); - assert_f32_biteq!(nan1.next_down(), nan1); - assert_f32_biteq!(nan2.next_down(), nan2); + assert_biteq!(nan0.next_down(), nan0); + assert_biteq!(nan1.next_down(), nan1); + assert_biteq!(nan2.next_down(), nan2); } // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 970519983538d..2b0f6b4001c36 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -23,17 +23,6 @@ const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; /// Second pattern over the mantissa const NAN_MASK2: u64 = 0x0005_5555_5555_5555; -#[allow(unused_macros)] -macro_rules! assert_f64_biteq { - ($left : expr, $right : expr) => { - let l: &f64 = &$left; - let r: &f64 = &$right; - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l} ({lb:#018x}) is not bitequal to {r} ({rb:#018x})"); - }; -} - #[test] fn test_num_f64() { super::test_num(10f64, 2f64); @@ -343,26 +332,26 @@ fn test_next_up() { let max_down = f64::from_bits(MAX_DOWN_BITS); let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); - assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); - assert_f64_biteq!(f64::MIN.next_up(), -max_down); - assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0); - assert_f64_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f64_biteq!((-tiny_up).next_up(), -tiny); - assert_f64_biteq!((-tiny).next_up(), -0.0f64); - assert_f64_biteq!((-0.0f64).next_up(), tiny); - assert_f64_biteq!(0.0f64.next_up(), tiny); - assert_f64_biteq!(tiny.next_up(), tiny_up); - assert_f64_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f64_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON); - assert_f64_biteq!(f64::MAX.next_up(), f64::INFINITY); - assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY); + assert_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); + assert_biteq!(f64::MIN.next_up(), -max_down); + assert_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0f64); + assert_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_biteq!((-tiny_up).next_up(), -tiny); + assert_biteq!((-tiny).next_up(), -0.0f64); + assert_biteq!((-0.0f64).next_up(), tiny); + assert_biteq!(0.0f64.next_up(), tiny); + assert_biteq!(tiny.next_up(), tiny_up); + assert_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON); + assert_biteq!(f64::MAX.next_up(), f64::INFINITY); + assert_biteq!(f64::INFINITY.next_up(), f64::INFINITY); let nan0 = f64::NAN; let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); - assert_f64_biteq!(nan0.next_up(), nan0); - assert_f64_biteq!(nan1.next_up(), nan1); - assert_f64_biteq!(nan2.next_up(), nan2); + assert_biteq!(nan0.next_up(), nan0); + assert_biteq!(nan1.next_up(), nan1); + assert_biteq!(nan2.next_up(), nan2); } #[test] @@ -372,27 +361,27 @@ fn test_next_down() { let max_down = f64::from_bits(MAX_DOWN_BITS); let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); - assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); - assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); - assert_f64_biteq!((-max_down).next_down(), f64::MIN); - assert_f64_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); - assert_f64_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f64_biteq!((-tiny).next_down(), -tiny_up); - assert_f64_biteq!((-0.0f64).next_down(), -tiny); - assert_f64_biteq!((0.0f64).next_down(), -tiny); - assert_f64_biteq!(tiny.next_down(), 0.0f64); - assert_f64_biteq!(tiny_up.next_down(), tiny); - assert_f64_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f64_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64); - assert_f64_biteq!(f64::MAX.next_down(), max_down); - assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX); + assert_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); + assert_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); + assert_biteq!((-max_down).next_down(), f64::MIN); + assert_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); + assert_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_biteq!((-tiny).next_down(), -tiny_up); + assert_biteq!((-0.0f64).next_down(), -tiny); + assert_biteq!((0.0f64).next_down(), -tiny); + assert_biteq!(tiny.next_down(), 0.0f64); + assert_biteq!(tiny_up.next_down(), tiny); + assert_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64); + assert_biteq!(f64::MAX.next_down(), max_down); + assert_biteq!(f64::INFINITY.next_down(), f64::MAX); let nan0 = f64::NAN; let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); - assert_f64_biteq!(nan0.next_down(), nan0); - assert_f64_biteq!(nan1.next_down(), nan1); - assert_f64_biteq!(nan2.next_down(), nan2); + assert_biteq!(nan0.next_down(), nan0); + assert_biteq!(nan1.next_down(), nan1); + assert_biteq!(nan2.next_down(), nan2); } // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 7de34271ad05e..c861b5ceff3f5 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -15,6 +15,37 @@ macro_rules! assert_approx_eq { }}; } +/// Verify that floats have the same bitwise representation. Used to avoid the default `0.0 == -0.0` +/// behavior, as well as to ensure exact NaN bitpatterns. +macro_rules! assert_biteq { + (@inner $left:expr, $right:expr, $msg_sep:literal, $($tt:tt)*) => {{ + let l = $left; + let r = $right; + + // Hack to coerce left and right to the same type + let mut _eq_ty = l; + _eq_ty = r; + + // Hack to get the width from a value + let bits = (l.to_bits() - l.to_bits()).leading_zeros(); + assert!( + l.to_bits() == r.to_bits(), + "{msg}{nl}l: {l:?} ({lb:#0width$x})\nr: {r:?} ({rb:#0width$x})", + msg = format_args!($($tt)*), + nl = $msg_sep, + lb = l.to_bits(), + rb = r.to_bits(), + width = ((bits / 4) + 2) as usize, + ); + }}; + ($left:expr, $right:expr , $($tt:tt)*) => { + assert_biteq!(@inner $left, $right, "\n", $($tt)*) + }; + ($left:expr, $right:expr $(,)?) => { + assert_biteq!(@inner $left, $right, "", "") + }; +} + /// Helper function for testing numeric operations pub fn test_num(ten: T, two: T) where From 9907c5a806cb8645b39cf6a7e2442224e521ea24 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 28 May 2025 01:22:53 +0000 Subject: [PATCH 15/17] float: Replace some approximate assertions with exact As was mentioned at [1], we currently use `assert_approx_eq` for testing some math functions that guarantee exact results. Replace approximate assertions with exact ones for the following: * `ceil` * `floor` * `fract` * `from_bits` * `mul_add` * `round_ties_even` * `round` * `trunc` This likely wasn't done in the past to avoid writing out exact decimals that don't match the intuitive answer (e.g. 1.3 - 1.0 = 0.300...004), but ensuring our results are accurate seems more important here. [1]: https://github.com/rust-lang/rust/pull/138087#issuecomment-2842069281 --- library/coretests/tests/floats/f128.rs | 140 ++++++++++++------------- library/coretests/tests/floats/f16.rs | 140 ++++++++++++------------- library/coretests/tests/floats/f32.rs | 140 ++++++++++++------------- library/coretests/tests/floats/f64.rs | 132 +++++++++++------------ 4 files changed, 276 insertions(+), 276 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index 2ae10d48bf197..d417e715de3c4 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -249,98 +249,98 @@ fn test_classify() { #[cfg(not(miri))] #[cfg(target_has_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); + assert_eq!(1.0f128.floor(), 1.0f128); + assert_eq!(1.3f128.floor(), 1.0f128); + assert_eq!(1.5f128.floor(), 1.0f128); + assert_eq!(1.7f128.floor(), 1.0f128); + assert_eq!(0.0f128.floor(), 0.0f128); + assert_eq!((-0.0f128).floor(), -0.0f128); + assert_eq!((-1.0f128).floor(), -1.0f128); + assert_eq!((-1.3f128).floor(), -2.0f128); + assert_eq!((-1.5f128).floor(), -2.0f128); + assert_eq!((-1.7f128).floor(), -2.0f128); } #[test] #[cfg(not(miri))] #[cfg(target_has_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); + assert_eq!(1.0f128.ceil(), 1.0f128); + assert_eq!(1.3f128.ceil(), 2.0f128); + assert_eq!(1.5f128.ceil(), 2.0f128); + assert_eq!(1.7f128.ceil(), 2.0f128); + assert_eq!(0.0f128.ceil(), 0.0f128); + assert_eq!((-0.0f128).ceil(), -0.0f128); + assert_eq!((-1.0f128).ceil(), -1.0f128); + assert_eq!((-1.3f128).ceil(), -1.0f128); + assert_eq!((-1.5f128).ceil(), -1.0f128); + assert_eq!((-1.7f128).ceil(), -1.0f128); } #[test] #[cfg(not(miri))] #[cfg(target_has_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); + assert_eq!(2.5f128.round(), 3.0f128); + assert_eq!(1.0f128.round(), 1.0f128); + assert_eq!(1.3f128.round(), 1.0f128); + assert_eq!(1.5f128.round(), 2.0f128); + assert_eq!(1.7f128.round(), 2.0f128); + assert_eq!(0.0f128.round(), 0.0f128); + assert_eq!((-0.0f128).round(), -0.0f128); + assert_eq!((-1.0f128).round(), -1.0f128); + assert_eq!((-1.3f128).round(), -1.0f128); + assert_eq!((-1.5f128).round(), -2.0f128); + assert_eq!((-1.7f128).round(), -2.0f128); } #[test] #[cfg(not(miri))] #[cfg(target_has_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); + assert_eq!(2.5f128.round_ties_even(), 2.0f128); + assert_eq!(1.0f128.round_ties_even(), 1.0f128); + assert_eq!(1.3f128.round_ties_even(), 1.0f128); + assert_eq!(1.5f128.round_ties_even(), 2.0f128); + assert_eq!(1.7f128.round_ties_even(), 2.0f128); + assert_eq!(0.0f128.round_ties_even(), 0.0f128); + assert_eq!((-0.0f128).round_ties_even(), -0.0f128); + assert_eq!((-1.0f128).round_ties_even(), -1.0f128); + assert_eq!((-1.3f128).round_ties_even(), -1.0f128); + assert_eq!((-1.5f128).round_ties_even(), -2.0f128); + assert_eq!((-1.7f128).round_ties_even(), -2.0f128); } #[test] #[cfg(not(miri))] #[cfg(target_has_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); + assert_eq!(1.0f128.trunc(), 1.0f128); + assert_eq!(1.3f128.trunc(), 1.0f128); + assert_eq!(1.5f128.trunc(), 1.0f128); + assert_eq!(1.7f128.trunc(), 1.0f128); + assert_eq!(0.0f128.trunc(), 0.0f128); + assert_eq!((-0.0f128).trunc(), -0.0f128); + assert_eq!((-1.0f128).trunc(), -1.0f128); + assert_eq!((-1.3f128).trunc(), -1.0f128); + assert_eq!((-1.5f128).trunc(), -1.0f128); + assert_eq!((-1.7f128).trunc(), -1.0f128); } #[test] #[cfg(not(miri))] #[cfg(target_has_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); + assert_eq!(1.0f128.fract(), 0.0f128); + assert_eq!(1.3f128.fract(), 0.300000000000000000000000000000000039f128); + assert_eq!(1.5f128.fract(), 0.5f128); + assert_eq!(1.7f128.fract(), 0.7f128); + assert_eq!(0.0f128.fract(), 0.0f128); + assert_eq!((-0.0f128).fract(), -0.0f128); + assert_eq!((-1.0f128).fract(), -0.0f128); + assert_eq!((-1.3f128).fract(), -0.300000000000000000000000000000000039f128); + assert_eq!((-1.5f128).fract(), -0.5f128); + assert_eq!((-1.7f128).fract(), -0.699999999999999999999999999999999961f128); } #[test] @@ -451,10 +451,10 @@ 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_eq!(12.3f128.mul_add(4.5, 6.7), 62.0500000000000000000000000000000037); + assert_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.6500000000000000000000000000000049); + assert_eq!(0.0f128.mul_add(8.9, 1.2), 1.2); + assert_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6); 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); @@ -550,10 +550,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, 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); + assert_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); + assert_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5); + assert_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); + assert_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); // 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/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index 4246cec25dda3..dd0ad6cd41054 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -235,98 +235,98 @@ fn test_classify() { #[cfg(not(miri))] #[cfg(target_has_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); + assert_eq!(1.0f16.floor(), 1.0f16); + assert_eq!(1.3f16.floor(), 1.0f16); + assert_eq!(1.5f16.floor(), 1.0f16); + assert_eq!(1.7f16.floor(), 1.0f16); + assert_eq!(0.0f16.floor(), 0.0f16); + assert_eq!((-0.0f16).floor(), -0.0f16); + assert_eq!((-1.0f16).floor(), -1.0f16); + assert_eq!((-1.3f16).floor(), -2.0f16); + assert_eq!((-1.5f16).floor(), -2.0f16); + assert_eq!((-1.7f16).floor(), -2.0f16); } #[test] #[cfg(not(miri))] #[cfg(target_has_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); + assert_eq!(1.0f16.ceil(), 1.0f16); + assert_eq!(1.3f16.ceil(), 2.0f16); + assert_eq!(1.5f16.ceil(), 2.0f16); + assert_eq!(1.7f16.ceil(), 2.0f16); + assert_eq!(0.0f16.ceil(), 0.0f16); + assert_eq!((-0.0f16).ceil(), -0.0f16); + assert_eq!((-1.0f16).ceil(), -1.0f16); + assert_eq!((-1.3f16).ceil(), -1.0f16); + assert_eq!((-1.5f16).ceil(), -1.0f16); + assert_eq!((-1.7f16).ceil(), -1.0f16); } #[test] #[cfg(not(miri))] #[cfg(target_has_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); + assert_eq!(2.5f16.round(), 3.0f16); + assert_eq!(1.0f16.round(), 1.0f16); + assert_eq!(1.3f16.round(), 1.0f16); + assert_eq!(1.5f16.round(), 2.0f16); + assert_eq!(1.7f16.round(), 2.0f16); + assert_eq!(0.0f16.round(), 0.0f16); + assert_eq!((-0.0f16).round(), -0.0f16); + assert_eq!((-1.0f16).round(), -1.0f16); + assert_eq!((-1.3f16).round(), -1.0f16); + assert_eq!((-1.5f16).round(), -2.0f16); + assert_eq!((-1.7f16).round(), -2.0f16); } #[test] #[cfg(not(miri))] #[cfg(target_has_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); + assert_eq!(2.5f16.round_ties_even(), 2.0f16); + assert_eq!(1.0f16.round_ties_even(), 1.0f16); + assert_eq!(1.3f16.round_ties_even(), 1.0f16); + assert_eq!(1.5f16.round_ties_even(), 2.0f16); + assert_eq!(1.7f16.round_ties_even(), 2.0f16); + assert_eq!(0.0f16.round_ties_even(), 0.0f16); + assert_eq!((-0.0f16).round_ties_even(), -0.0f16); + assert_eq!((-1.0f16).round_ties_even(), -1.0f16); + assert_eq!((-1.3f16).round_ties_even(), -1.0f16); + assert_eq!((-1.5f16).round_ties_even(), -2.0f16); + assert_eq!((-1.7f16).round_ties_even(), -2.0f16); } #[test] #[cfg(not(miri))] #[cfg(target_has_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); + assert_eq!(1.0f16.trunc(), 1.0f16); + assert_eq!(1.3f16.trunc(), 1.0f16); + assert_eq!(1.5f16.trunc(), 1.0f16); + assert_eq!(1.7f16.trunc(), 1.0f16); + assert_eq!(0.0f16.trunc(), 0.0f16); + assert_eq!((-0.0f16).trunc(), -0.0f16); + assert_eq!((-1.0f16).trunc(), -1.0f16); + assert_eq!((-1.3f16).trunc(), -1.0f16); + assert_eq!((-1.5f16).trunc(), -1.0f16); + assert_eq!((-1.7f16).trunc(), -1.0f16); } #[test] #[cfg(not(miri))] #[cfg(target_has_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); + assert_eq!(1.0f16.fract(), 0.0f16); + assert_eq!(1.3f16.fract(), 0.2998f16); + assert_eq!(1.5f16.fract(), 0.5f16); + assert_eq!(1.7f16.fract(), 0.7f16); + assert_eq!(0.0f16.fract(), 0.0f16); + assert_eq!((-0.0f16).fract(), -0.0f16); + assert_eq!((-1.0f16).fract(), -0.0f16); + assert_eq!((-1.3f16).fract(), -0.2998f16); + assert_eq!((-1.5f16).fract(), -0.5f16); + assert_eq!((-1.7f16).fract(), -0.7f16); } #[test] @@ -437,10 +437,10 @@ 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_eq!(12.3f16.mul_add(4.5, 6.7), 62.031); + assert_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.625); + assert_eq!(0.0f16.mul_add(8.9, 1.2), 1.2); + assert_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6); 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); @@ -530,10 +530,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, 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); + assert_eq!(f16::from_bits(0x3c00), 1.0); + assert_eq!(f16::from_bits(0x4a40), 12.5); + assert_eq!(f16::from_bits(0x6539), 1337.0); + assert_eq!(f16::from_bits(0xcb20), -14.25); // Check that NaNs roundtrip their bits regardless of signaling-ness let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index b989a8f0b33d6..17e903ebebd5d 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -204,88 +204,88 @@ fn test_classify() { #[test] fn test_floor() { - assert_approx_eq!(f32::math::floor(1.0f32), 1.0f32); - assert_approx_eq!(f32::math::floor(1.3f32), 1.0f32); - assert_approx_eq!(f32::math::floor(1.5f32), 1.0f32); - assert_approx_eq!(f32::math::floor(1.7f32), 1.0f32); - assert_approx_eq!(f32::math::floor(0.0f32), 0.0f32); - assert_approx_eq!(f32::math::floor(-0.0f32), -0.0f32); - assert_approx_eq!(f32::math::floor(-1.0f32), -1.0f32); - assert_approx_eq!(f32::math::floor(-1.3f32), -2.0f32); - assert_approx_eq!(f32::math::floor(-1.5f32), -2.0f32); - assert_approx_eq!(f32::math::floor(-1.7f32), -2.0f32); + assert_eq!(f32::math::floor(1.0f32), 1.0f32); + assert_eq!(f32::math::floor(1.3f32), 1.0f32); + assert_eq!(f32::math::floor(1.5f32), 1.0f32); + assert_eq!(f32::math::floor(1.7f32), 1.0f32); + assert_eq!(f32::math::floor(0.0f32), 0.0f32); + assert_eq!(f32::math::floor(-0.0f32), -0.0f32); + assert_eq!(f32::math::floor(-1.0f32), -1.0f32); + assert_eq!(f32::math::floor(-1.3f32), -2.0f32); + assert_eq!(f32::math::floor(-1.5f32), -2.0f32); + assert_eq!(f32::math::floor(-1.7f32), -2.0f32); } #[test] fn test_ceil() { - assert_approx_eq!(f32::math::ceil(1.0f32), 1.0f32); - assert_approx_eq!(f32::math::ceil(1.3f32), 2.0f32); - assert_approx_eq!(f32::math::ceil(1.5f32), 2.0f32); - assert_approx_eq!(f32::math::ceil(1.7f32), 2.0f32); - assert_approx_eq!(f32::math::ceil(0.0f32), 0.0f32); - assert_approx_eq!(f32::math::ceil(-0.0f32), -0.0f32); - assert_approx_eq!(f32::math::ceil(-1.0f32), -1.0f32); - assert_approx_eq!(f32::math::ceil(-1.3f32), -1.0f32); - assert_approx_eq!(f32::math::ceil(-1.5f32), -1.0f32); - assert_approx_eq!(f32::math::ceil(-1.7f32), -1.0f32); + assert_eq!(f32::math::ceil(1.0f32), 1.0f32); + assert_eq!(f32::math::ceil(1.3f32), 2.0f32); + assert_eq!(f32::math::ceil(1.5f32), 2.0f32); + assert_eq!(f32::math::ceil(1.7f32), 2.0f32); + assert_eq!(f32::math::ceil(0.0f32), 0.0f32); + assert_eq!(f32::math::ceil(-0.0f32), -0.0f32); + assert_eq!(f32::math::ceil(-1.0f32), -1.0f32); + assert_eq!(f32::math::ceil(-1.3f32), -1.0f32); + assert_eq!(f32::math::ceil(-1.5f32), -1.0f32); + assert_eq!(f32::math::ceil(-1.7f32), -1.0f32); } #[test] fn test_round() { - assert_approx_eq!(f32::math::round(2.5f32), 3.0f32); - assert_approx_eq!(f32::math::round(1.0f32), 1.0f32); - assert_approx_eq!(f32::math::round(1.3f32), 1.0f32); - assert_approx_eq!(f32::math::round(1.5f32), 2.0f32); - assert_approx_eq!(f32::math::round(1.7f32), 2.0f32); - assert_approx_eq!(f32::math::round(0.0f32), 0.0f32); - assert_approx_eq!(f32::math::round(-0.0f32), -0.0f32); - assert_approx_eq!(f32::math::round(-1.0f32), -1.0f32); - assert_approx_eq!(f32::math::round(-1.3f32), -1.0f32); - assert_approx_eq!(f32::math::round(-1.5f32), -2.0f32); - assert_approx_eq!(f32::math::round(-1.7f32), -2.0f32); + assert_eq!(f32::math::round(2.5f32), 3.0f32); + assert_eq!(f32::math::round(1.0f32), 1.0f32); + assert_eq!(f32::math::round(1.3f32), 1.0f32); + assert_eq!(f32::math::round(1.5f32), 2.0f32); + assert_eq!(f32::math::round(1.7f32), 2.0f32); + assert_eq!(f32::math::round(0.0f32), 0.0f32); + assert_eq!(f32::math::round(-0.0f32), -0.0f32); + assert_eq!(f32::math::round(-1.0f32), -1.0f32); + assert_eq!(f32::math::round(-1.3f32), -1.0f32); + assert_eq!(f32::math::round(-1.5f32), -2.0f32); + assert_eq!(f32::math::round(-1.7f32), -2.0f32); } #[test] fn test_round_ties_even() { - assert_approx_eq!(f32::math::round_ties_even(2.5f32), 2.0f32); - assert_approx_eq!(f32::math::round_ties_even(1.0f32), 1.0f32); - assert_approx_eq!(f32::math::round_ties_even(1.3f32), 1.0f32); - assert_approx_eq!(f32::math::round_ties_even(1.5f32), 2.0f32); - assert_approx_eq!(f32::math::round_ties_even(1.7f32), 2.0f32); - assert_approx_eq!(f32::math::round_ties_even(0.0f32), 0.0f32); - assert_approx_eq!(f32::math::round_ties_even(-0.0f32), -0.0f32); - assert_approx_eq!(f32::math::round_ties_even(-1.0f32), -1.0f32); - assert_approx_eq!(f32::math::round_ties_even(-1.3f32), -1.0f32); - assert_approx_eq!(f32::math::round_ties_even(-1.5f32), -2.0f32); - assert_approx_eq!(f32::math::round_ties_even(-1.7f32), -2.0f32); + assert_eq!(f32::math::round_ties_even(2.5f32), 2.0f32); + assert_eq!(f32::math::round_ties_even(1.0f32), 1.0f32); + assert_eq!(f32::math::round_ties_even(1.3f32), 1.0f32); + assert_eq!(f32::math::round_ties_even(1.5f32), 2.0f32); + assert_eq!(f32::math::round_ties_even(1.7f32), 2.0f32); + assert_eq!(f32::math::round_ties_even(0.0f32), 0.0f32); + assert_eq!(f32::math::round_ties_even(-0.0f32), -0.0f32); + assert_eq!(f32::math::round_ties_even(-1.0f32), -1.0f32); + assert_eq!(f32::math::round_ties_even(-1.3f32), -1.0f32); + assert_eq!(f32::math::round_ties_even(-1.5f32), -2.0f32); + assert_eq!(f32::math::round_ties_even(-1.7f32), -2.0f32); } #[test] fn test_trunc() { - assert_approx_eq!(f32::math::trunc(1.0f32), 1.0f32); - assert_approx_eq!(f32::math::trunc(1.3f32), 1.0f32); - assert_approx_eq!(f32::math::trunc(1.5f32), 1.0f32); - assert_approx_eq!(f32::math::trunc(1.7f32), 1.0f32); - assert_approx_eq!(f32::math::trunc(0.0f32), 0.0f32); - assert_approx_eq!(f32::math::trunc(-0.0f32), -0.0f32); - assert_approx_eq!(f32::math::trunc(-1.0f32), -1.0f32); - assert_approx_eq!(f32::math::trunc(-1.3f32), -1.0f32); - assert_approx_eq!(f32::math::trunc(-1.5f32), -1.0f32); - assert_approx_eq!(f32::math::trunc(-1.7f32), -1.0f32); + assert_eq!(f32::math::trunc(1.0f32), 1.0f32); + assert_eq!(f32::math::trunc(1.3f32), 1.0f32); + assert_eq!(f32::math::trunc(1.5f32), 1.0f32); + assert_eq!(f32::math::trunc(1.7f32), 1.0f32); + assert_eq!(f32::math::trunc(0.0f32), 0.0f32); + assert_eq!(f32::math::trunc(-0.0f32), -0.0f32); + assert_eq!(f32::math::trunc(-1.0f32), -1.0f32); + assert_eq!(f32::math::trunc(-1.3f32), -1.0f32); + assert_eq!(f32::math::trunc(-1.5f32), -1.0f32); + assert_eq!(f32::math::trunc(-1.7f32), -1.0f32); } #[test] fn test_fract() { - assert_approx_eq!(f32::math::fract(1.0f32), 0.0f32); - assert_approx_eq!(f32::math::fract(1.3f32), 0.3f32); - assert_approx_eq!(f32::math::fract(1.5f32), 0.5f32); - assert_approx_eq!(f32::math::fract(1.7f32), 0.7f32); - assert_approx_eq!(f32::math::fract(0.0f32), 0.0f32); - assert_approx_eq!(f32::math::fract(-0.0f32), -0.0f32); - assert_approx_eq!(f32::math::fract(-1.0f32), -0.0f32); - assert_approx_eq!(f32::math::fract(-1.3f32), -0.3f32); - assert_approx_eq!(f32::math::fract(-1.5f32), -0.5f32); - assert_approx_eq!(f32::math::fract(-1.7f32), -0.7f32); + assert_eq!(f32::math::fract(1.0f32), 0.0f32); + assert_eq!(f32::math::fract(1.3f32), 0.29999995f32); + assert_eq!(f32::math::fract(1.5f32), 0.5f32); + assert_eq!(f32::math::fract(1.7f32), 0.70000005f32); + assert_eq!(f32::math::fract(0.0f32), 0.0f32); + assert_eq!(f32::math::fract(-0.0f32), -0.0f32); + assert_eq!(f32::math::fract(-1.0f32), -0.0f32); + assert_eq!(f32::math::fract(-1.3f32), -0.29999995f32); + assert_eq!(f32::math::fract(-1.5f32), -0.5f32); + assert_eq!(f32::math::fract(-1.7f32), -0.70000005f32); } #[test] @@ -406,10 +406,10 @@ fn test_mul_add() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(f32::math::mul_add(12.3f32, 4.5, 6.7), 62.05); - assert_approx_eq!(f32::math::mul_add(-12.3f32, -4.5, -6.7), 48.65); - assert_approx_eq!(f32::math::mul_add(0.0f32, 8.9, 1.2), 1.2); - assert_approx_eq!(f32::math::mul_add(3.4f32, -0.0, 5.6), 5.6); + assert_eq!(f32::math::mul_add(12.3f32, 4.5, 6.7), 62.05); + assert_eq!(f32::math::mul_add(-12.3f32, -4.5, -6.7), 48.65); + assert_eq!(f32::math::mul_add(0.0f32, 8.9, 1.2), 1.2); + assert_eq!(f32::math::mul_add(3.4f32, -0.0, 5.6), 5.6); assert!(f32::math::mul_add(nan, 7.8, 9.0).is_nan()); assert_eq!(f32::math::mul_add(inf, 7.8, 9.0), inf); assert_eq!(f32::math::mul_add(neg_inf, 7.8, 9.0), neg_inf); @@ -492,10 +492,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f32).to_bits(), 0x41480000); assert_eq!((1337f32).to_bits(), 0x44a72000); assert_eq!((-14.25f32).to_bits(), 0xc1640000); - assert_approx_eq!(f32::from_bits(0x3f800000), 1.0); - assert_approx_eq!(f32::from_bits(0x41480000), 12.5); - assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0); - assert_approx_eq!(f32::from_bits(0xc1640000), -14.25); + assert_eq!(f32::from_bits(0x3f800000), 1.0); + assert_eq!(f32::from_bits(0x41480000), 12.5); + assert_eq!(f32::from_bits(0x44a72000), 1337.0); + assert_eq!(f32::from_bits(0xc1640000), -14.25); // 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/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 2b0f6b4001c36..1cc7b3e13226f 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -191,88 +191,88 @@ fn test_classify() { #[test] fn test_floor() { - assert_approx_eq!(f64::math::floor(1.0f64), 1.0f64); - assert_approx_eq!(f64::math::floor(1.3f64), 1.0f64); - assert_approx_eq!(f64::math::floor(1.5f64), 1.0f64); - assert_approx_eq!(f64::math::floor(1.7f64), 1.0f64); - assert_approx_eq!(f64::math::floor(0.0f64), 0.0f64); - assert_approx_eq!(f64::math::floor(-0.0f64), -0.0f64); - assert_approx_eq!(f64::math::floor(-1.0f64), -1.0f64); - assert_approx_eq!(f64::math::floor(-1.3f64), -2.0f64); - assert_approx_eq!(f64::math::floor(-1.5f64), -2.0f64); - assert_approx_eq!(f64::math::floor(-1.7f64), -2.0f64); + assert_eq!(f64::math::floor(1.0f64), 1.0f64); + assert_eq!(f64::math::floor(1.3f64), 1.0f64); + assert_eq!(f64::math::floor(1.5f64), 1.0f64); + assert_eq!(f64::math::floor(1.7f64), 1.0f64); + assert_eq!(f64::math::floor(0.0f64), 0.0f64); + assert_eq!(f64::math::floor(-0.0f64), -0.0f64); + assert_eq!(f64::math::floor(-1.0f64), -1.0f64); + assert_eq!(f64::math::floor(-1.3f64), -2.0f64); + assert_eq!(f64::math::floor(-1.5f64), -2.0f64); + assert_eq!(f64::math::floor(-1.7f64), -2.0f64); } #[test] fn test_ceil() { - assert_approx_eq!(f64::math::ceil(1.0f64), 1.0f64); - assert_approx_eq!(f64::math::ceil(1.3f64), 2.0f64); - assert_approx_eq!(f64::math::ceil(1.5f64), 2.0f64); - assert_approx_eq!(f64::math::ceil(1.7f64), 2.0f64); - assert_approx_eq!(f64::math::ceil(0.0f64), 0.0f64); - assert_approx_eq!(f64::math::ceil(-0.0f64), -0.0f64); - assert_approx_eq!(f64::math::ceil(-1.0f64), -1.0f64); - assert_approx_eq!(f64::math::ceil(-1.3f64), -1.0f64); - assert_approx_eq!(f64::math::ceil(-1.5f64), -1.0f64); - assert_approx_eq!(f64::math::ceil(-1.7f64), -1.0f64); + assert_eq!(f64::math::ceil(1.0f64), 1.0f64); + assert_eq!(f64::math::ceil(1.3f64), 2.0f64); + assert_eq!(f64::math::ceil(1.5f64), 2.0f64); + assert_eq!(f64::math::ceil(1.7f64), 2.0f64); + assert_eq!(f64::math::ceil(0.0f64), 0.0f64); + assert_eq!(f64::math::ceil(-0.0f64), -0.0f64); + assert_eq!(f64::math::ceil(-1.0f64), -1.0f64); + assert_eq!(f64::math::ceil(-1.3f64), -1.0f64); + assert_eq!(f64::math::ceil(-1.5f64), -1.0f64); + assert_eq!(f64::math::ceil(-1.7f64), -1.0f64); } #[test] fn test_round() { - assert_approx_eq!(f64::math::round(2.5f64), 3.0f64); - assert_approx_eq!(f64::math::round(1.0f64), 1.0f64); - assert_approx_eq!(f64::math::round(1.3f64), 1.0f64); - assert_approx_eq!(f64::math::round(1.5f64), 2.0f64); - assert_approx_eq!(f64::math::round(1.7f64), 2.0f64); - assert_approx_eq!(f64::math::round(0.0f64), 0.0f64); - assert_approx_eq!(f64::math::round(-0.0f64), -0.0f64); - assert_approx_eq!(f64::math::round(-1.0f64), -1.0f64); - assert_approx_eq!(f64::math::round(-1.3f64), -1.0f64); - assert_approx_eq!(f64::math::round(-1.5f64), -2.0f64); - assert_approx_eq!(f64::math::round(-1.7f64), -2.0f64); + assert_eq!(f64::math::round(2.5f64), 3.0f64); + assert_eq!(f64::math::round(1.0f64), 1.0f64); + assert_eq!(f64::math::round(1.3f64), 1.0f64); + assert_eq!(f64::math::round(1.5f64), 2.0f64); + assert_eq!(f64::math::round(1.7f64), 2.0f64); + assert_eq!(f64::math::round(0.0f64), 0.0f64); + assert_eq!(f64::math::round(-0.0f64), -0.0f64); + assert_eq!(f64::math::round(-1.0f64), -1.0f64); + assert_eq!(f64::math::round(-1.3f64), -1.0f64); + assert_eq!(f64::math::round(-1.5f64), -2.0f64); + assert_eq!(f64::math::round(-1.7f64), -2.0f64); } #[test] fn test_round_ties_even() { - assert_approx_eq!(f64::math::round_ties_even(2.5f64), 2.0f64); - assert_approx_eq!(f64::math::round_ties_even(1.0f64), 1.0f64); - assert_approx_eq!(f64::math::round_ties_even(1.3f64), 1.0f64); - assert_approx_eq!(f64::math::round_ties_even(1.5f64), 2.0f64); - assert_approx_eq!(f64::math::round_ties_even(1.7f64), 2.0f64); - assert_approx_eq!(f64::math::round_ties_even(0.0f64), 0.0f64); - assert_approx_eq!(f64::math::round_ties_even(-0.0f64), -0.0f64); - assert_approx_eq!(f64::math::round_ties_even(-1.0f64), -1.0f64); - assert_approx_eq!(f64::math::round_ties_even(-1.3f64), -1.0f64); - assert_approx_eq!(f64::math::round_ties_even(-1.5f64), -2.0f64); - assert_approx_eq!(f64::math::round_ties_even(-1.7f64), -2.0f64); + assert_eq!(f64::math::round_ties_even(2.5f64), 2.0f64); + assert_eq!(f64::math::round_ties_even(1.0f64), 1.0f64); + assert_eq!(f64::math::round_ties_even(1.3f64), 1.0f64); + assert_eq!(f64::math::round_ties_even(1.5f64), 2.0f64); + assert_eq!(f64::math::round_ties_even(1.7f64), 2.0f64); + assert_eq!(f64::math::round_ties_even(0.0f64), 0.0f64); + assert_eq!(f64::math::round_ties_even(-0.0f64), -0.0f64); + assert_eq!(f64::math::round_ties_even(-1.0f64), -1.0f64); + assert_eq!(f64::math::round_ties_even(-1.3f64), -1.0f64); + assert_eq!(f64::math::round_ties_even(-1.5f64), -2.0f64); + assert_eq!(f64::math::round_ties_even(-1.7f64), -2.0f64); } #[test] fn test_trunc() { - assert_approx_eq!(f64::math::trunc(1.0f64), 1.0f64); - assert_approx_eq!(f64::math::trunc(1.3f64), 1.0f64); - assert_approx_eq!(f64::math::trunc(1.5f64), 1.0f64); - assert_approx_eq!(f64::math::trunc(1.7f64), 1.0f64); - assert_approx_eq!(f64::math::trunc(0.0f64), 0.0f64); - assert_approx_eq!(f64::math::trunc(-0.0f64), -0.0f64); - assert_approx_eq!(f64::math::trunc(-1.0f64), -1.0f64); - assert_approx_eq!(f64::math::trunc(-1.3f64), -1.0f64); - assert_approx_eq!(f64::math::trunc(-1.5f64), -1.0f64); - assert_approx_eq!(f64::math::trunc(-1.7f64), -1.0f64); + assert_eq!(f64::math::trunc(1.0f64), 1.0f64); + assert_eq!(f64::math::trunc(1.3f64), 1.0f64); + assert_eq!(f64::math::trunc(1.5f64), 1.0f64); + assert_eq!(f64::math::trunc(1.7f64), 1.0f64); + assert_eq!(f64::math::trunc(0.0f64), 0.0f64); + assert_eq!(f64::math::trunc(-0.0f64), -0.0f64); + assert_eq!(f64::math::trunc(-1.0f64), -1.0f64); + assert_eq!(f64::math::trunc(-1.3f64), -1.0f64); + assert_eq!(f64::math::trunc(-1.5f64), -1.0f64); + assert_eq!(f64::math::trunc(-1.7f64), -1.0f64); } #[test] fn test_fract() { - assert_approx_eq!(f64::math::fract(1.0f64), 0.0f64); - assert_approx_eq!(f64::math::fract(1.3f64), 0.3f64); - assert_approx_eq!(f64::math::fract(1.5f64), 0.5f64); - assert_approx_eq!(f64::math::fract(1.7f64), 0.7f64); - assert_approx_eq!(f64::math::fract(0.0f64), 0.0f64); - assert_approx_eq!(f64::math::fract(-0.0f64), -0.0f64); - assert_approx_eq!(f64::math::fract(-1.0f64), -0.0f64); - assert_approx_eq!(f64::math::fract(-1.3f64), -0.3f64); - assert_approx_eq!(f64::math::fract(-1.5f64), -0.5f64); - assert_approx_eq!(f64::math::fract(-1.7f64), -0.7f64); + assert_eq!(f64::math::fract(1.0f64), 0.0f64); + assert_eq!(f64::math::fract(1.3f64), 0.30000000000000004f64); + assert_eq!(f64::math::fract(1.5f64), 0.5f64); + assert_eq!(f64::math::fract(1.7f64), 0.7f64); + assert_eq!(f64::math::fract(0.0f64), 0.0f64); + assert_eq!(f64::math::fract(-0.0f64), -0.0f64); + assert_eq!(f64::math::fract(-1.0f64), -0.0f64); + assert_eq!(f64::math::fract(-1.3f64), -0.30000000000000004f64); + assert_eq!(f64::math::fract(-1.5f64), -0.5f64); + assert_eq!(f64::math::fract(-1.7f64), -0.69999999999999996f64); } #[test] @@ -391,10 +391,10 @@ fn test_mul_add() { let nan: f64 = f64::NAN; let inf: f64 = f64::INFINITY; let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(12.3f64.mul_add(4.5, 6.7), 62.05); - assert_approx_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.65); - assert_approx_eq!(0.0f64.mul_add(8.9, 1.2), 1.2); - assert_approx_eq!(3.4f64.mul_add(-0.0, 5.6), 5.6); + assert_eq!(12.3f64.mul_add(4.5, 6.7), 62.050000000000004); + assert_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.650000000000006); + assert_eq!(0.0f64.mul_add(8.9, 1.2), 1.2); + assert_eq!(3.4f64.mul_add(-0.0, 5.6), 5.6); 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); From 5446ba3c2d8766e3c6fcfe9e09c6e513c341dfc4 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 29 May 2025 14:15:17 +0000 Subject: [PATCH 16/17] float: Enable some f16 and f128 rounding tests on miri The rounding tests are now supported, so there is no longer any reason to skip these. --- library/coretests/tests/floats/f128.rs | 50 ++++++++------------------ library/coretests/tests/floats/f16.rs | 36 +++++++------------ 2 files changed, 26 insertions(+), 60 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index d417e715de3c4..0cee83d2599e0 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -1,12 +1,9 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(target_has_reliable_f128)] +use core::ops::{Add, Div, Mul, Sub}; use std::f128::consts; use std::num::FpCategory as Fp; -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -use std::ops::Rem; -use std::ops::{Add, Div, Mul, Sub}; // Note these tolerances make sense around zero, but not for more extreme exponents. @@ -49,47 +46,36 @@ fn test_num_f128() { assert_eq!(ten.sub(two), ten - two); assert_eq!(ten.mul(two), ten * two); assert_eq!(ten.div(two), ten / two); + #[cfg(any(miri, target_has_reliable_f128_math))] + assert_eq!(core::ops::Rem::rem(ten, two), ten % two); } // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_num_f128_rem() { - let ten = 10f128; - let two = 2f128; - assert_eq!(ten.rem(two), ten % two); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_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(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_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(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_minimum() { assert!(f128::NAN.minimum(2.0).is_nan()); assert!(2.0f128.minimum(f128::NAN).is_nan()); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_maximum() { assert!(f128::NAN.maximum(2.0).is_nan()); assert!(2.0f128.maximum(f128::NAN).is_nan()); @@ -246,7 +232,6 @@ fn test_classify() { } #[test] -#[cfg(not(miri))] #[cfg(target_has_reliable_f128_math)] fn test_floor() { assert_eq!(1.0f128.floor(), 1.0f128); @@ -262,8 +247,7 @@ fn test_floor() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_ceil() { assert_eq!(1.0f128.ceil(), 1.0f128); assert_eq!(1.3f128.ceil(), 2.0f128); @@ -278,8 +262,7 @@ fn test_ceil() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_round() { assert_eq!(2.5f128.round(), 3.0f128); assert_eq!(1.0f128.round(), 1.0f128); @@ -295,8 +278,7 @@ fn test_round() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_round_ties_even() { assert_eq!(2.5f128.round_ties_even(), 2.0f128); assert_eq!(1.0f128.round_ties_even(), 1.0f128); @@ -312,8 +294,7 @@ fn test_round_ties_even() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_trunc() { assert_eq!(1.0f128.trunc(), 1.0f128); assert_eq!(1.3f128.trunc(), 1.0f128); @@ -328,8 +309,7 @@ fn test_trunc() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_fract() { assert_eq!(1.0f128.fract(), 0.0f128); assert_eq!(1.3f128.fract(), 0.300000000000000000000000000000000039f128); @@ -344,8 +324,7 @@ fn test_fract() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_abs() { assert_eq!(f128::INFINITY.abs(), f128::INFINITY); assert_eq!(1f128.abs(), 1f128); @@ -463,8 +442,7 @@ fn test_mul_add() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] +#[cfg(any(miri, target_has_reliable_f128_math))] fn test_recip() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index dd0ad6cd41054..ff23be0d84167 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -50,32 +50,28 @@ fn test_num_f16() { // the intrinsics. #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_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(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_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(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_minimum() { assert!(f16::NAN.minimum(2.0).is_nan()); assert!(2.0f16.minimum(f16::NAN).is_nan()); } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_maximum() { assert!(f16::NAN.maximum(2.0).is_nan()); assert!(2.0f16.maximum(f16::NAN).is_nan()); @@ -232,8 +228,7 @@ fn test_classify() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_floor() { assert_eq!(1.0f16.floor(), 1.0f16); assert_eq!(1.3f16.floor(), 1.0f16); @@ -248,8 +243,7 @@ fn test_floor() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_ceil() { assert_eq!(1.0f16.ceil(), 1.0f16); assert_eq!(1.3f16.ceil(), 2.0f16); @@ -264,8 +258,7 @@ fn test_ceil() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_round() { assert_eq!(2.5f16.round(), 3.0f16); assert_eq!(1.0f16.round(), 1.0f16); @@ -281,8 +274,7 @@ fn test_round() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_round_ties_even() { assert_eq!(2.5f16.round_ties_even(), 2.0f16); assert_eq!(1.0f16.round_ties_even(), 1.0f16); @@ -298,8 +290,7 @@ fn test_round_ties_even() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_trunc() { assert_eq!(1.0f16.trunc(), 1.0f16); assert_eq!(1.3f16.trunc(), 1.0f16); @@ -314,8 +305,7 @@ fn test_trunc() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_fract() { assert_eq!(1.0f16.fract(), 0.0f16); assert_eq!(1.3f16.fract(), 0.2998f16); @@ -330,8 +320,7 @@ fn test_fract() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_abs() { assert_eq!(f16::INFINITY.abs(), f16::INFINITY); assert_eq!(1f16.abs(), 1f16); @@ -449,8 +438,7 @@ fn test_mul_add() { } #[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] +#[cfg(any(miri, target_has_reliable_f16_math))] fn test_recip() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; From 70cce1c76264cfa7006d296a81ff2ae01afe186a Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 29 May 2025 14:44:32 +0000 Subject: [PATCH 17/17] float: Use `assert_biteq!` where possible `assert_eq!` ignores the sign of zero, but for any tests involving zeros we do care about this sign. Replace `assert_eq!` with `assert_biteq!` everywhere possible for float tests to ensure we don't miss this. `assert_biteq!` is also updated to check equality on non-NaNs, to catch the unlikely case that bitwise equality works but our `==` implementation is broken. There is one notable output change: we were asserting that `(-0.0).fract()` and `(-1.0).fract()` both return -0.0, but both actually return +0.0. --- library/coretests/tests/floats/f128.rs | 267 +++++++++++++------------ library/coretests/tests/floats/f16.rs | 233 ++++++++++----------- library/coretests/tests/floats/f32.rs | 235 +++++++++++----------- library/coretests/tests/floats/f64.rs | 233 ++++++++++----------- library/coretests/tests/floats/mod.rs | 6 + 5 files changed, 492 insertions(+), 482 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index 0cee83d2599e0..01770f119df10 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -42,12 +42,12 @@ fn test_num_f128() { // function is available on all platforms. let ten = 10f128; let two = 2f128; - assert_eq!(ten.add(two), ten + two); - assert_eq!(ten.sub(two), ten - two); - assert_eq!(ten.mul(two), ten * two); - assert_eq!(ten.div(two), ten / two); + assert_biteq!(ten.add(two), ten + two); + assert_biteq!(ten.sub(two), ten - two); + assert_biteq!(ten.mul(two), ten * two); + assert_biteq!(ten.div(two), ten / two); #[cfg(any(miri, target_has_reliable_f128_math))] - assert_eq!(core::ops::Rem::rem(ten, two), ten % two); + assert_biteq!(core::ops::Rem::rem(ten, two), ten % two); } // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support @@ -56,15 +56,15 @@ fn test_num_f128() { #[test] #[cfg(any(miri, target_has_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); + assert_biteq!(f128::NAN.min(2.0), 2.0); + assert_biteq!(2.0f128.min(f128::NAN), 2.0); } #[test] #[cfg(any(miri, target_has_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); + assert_biteq!(f128::NAN.max(2.0), 2.0); + assert_biteq!(2.0f128.max(f128::NAN), 2.0); } #[test] @@ -122,7 +122,7 @@ fn test_neg_infinity() { #[test] fn test_zero() { let zero: f128 = 0.0f128; - assert_eq!(0.0, zero); + assert_biteq!(0.0, zero); assert!(!zero.is_infinite()); assert!(zero.is_finite()); assert!(zero.is_sign_positive()); @@ -136,6 +136,7 @@ fn test_zero() { fn test_neg_zero() { let neg_zero: f128 = -0.0; assert_eq!(0.0, neg_zero); + assert_biteq!(-0.0, neg_zero); assert!(!neg_zero.is_infinite()); assert!(neg_zero.is_finite()); assert!(!neg_zero.is_sign_positive()); @@ -148,7 +149,7 @@ fn test_neg_zero() { #[test] fn test_one() { let one: f128 = 1.0f128; - assert_eq!(1.0, one); + assert_biteq!(1.0, one); assert!(!one.is_infinite()); assert!(one.is_finite()); assert!(one.is_sign_positive()); @@ -234,105 +235,105 @@ fn test_classify() { #[test] #[cfg(target_has_reliable_f128_math)] fn test_floor() { - assert_eq!(1.0f128.floor(), 1.0f128); - assert_eq!(1.3f128.floor(), 1.0f128); - assert_eq!(1.5f128.floor(), 1.0f128); - assert_eq!(1.7f128.floor(), 1.0f128); - assert_eq!(0.0f128.floor(), 0.0f128); - assert_eq!((-0.0f128).floor(), -0.0f128); - assert_eq!((-1.0f128).floor(), -1.0f128); - assert_eq!((-1.3f128).floor(), -2.0f128); - assert_eq!((-1.5f128).floor(), -2.0f128); - assert_eq!((-1.7f128).floor(), -2.0f128); + assert_biteq!(1.0f128.floor(), 1.0f128); + assert_biteq!(1.3f128.floor(), 1.0f128); + assert_biteq!(1.5f128.floor(), 1.0f128); + assert_biteq!(1.7f128.floor(), 1.0f128); + assert_biteq!(0.0f128.floor(), 0.0f128); + assert_biteq!((-0.0f128).floor(), -0.0f128); + assert_biteq!((-1.0f128).floor(), -1.0f128); + assert_biteq!((-1.3f128).floor(), -2.0f128); + assert_biteq!((-1.5f128).floor(), -2.0f128); + assert_biteq!((-1.7f128).floor(), -2.0f128); } #[test] #[cfg(any(miri, target_has_reliable_f128_math))] fn test_ceil() { - assert_eq!(1.0f128.ceil(), 1.0f128); - assert_eq!(1.3f128.ceil(), 2.0f128); - assert_eq!(1.5f128.ceil(), 2.0f128); - assert_eq!(1.7f128.ceil(), 2.0f128); - assert_eq!(0.0f128.ceil(), 0.0f128); - assert_eq!((-0.0f128).ceil(), -0.0f128); - assert_eq!((-1.0f128).ceil(), -1.0f128); - assert_eq!((-1.3f128).ceil(), -1.0f128); - assert_eq!((-1.5f128).ceil(), -1.0f128); - assert_eq!((-1.7f128).ceil(), -1.0f128); + assert_biteq!(1.0f128.ceil(), 1.0f128); + assert_biteq!(1.3f128.ceil(), 2.0f128); + assert_biteq!(1.5f128.ceil(), 2.0f128); + assert_biteq!(1.7f128.ceil(), 2.0f128); + assert_biteq!(0.0f128.ceil(), 0.0f128); + assert_biteq!((-0.0f128).ceil(), -0.0f128); + assert_biteq!((-1.0f128).ceil(), -1.0f128); + assert_biteq!((-1.3f128).ceil(), -1.0f128); + assert_biteq!((-1.5f128).ceil(), -1.0f128); + assert_biteq!((-1.7f128).ceil(), -1.0f128); } #[test] #[cfg(any(miri, target_has_reliable_f128_math))] fn test_round() { - assert_eq!(2.5f128.round(), 3.0f128); - assert_eq!(1.0f128.round(), 1.0f128); - assert_eq!(1.3f128.round(), 1.0f128); - assert_eq!(1.5f128.round(), 2.0f128); - assert_eq!(1.7f128.round(), 2.0f128); - assert_eq!(0.0f128.round(), 0.0f128); - assert_eq!((-0.0f128).round(), -0.0f128); - assert_eq!((-1.0f128).round(), -1.0f128); - assert_eq!((-1.3f128).round(), -1.0f128); - assert_eq!((-1.5f128).round(), -2.0f128); - assert_eq!((-1.7f128).round(), -2.0f128); + assert_biteq!(2.5f128.round(), 3.0f128); + assert_biteq!(1.0f128.round(), 1.0f128); + assert_biteq!(1.3f128.round(), 1.0f128); + assert_biteq!(1.5f128.round(), 2.0f128); + assert_biteq!(1.7f128.round(), 2.0f128); + assert_biteq!(0.0f128.round(), 0.0f128); + assert_biteq!((-0.0f128).round(), -0.0f128); + assert_biteq!((-1.0f128).round(), -1.0f128); + assert_biteq!((-1.3f128).round(), -1.0f128); + assert_biteq!((-1.5f128).round(), -2.0f128); + assert_biteq!((-1.7f128).round(), -2.0f128); } #[test] #[cfg(any(miri, target_has_reliable_f128_math))] fn test_round_ties_even() { - assert_eq!(2.5f128.round_ties_even(), 2.0f128); - assert_eq!(1.0f128.round_ties_even(), 1.0f128); - assert_eq!(1.3f128.round_ties_even(), 1.0f128); - assert_eq!(1.5f128.round_ties_even(), 2.0f128); - assert_eq!(1.7f128.round_ties_even(), 2.0f128); - assert_eq!(0.0f128.round_ties_even(), 0.0f128); - assert_eq!((-0.0f128).round_ties_even(), -0.0f128); - assert_eq!((-1.0f128).round_ties_even(), -1.0f128); - assert_eq!((-1.3f128).round_ties_even(), -1.0f128); - assert_eq!((-1.5f128).round_ties_even(), -2.0f128); - assert_eq!((-1.7f128).round_ties_even(), -2.0f128); + assert_biteq!(2.5f128.round_ties_even(), 2.0f128); + assert_biteq!(1.0f128.round_ties_even(), 1.0f128); + assert_biteq!(1.3f128.round_ties_even(), 1.0f128); + assert_biteq!(1.5f128.round_ties_even(), 2.0f128); + assert_biteq!(1.7f128.round_ties_even(), 2.0f128); + assert_biteq!(0.0f128.round_ties_even(), 0.0f128); + assert_biteq!((-0.0f128).round_ties_even(), -0.0f128); + assert_biteq!((-1.0f128).round_ties_even(), -1.0f128); + assert_biteq!((-1.3f128).round_ties_even(), -1.0f128); + assert_biteq!((-1.5f128).round_ties_even(), -2.0f128); + assert_biteq!((-1.7f128).round_ties_even(), -2.0f128); } #[test] #[cfg(any(miri, target_has_reliable_f128_math))] fn test_trunc() { - assert_eq!(1.0f128.trunc(), 1.0f128); - assert_eq!(1.3f128.trunc(), 1.0f128); - assert_eq!(1.5f128.trunc(), 1.0f128); - assert_eq!(1.7f128.trunc(), 1.0f128); - assert_eq!(0.0f128.trunc(), 0.0f128); - assert_eq!((-0.0f128).trunc(), -0.0f128); - assert_eq!((-1.0f128).trunc(), -1.0f128); - assert_eq!((-1.3f128).trunc(), -1.0f128); - assert_eq!((-1.5f128).trunc(), -1.0f128); - assert_eq!((-1.7f128).trunc(), -1.0f128); + assert_biteq!(1.0f128.trunc(), 1.0f128); + assert_biteq!(1.3f128.trunc(), 1.0f128); + assert_biteq!(1.5f128.trunc(), 1.0f128); + assert_biteq!(1.7f128.trunc(), 1.0f128); + assert_biteq!(0.0f128.trunc(), 0.0f128); + assert_biteq!((-0.0f128).trunc(), -0.0f128); + assert_biteq!((-1.0f128).trunc(), -1.0f128); + assert_biteq!((-1.3f128).trunc(), -1.0f128); + assert_biteq!((-1.5f128).trunc(), -1.0f128); + assert_biteq!((-1.7f128).trunc(), -1.0f128); } #[test] #[cfg(any(miri, target_has_reliable_f128_math))] fn test_fract() { - assert_eq!(1.0f128.fract(), 0.0f128); - assert_eq!(1.3f128.fract(), 0.300000000000000000000000000000000039f128); - assert_eq!(1.5f128.fract(), 0.5f128); - assert_eq!(1.7f128.fract(), 0.7f128); - assert_eq!(0.0f128.fract(), 0.0f128); - assert_eq!((-0.0f128).fract(), -0.0f128); - assert_eq!((-1.0f128).fract(), -0.0f128); - assert_eq!((-1.3f128).fract(), -0.300000000000000000000000000000000039f128); - assert_eq!((-1.5f128).fract(), -0.5f128); - assert_eq!((-1.7f128).fract(), -0.699999999999999999999999999999999961f128); + assert_biteq!(1.0f128.fract(), 0.0f128); + assert_biteq!(1.3f128.fract(), 0.300000000000000000000000000000000039f128); + assert_biteq!(1.5f128.fract(), 0.5f128); + assert_biteq!(1.7f128.fract(), 0.7f128); + assert_biteq!(0.0f128.fract(), 0.0f128); + assert_biteq!((-0.0f128).fract(), 0.0f128); + assert_biteq!((-1.0f128).fract(), 0.0f128); + assert_biteq!((-1.3f128).fract(), -0.300000000000000000000000000000000039f128); + assert_biteq!((-1.5f128).fract(), -0.5f128); + assert_biteq!((-1.7f128).fract(), -0.699999999999999999999999999999999961f128); } #[test] #[cfg(any(miri, target_has_reliable_f128_math))] fn test_abs() { - assert_eq!(f128::INFINITY.abs(), f128::INFINITY); - assert_eq!(1f128.abs(), 1f128); - assert_eq!(0f128.abs(), 0f128); - assert_eq!((-0f128).abs(), 0f128); - assert_eq!((-1f128).abs(), 1f128); - assert_eq!(f128::NEG_INFINITY.abs(), f128::INFINITY); - assert_eq!((1f128 / f128::NEG_INFINITY).abs(), 0f128); + assert_biteq!(f128::INFINITY.abs(), f128::INFINITY); + assert_biteq!(1f128.abs(), 1f128); + assert_biteq!(0f128.abs(), 0f128); + assert_biteq!((-0f128).abs(), 0f128); + assert_biteq!((-1f128).abs(), 1f128); + assert_biteq!(f128::NEG_INFINITY.abs(), f128::INFINITY); + assert_biteq!((1f128 / f128::NEG_INFINITY).abs(), 0f128); assert!(f128::NAN.abs().is_nan()); } @@ -430,15 +431,15 @@ fn test_mul_add() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(12.3f128.mul_add(4.5, 6.7), 62.0500000000000000000000000000000037); - assert_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.6500000000000000000000000000000049); - assert_eq!(0.0f128.mul_add(8.9, 1.2), 1.2); - assert_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6); + assert_biteq!(12.3f128.mul_add(4.5, 6.7), 62.0500000000000000000000000000000037); + assert_biteq!((-12.3f128).mul_add(-4.5, -6.7), 48.6500000000000000000000000000000049); + assert_biteq!(0.0f128.mul_add(8.9, 1.2), 1.2); + assert_biteq!(3.4f128.mul_add(-0.0, 5.6), 5.6); 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); + assert_biteq!(inf.mul_add(7.8, 9.0), inf); + assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_biteq!(8.9f128.mul_add(inf, 3.2), inf); + assert_biteq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); } #[test] @@ -447,18 +448,18 @@ fn test_recip() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.recip(), 1.0); - assert_eq!(2.0f128.recip(), 0.5); - assert_eq!((-0.4f128).recip(), -2.5); - assert_eq!(0.0f128.recip(), inf); + assert_biteq!(1.0f128.recip(), 1.0); + assert_biteq!(2.0f128.recip(), 0.5); + assert_biteq!((-0.4f128).recip(), -2.5); + assert_biteq!(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); + assert_biteq!(inf.recip(), 0.0); + assert_biteq!(neg_inf.recip(), -0.0); } #[test] @@ -468,13 +469,13 @@ 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_biteq!(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_biteq!(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); + assert_biteq!(inf.powi(3), inf); + assert_biteq!(neg_inf.powi(2), inf); } #[test] @@ -484,10 +485,10 @@ 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); + assert_biteq!((-0.0f128).sqrt(), -0.0); + assert_biteq!(0.0f128.sqrt(), 0.0); + assert_biteq!(1.0f128.sqrt(), 1.0); + assert_biteq!(f128::INFINITY.sqrt(), f128::INFINITY); } #[test] @@ -496,13 +497,13 @@ fn test_to_degrees() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(0.0f128.to_degrees(), 0.0); + assert_biteq!(0.0f128.to_degrees(), 0.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); - assert_eq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703); + assert_biteq!(inf.to_degrees(), inf); + assert_biteq!(neg_inf.to_degrees(), neg_inf); + assert_biteq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703); } #[test] @@ -511,15 +512,15 @@ fn test_to_radians() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(0.0f128.to_radians(), 0.0); + assert_biteq!(0.0f128.to_radians(), 0.0); 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, TOL_PRECISE); assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); + assert_biteq!(inf.to_radians(), inf); + assert_biteq!(neg_inf.to_radians(), neg_inf); } #[test] @@ -528,10 +529,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_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); - assert_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5); - assert_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); - assert_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); + assert_biteq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); + assert_biteq!(f128::from_bits(0x40029000000000000000000000000000), 12.5); + assert_biteq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); + assert_biteq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); // 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 @@ -727,26 +728,26 @@ fn test_algebraic() { #[test] fn test_from() { - assert_eq!(f128::from(false), 0.0); - assert_eq!(f128::from(true), 1.0); - assert_eq!(f128::from(u8::MIN), 0.0); - assert_eq!(f128::from(42_u8), 42.0); - assert_eq!(f128::from(u8::MAX), 255.0); - assert_eq!(f128::from(i8::MIN), -128.0); - assert_eq!(f128::from(42_i8), 42.0); - assert_eq!(f128::from(i8::MAX), 127.0); - assert_eq!(f128::from(u16::MIN), 0.0); - assert_eq!(f128::from(42_u16), 42.0); - assert_eq!(f128::from(u16::MAX), 65535.0); - assert_eq!(f128::from(i16::MIN), -32768.0); - assert_eq!(f128::from(42_i16), 42.0); - assert_eq!(f128::from(i16::MAX), 32767.0); - assert_eq!(f128::from(u32::MIN), 0.0); - assert_eq!(f128::from(42_u32), 42.0); - assert_eq!(f128::from(u32::MAX), 4294967295.0); - assert_eq!(f128::from(i32::MIN), -2147483648.0); - assert_eq!(f128::from(42_i32), 42.0); - assert_eq!(f128::from(i32::MAX), 2147483647.0); + assert_biteq!(f128::from(false), 0.0); + assert_biteq!(f128::from(true), 1.0); + assert_biteq!(f128::from(u8::MIN), 0.0); + assert_biteq!(f128::from(42_u8), 42.0); + assert_biteq!(f128::from(u8::MAX), 255.0); + assert_biteq!(f128::from(i8::MIN), -128.0); + assert_biteq!(f128::from(42_i8), 42.0); + assert_biteq!(f128::from(i8::MAX), 127.0); + assert_biteq!(f128::from(u16::MIN), 0.0); + assert_biteq!(f128::from(42_u16), 42.0); + assert_biteq!(f128::from(u16::MAX), 65535.0); + assert_biteq!(f128::from(i16::MIN), -32768.0); + assert_biteq!(f128::from(42_i16), 42.0); + assert_biteq!(f128::from(i16::MAX), 32767.0); + assert_biteq!(f128::from(u32::MIN), 0.0); + assert_biteq!(f128::from(42_u32), 42.0); + assert_biteq!(f128::from(u32::MAX), 4294967295.0); + assert_biteq!(f128::from(i32::MIN), -2147483648.0); + assert_biteq!(f128::from(42_i32), 42.0); + assert_biteq!(f128::from(i32::MAX), 2147483647.0); // FIXME(f16_f128): Uncomment these tests once the From<{u64,i64}> impls are added. // assert_eq!(f128::from(u64::MIN), 0.0); // assert_eq!(f128::from(42_u64), 42.0); diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index ff23be0d84167..98674834a3e3a 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -52,15 +52,15 @@ fn test_num_f16() { #[test] #[cfg(any(miri, target_has_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); + assert_biteq!(f16::NAN.min(2.0), 2.0); + assert_biteq!(2.0f16.min(f16::NAN), 2.0); } #[test] #[cfg(any(miri, target_has_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); + assert_biteq!(f16::NAN.max(2.0), 2.0); + assert_biteq!(2.0f16.max(f16::NAN), 2.0); } #[test] @@ -118,7 +118,7 @@ fn test_neg_infinity() { #[test] fn test_zero() { let zero: f16 = 0.0f16; - assert_eq!(0.0, zero); + assert_biteq!(0.0, zero); assert!(!zero.is_infinite()); assert!(zero.is_finite()); assert!(zero.is_sign_positive()); @@ -132,6 +132,7 @@ fn test_zero() { fn test_neg_zero() { let neg_zero: f16 = -0.0; assert_eq!(0.0, neg_zero); + assert_biteq!(-0.0, neg_zero); assert!(!neg_zero.is_infinite()); assert!(neg_zero.is_finite()); assert!(!neg_zero.is_sign_positive()); @@ -144,7 +145,7 @@ fn test_neg_zero() { #[test] fn test_one() { let one: f16 = 1.0f16; - assert_eq!(1.0, one); + assert_biteq!(1.0, one); assert!(!one.is_infinite()); assert!(one.is_finite()); assert!(one.is_sign_positive()); @@ -230,105 +231,105 @@ fn test_classify() { #[test] #[cfg(any(miri, target_has_reliable_f16_math))] fn test_floor() { - assert_eq!(1.0f16.floor(), 1.0f16); - assert_eq!(1.3f16.floor(), 1.0f16); - assert_eq!(1.5f16.floor(), 1.0f16); - assert_eq!(1.7f16.floor(), 1.0f16); - assert_eq!(0.0f16.floor(), 0.0f16); - assert_eq!((-0.0f16).floor(), -0.0f16); - assert_eq!((-1.0f16).floor(), -1.0f16); - assert_eq!((-1.3f16).floor(), -2.0f16); - assert_eq!((-1.5f16).floor(), -2.0f16); - assert_eq!((-1.7f16).floor(), -2.0f16); + assert_biteq!(1.0f16.floor(), 1.0f16); + assert_biteq!(1.3f16.floor(), 1.0f16); + assert_biteq!(1.5f16.floor(), 1.0f16); + assert_biteq!(1.7f16.floor(), 1.0f16); + assert_biteq!(0.0f16.floor(), 0.0f16); + assert_biteq!((-0.0f16).floor(), -0.0f16); + assert_biteq!((-1.0f16).floor(), -1.0f16); + assert_biteq!((-1.3f16).floor(), -2.0f16); + assert_biteq!((-1.5f16).floor(), -2.0f16); + assert_biteq!((-1.7f16).floor(), -2.0f16); } #[test] #[cfg(any(miri, target_has_reliable_f16_math))] fn test_ceil() { - assert_eq!(1.0f16.ceil(), 1.0f16); - assert_eq!(1.3f16.ceil(), 2.0f16); - assert_eq!(1.5f16.ceil(), 2.0f16); - assert_eq!(1.7f16.ceil(), 2.0f16); - assert_eq!(0.0f16.ceil(), 0.0f16); - assert_eq!((-0.0f16).ceil(), -0.0f16); - assert_eq!((-1.0f16).ceil(), -1.0f16); - assert_eq!((-1.3f16).ceil(), -1.0f16); - assert_eq!((-1.5f16).ceil(), -1.0f16); - assert_eq!((-1.7f16).ceil(), -1.0f16); + assert_biteq!(1.0f16.ceil(), 1.0f16); + assert_biteq!(1.3f16.ceil(), 2.0f16); + assert_biteq!(1.5f16.ceil(), 2.0f16); + assert_biteq!(1.7f16.ceil(), 2.0f16); + assert_biteq!(0.0f16.ceil(), 0.0f16); + assert_biteq!((-0.0f16).ceil(), -0.0f16); + assert_biteq!((-1.0f16).ceil(), -1.0f16); + assert_biteq!((-1.3f16).ceil(), -1.0f16); + assert_biteq!((-1.5f16).ceil(), -1.0f16); + assert_biteq!((-1.7f16).ceil(), -1.0f16); } #[test] #[cfg(any(miri, target_has_reliable_f16_math))] fn test_round() { - assert_eq!(2.5f16.round(), 3.0f16); - assert_eq!(1.0f16.round(), 1.0f16); - assert_eq!(1.3f16.round(), 1.0f16); - assert_eq!(1.5f16.round(), 2.0f16); - assert_eq!(1.7f16.round(), 2.0f16); - assert_eq!(0.0f16.round(), 0.0f16); - assert_eq!((-0.0f16).round(), -0.0f16); - assert_eq!((-1.0f16).round(), -1.0f16); - assert_eq!((-1.3f16).round(), -1.0f16); - assert_eq!((-1.5f16).round(), -2.0f16); - assert_eq!((-1.7f16).round(), -2.0f16); + assert_biteq!(2.5f16.round(), 3.0f16); + assert_biteq!(1.0f16.round(), 1.0f16); + assert_biteq!(1.3f16.round(), 1.0f16); + assert_biteq!(1.5f16.round(), 2.0f16); + assert_biteq!(1.7f16.round(), 2.0f16); + assert_biteq!(0.0f16.round(), 0.0f16); + assert_biteq!((-0.0f16).round(), -0.0f16); + assert_biteq!((-1.0f16).round(), -1.0f16); + assert_biteq!((-1.3f16).round(), -1.0f16); + assert_biteq!((-1.5f16).round(), -2.0f16); + assert_biteq!((-1.7f16).round(), -2.0f16); } #[test] #[cfg(any(miri, target_has_reliable_f16_math))] fn test_round_ties_even() { - assert_eq!(2.5f16.round_ties_even(), 2.0f16); - assert_eq!(1.0f16.round_ties_even(), 1.0f16); - assert_eq!(1.3f16.round_ties_even(), 1.0f16); - assert_eq!(1.5f16.round_ties_even(), 2.0f16); - assert_eq!(1.7f16.round_ties_even(), 2.0f16); - assert_eq!(0.0f16.round_ties_even(), 0.0f16); - assert_eq!((-0.0f16).round_ties_even(), -0.0f16); - assert_eq!((-1.0f16).round_ties_even(), -1.0f16); - assert_eq!((-1.3f16).round_ties_even(), -1.0f16); - assert_eq!((-1.5f16).round_ties_even(), -2.0f16); - assert_eq!((-1.7f16).round_ties_even(), -2.0f16); + assert_biteq!(2.5f16.round_ties_even(), 2.0f16); + assert_biteq!(1.0f16.round_ties_even(), 1.0f16); + assert_biteq!(1.3f16.round_ties_even(), 1.0f16); + assert_biteq!(1.5f16.round_ties_even(), 2.0f16); + assert_biteq!(1.7f16.round_ties_even(), 2.0f16); + assert_biteq!(0.0f16.round_ties_even(), 0.0f16); + assert_biteq!((-0.0f16).round_ties_even(), -0.0f16); + assert_biteq!((-1.0f16).round_ties_even(), -1.0f16); + assert_biteq!((-1.3f16).round_ties_even(), -1.0f16); + assert_biteq!((-1.5f16).round_ties_even(), -2.0f16); + assert_biteq!((-1.7f16).round_ties_even(), -2.0f16); } #[test] #[cfg(any(miri, target_has_reliable_f16_math))] fn test_trunc() { - assert_eq!(1.0f16.trunc(), 1.0f16); - assert_eq!(1.3f16.trunc(), 1.0f16); - assert_eq!(1.5f16.trunc(), 1.0f16); - assert_eq!(1.7f16.trunc(), 1.0f16); - assert_eq!(0.0f16.trunc(), 0.0f16); - assert_eq!((-0.0f16).trunc(), -0.0f16); - assert_eq!((-1.0f16).trunc(), -1.0f16); - assert_eq!((-1.3f16).trunc(), -1.0f16); - assert_eq!((-1.5f16).trunc(), -1.0f16); - assert_eq!((-1.7f16).trunc(), -1.0f16); + assert_biteq!(1.0f16.trunc(), 1.0f16); + assert_biteq!(1.3f16.trunc(), 1.0f16); + assert_biteq!(1.5f16.trunc(), 1.0f16); + assert_biteq!(1.7f16.trunc(), 1.0f16); + assert_biteq!(0.0f16.trunc(), 0.0f16); + assert_biteq!((-0.0f16).trunc(), -0.0f16); + assert_biteq!((-1.0f16).trunc(), -1.0f16); + assert_biteq!((-1.3f16).trunc(), -1.0f16); + assert_biteq!((-1.5f16).trunc(), -1.0f16); + assert_biteq!((-1.7f16).trunc(), -1.0f16); } #[test] #[cfg(any(miri, target_has_reliable_f16_math))] fn test_fract() { - assert_eq!(1.0f16.fract(), 0.0f16); - assert_eq!(1.3f16.fract(), 0.2998f16); - assert_eq!(1.5f16.fract(), 0.5f16); - assert_eq!(1.7f16.fract(), 0.7f16); - assert_eq!(0.0f16.fract(), 0.0f16); - assert_eq!((-0.0f16).fract(), -0.0f16); - assert_eq!((-1.0f16).fract(), -0.0f16); - assert_eq!((-1.3f16).fract(), -0.2998f16); - assert_eq!((-1.5f16).fract(), -0.5f16); - assert_eq!((-1.7f16).fract(), -0.7f16); + assert_biteq!(1.0f16.fract(), 0.0f16); + assert_biteq!(1.3f16.fract(), 0.2998f16); + assert_biteq!(1.5f16.fract(), 0.5f16); + assert_biteq!(1.7f16.fract(), 0.7f16); + assert_biteq!(0.0f16.fract(), 0.0f16); + assert_biteq!((-0.0f16).fract(), 0.0f16); + assert_biteq!((-1.0f16).fract(), 0.0f16); + assert_biteq!((-1.3f16).fract(), -0.2998f16); + assert_biteq!((-1.5f16).fract(), -0.5f16); + assert_biteq!((-1.7f16).fract(), -0.7f16); } #[test] #[cfg(any(miri, target_has_reliable_f16_math))] fn test_abs() { - assert_eq!(f16::INFINITY.abs(), f16::INFINITY); - assert_eq!(1f16.abs(), 1f16); - assert_eq!(0f16.abs(), 0f16); - assert_eq!((-0f16).abs(), 0f16); - assert_eq!((-1f16).abs(), 1f16); - assert_eq!(f16::NEG_INFINITY.abs(), f16::INFINITY); - assert_eq!((1f16 / f16::NEG_INFINITY).abs(), 0f16); + assert_biteq!(f16::INFINITY.abs(), f16::INFINITY); + assert_biteq!(1f16.abs(), 1f16); + assert_biteq!(0f16.abs(), 0f16); + assert_biteq!((-0f16).abs(), 0f16); + assert_biteq!((-1f16).abs(), 1f16); + assert_biteq!(f16::NEG_INFINITY.abs(), f16::INFINITY); + assert_biteq!((1f16 / f16::NEG_INFINITY).abs(), 0f16); assert!(f16::NAN.abs().is_nan()); } @@ -426,15 +427,15 @@ fn test_mul_add() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(12.3f16.mul_add(4.5, 6.7), 62.031); - assert_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.625); - assert_eq!(0.0f16.mul_add(8.9, 1.2), 1.2); - assert_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6); + assert_biteq!(12.3f16.mul_add(4.5, 6.7), 62.031); + assert_biteq!((-12.3f16).mul_add(-4.5, -6.7), 48.625); + assert_biteq!(0.0f16.mul_add(8.9, 1.2), 1.2); + assert_biteq!(3.4f16.mul_add(-0.0, 5.6), 5.6); 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); + assert_biteq!(inf.mul_add(7.8, 9.0), inf); + assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_biteq!(8.9f16.mul_add(inf, 3.2), inf); + assert_biteq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); } #[test] @@ -443,14 +444,14 @@ fn test_recip() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(1.0f16.recip(), 1.0); - assert_eq!(2.0f16.recip(), 0.5); - assert_eq!((-0.4f16).recip(), -2.5); - assert_eq!(0.0f16.recip(), inf); + assert_biteq!(1.0f16.recip(), 1.0); + assert_biteq!(2.0f16.recip(), 0.5); + assert_biteq!((-0.4f16).recip(), -2.5); + assert_biteq!(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); + assert_biteq!(inf.recip(), 0.0); + assert_biteq!(neg_inf.recip(), -0.0); } #[test] @@ -460,13 +461,13 @@ fn test_powi() { 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_biteq!(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_biteq!(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); + assert_biteq!(inf.powi(3), inf); + assert_biteq!(neg_inf.powi(2), inf); } #[test] @@ -476,10 +477,10 @@ 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); + assert_biteq!((-0.0f16).sqrt(), -0.0); + assert_biteq!(0.0f16.sqrt(), 0.0); + assert_biteq!(1.0f16.sqrt(), 1.0); + assert_biteq!(f16::INFINITY.sqrt(), f16::INFINITY); } #[test] @@ -488,13 +489,13 @@ fn test_to_degrees() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(0.0f16.to_degrees(), 0.0); + assert_biteq!(0.0f16.to_degrees(), 0.0); 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); - assert_eq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703); + assert_biteq!(inf.to_degrees(), inf); + assert_biteq!(neg_inf.to_degrees(), neg_inf); + assert_biteq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703); } #[test] @@ -503,13 +504,13 @@ fn test_to_radians() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(0.0f16.to_radians(), 0.0); + assert_biteq!(0.0f16.to_radians(), 0.0); 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); + assert_biteq!(inf.to_radians(), inf); + assert_biteq!(neg_inf.to_radians(), neg_inf); } #[test] @@ -518,10 +519,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_eq!(f16::from_bits(0x3c00), 1.0); - assert_eq!(f16::from_bits(0x4a40), 12.5); - assert_eq!(f16::from_bits(0x6539), 1337.0); - assert_eq!(f16::from_bits(0xcb20), -14.25); + assert_biteq!(f16::from_bits(0x3c00), 1.0); + assert_biteq!(f16::from_bits(0x4a40), 12.5); + assert_biteq!(f16::from_bits(0x6539), 1337.0); + assert_biteq!(f16::from_bits(0xcb20), -14.25); // Check that NaNs roundtrip their bits regardless of signaling-ness let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; @@ -719,12 +720,12 @@ fn test_algebraic() { #[test] fn test_from() { - assert_eq!(f16::from(false), 0.0); - assert_eq!(f16::from(true), 1.0); - assert_eq!(f16::from(u8::MIN), 0.0); - assert_eq!(f16::from(42_u8), 42.0); - assert_eq!(f16::from(u8::MAX), 255.0); - assert_eq!(f16::from(i8::MIN), -128.0); - assert_eq!(f16::from(42_i8), 42.0); - assert_eq!(f16::from(i8::MAX), 127.0); + assert_biteq!(f16::from(false), 0.0); + assert_biteq!(f16::from(true), 1.0); + assert_biteq!(f16::from(u8::MIN), 0.0); + assert_biteq!(f16::from(42_u8), 42.0); + assert_biteq!(f16::from(u8::MAX), 255.0); + assert_biteq!(f16::from(i8::MIN), -128.0); + assert_biteq!(f16::from(42_i8), 42.0); + assert_biteq!(f16::from(i8::MAX), 127.0); } diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 17e903ebebd5d..4e6509ead2ba8 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -30,14 +30,14 @@ fn test_num_f32() { #[test] fn test_min_nan() { - assert_eq!(f32::NAN.min(2.0), 2.0); - assert_eq!(2.0f32.min(f32::NAN), 2.0); + assert_biteq!(f32::NAN.min(2.0), 2.0); + assert_biteq!(2.0f32.min(f32::NAN), 2.0); } #[test] fn test_max_nan() { - assert_eq!(f32::NAN.max(2.0), 2.0); - assert_eq!(2.0f32.max(f32::NAN), 2.0); + assert_biteq!(f32::NAN.max(2.0), 2.0); + assert_biteq!(2.0f32.max(f32::NAN), 2.0); } #[test] @@ -93,7 +93,7 @@ fn test_neg_infinity() { #[test] fn test_zero() { let zero: f32 = 0.0f32; - assert_eq!(0.0, zero); + assert_biteq!(0.0, zero); assert!(!zero.is_infinite()); assert!(zero.is_finite()); assert!(zero.is_sign_positive()); @@ -107,6 +107,7 @@ fn test_zero() { fn test_neg_zero() { let neg_zero: f32 = -0.0; assert_eq!(0.0, neg_zero); + assert_biteq!(-0.0, neg_zero); assert!(!neg_zero.is_infinite()); assert!(neg_zero.is_finite()); assert!(!neg_zero.is_sign_positive()); @@ -119,7 +120,7 @@ fn test_neg_zero() { #[test] fn test_one() { let one: f32 = 1.0f32; - assert_eq!(1.0, one); + assert_biteq!(1.0, one); assert!(!one.is_infinite()); assert!(one.is_finite()); assert!(one.is_sign_positive()); @@ -204,111 +205,111 @@ fn test_classify() { #[test] fn test_floor() { - assert_eq!(f32::math::floor(1.0f32), 1.0f32); - assert_eq!(f32::math::floor(1.3f32), 1.0f32); - assert_eq!(f32::math::floor(1.5f32), 1.0f32); - assert_eq!(f32::math::floor(1.7f32), 1.0f32); - assert_eq!(f32::math::floor(0.0f32), 0.0f32); - assert_eq!(f32::math::floor(-0.0f32), -0.0f32); - assert_eq!(f32::math::floor(-1.0f32), -1.0f32); - assert_eq!(f32::math::floor(-1.3f32), -2.0f32); - assert_eq!(f32::math::floor(-1.5f32), -2.0f32); - assert_eq!(f32::math::floor(-1.7f32), -2.0f32); + assert_biteq!(f32::math::floor(1.0f32), 1.0f32); + assert_biteq!(f32::math::floor(1.3f32), 1.0f32); + assert_biteq!(f32::math::floor(1.5f32), 1.0f32); + assert_biteq!(f32::math::floor(1.7f32), 1.0f32); + assert_biteq!(f32::math::floor(0.0f32), 0.0f32); + assert_biteq!(f32::math::floor(-0.0f32), -0.0f32); + assert_biteq!(f32::math::floor(-1.0f32), -1.0f32); + assert_biteq!(f32::math::floor(-1.3f32), -2.0f32); + assert_biteq!(f32::math::floor(-1.5f32), -2.0f32); + assert_biteq!(f32::math::floor(-1.7f32), -2.0f32); } #[test] fn test_ceil() { - assert_eq!(f32::math::ceil(1.0f32), 1.0f32); - assert_eq!(f32::math::ceil(1.3f32), 2.0f32); - assert_eq!(f32::math::ceil(1.5f32), 2.0f32); - assert_eq!(f32::math::ceil(1.7f32), 2.0f32); - assert_eq!(f32::math::ceil(0.0f32), 0.0f32); - assert_eq!(f32::math::ceil(-0.0f32), -0.0f32); - assert_eq!(f32::math::ceil(-1.0f32), -1.0f32); - assert_eq!(f32::math::ceil(-1.3f32), -1.0f32); - assert_eq!(f32::math::ceil(-1.5f32), -1.0f32); - assert_eq!(f32::math::ceil(-1.7f32), -1.0f32); + assert_biteq!(f32::math::ceil(1.0f32), 1.0f32); + assert_biteq!(f32::math::ceil(1.3f32), 2.0f32); + assert_biteq!(f32::math::ceil(1.5f32), 2.0f32); + assert_biteq!(f32::math::ceil(1.7f32), 2.0f32); + assert_biteq!(f32::math::ceil(0.0f32), 0.0f32); + assert_biteq!(f32::math::ceil(-0.0f32), -0.0f32); + assert_biteq!(f32::math::ceil(-1.0f32), -1.0f32); + assert_biteq!(f32::math::ceil(-1.3f32), -1.0f32); + assert_biteq!(f32::math::ceil(-1.5f32), -1.0f32); + assert_biteq!(f32::math::ceil(-1.7f32), -1.0f32); } #[test] fn test_round() { - assert_eq!(f32::math::round(2.5f32), 3.0f32); - assert_eq!(f32::math::round(1.0f32), 1.0f32); - assert_eq!(f32::math::round(1.3f32), 1.0f32); - assert_eq!(f32::math::round(1.5f32), 2.0f32); - assert_eq!(f32::math::round(1.7f32), 2.0f32); - assert_eq!(f32::math::round(0.0f32), 0.0f32); - assert_eq!(f32::math::round(-0.0f32), -0.0f32); - assert_eq!(f32::math::round(-1.0f32), -1.0f32); - assert_eq!(f32::math::round(-1.3f32), -1.0f32); - assert_eq!(f32::math::round(-1.5f32), -2.0f32); - assert_eq!(f32::math::round(-1.7f32), -2.0f32); + assert_biteq!(f32::math::round(2.5f32), 3.0f32); + assert_biteq!(f32::math::round(1.0f32), 1.0f32); + assert_biteq!(f32::math::round(1.3f32), 1.0f32); + assert_biteq!(f32::math::round(1.5f32), 2.0f32); + assert_biteq!(f32::math::round(1.7f32), 2.0f32); + assert_biteq!(f32::math::round(0.0f32), 0.0f32); + assert_biteq!(f32::math::round(-0.0f32), -0.0f32); + assert_biteq!(f32::math::round(-1.0f32), -1.0f32); + assert_biteq!(f32::math::round(-1.3f32), -1.0f32); + assert_biteq!(f32::math::round(-1.5f32), -2.0f32); + assert_biteq!(f32::math::round(-1.7f32), -2.0f32); } #[test] fn test_round_ties_even() { - assert_eq!(f32::math::round_ties_even(2.5f32), 2.0f32); - assert_eq!(f32::math::round_ties_even(1.0f32), 1.0f32); - assert_eq!(f32::math::round_ties_even(1.3f32), 1.0f32); - assert_eq!(f32::math::round_ties_even(1.5f32), 2.0f32); - assert_eq!(f32::math::round_ties_even(1.7f32), 2.0f32); - assert_eq!(f32::math::round_ties_even(0.0f32), 0.0f32); - assert_eq!(f32::math::round_ties_even(-0.0f32), -0.0f32); - assert_eq!(f32::math::round_ties_even(-1.0f32), -1.0f32); - assert_eq!(f32::math::round_ties_even(-1.3f32), -1.0f32); - assert_eq!(f32::math::round_ties_even(-1.5f32), -2.0f32); - assert_eq!(f32::math::round_ties_even(-1.7f32), -2.0f32); + assert_biteq!(f32::math::round_ties_even(2.5f32), 2.0f32); + assert_biteq!(f32::math::round_ties_even(1.0f32), 1.0f32); + assert_biteq!(f32::math::round_ties_even(1.3f32), 1.0f32); + assert_biteq!(f32::math::round_ties_even(1.5f32), 2.0f32); + assert_biteq!(f32::math::round_ties_even(1.7f32), 2.0f32); + assert_biteq!(f32::math::round_ties_even(0.0f32), 0.0f32); + assert_biteq!(f32::math::round_ties_even(-0.0f32), -0.0f32); + assert_biteq!(f32::math::round_ties_even(-1.0f32), -1.0f32); + assert_biteq!(f32::math::round_ties_even(-1.3f32), -1.0f32); + assert_biteq!(f32::math::round_ties_even(-1.5f32), -2.0f32); + assert_biteq!(f32::math::round_ties_even(-1.7f32), -2.0f32); } #[test] fn test_trunc() { - assert_eq!(f32::math::trunc(1.0f32), 1.0f32); - assert_eq!(f32::math::trunc(1.3f32), 1.0f32); - assert_eq!(f32::math::trunc(1.5f32), 1.0f32); - assert_eq!(f32::math::trunc(1.7f32), 1.0f32); - assert_eq!(f32::math::trunc(0.0f32), 0.0f32); - assert_eq!(f32::math::trunc(-0.0f32), -0.0f32); - assert_eq!(f32::math::trunc(-1.0f32), -1.0f32); - assert_eq!(f32::math::trunc(-1.3f32), -1.0f32); - assert_eq!(f32::math::trunc(-1.5f32), -1.0f32); - assert_eq!(f32::math::trunc(-1.7f32), -1.0f32); + assert_biteq!(f32::math::trunc(1.0f32), 1.0f32); + assert_biteq!(f32::math::trunc(1.3f32), 1.0f32); + assert_biteq!(f32::math::trunc(1.5f32), 1.0f32); + assert_biteq!(f32::math::trunc(1.7f32), 1.0f32); + assert_biteq!(f32::math::trunc(0.0f32), 0.0f32); + assert_biteq!(f32::math::trunc(-0.0f32), -0.0f32); + assert_biteq!(f32::math::trunc(-1.0f32), -1.0f32); + assert_biteq!(f32::math::trunc(-1.3f32), -1.0f32); + assert_biteq!(f32::math::trunc(-1.5f32), -1.0f32); + assert_biteq!(f32::math::trunc(-1.7f32), -1.0f32); } #[test] fn test_fract() { - assert_eq!(f32::math::fract(1.0f32), 0.0f32); - assert_eq!(f32::math::fract(1.3f32), 0.29999995f32); - assert_eq!(f32::math::fract(1.5f32), 0.5f32); - assert_eq!(f32::math::fract(1.7f32), 0.70000005f32); - assert_eq!(f32::math::fract(0.0f32), 0.0f32); - assert_eq!(f32::math::fract(-0.0f32), -0.0f32); - assert_eq!(f32::math::fract(-1.0f32), -0.0f32); - assert_eq!(f32::math::fract(-1.3f32), -0.29999995f32); - assert_eq!(f32::math::fract(-1.5f32), -0.5f32); - assert_eq!(f32::math::fract(-1.7f32), -0.70000005f32); + assert_biteq!(f32::math::fract(1.0f32), 0.0f32); + assert_biteq!(f32::math::fract(1.3f32), 0.29999995f32); + assert_biteq!(f32::math::fract(1.5f32), 0.5f32); + assert_biteq!(f32::math::fract(1.7f32), 0.70000005f32); + assert_biteq!(f32::math::fract(0.0f32), 0.0f32); + assert_biteq!(f32::math::fract(-0.0f32), 0.0f32); + assert_biteq!(f32::math::fract(-1.0f32), 0.0f32); + assert_biteq!(f32::math::fract(-1.3f32), -0.29999995f32); + assert_biteq!(f32::math::fract(-1.5f32), -0.5f32); + assert_biteq!(f32::math::fract(-1.7f32), -0.70000005f32); } #[test] fn test_abs() { - assert_eq!(f32::INFINITY.abs(), f32::INFINITY); - assert_eq!(1f32.abs(), 1f32); - assert_eq!(0f32.abs(), 0f32); - assert_eq!((-0f32).abs(), 0f32); - assert_eq!((-1f32).abs(), 1f32); - assert_eq!(f32::NEG_INFINITY.abs(), f32::INFINITY); - assert_eq!((1f32 / f32::NEG_INFINITY).abs(), 0f32); + assert_biteq!(f32::INFINITY.abs(), f32::INFINITY); + assert_biteq!(1f32.abs(), 1f32); + assert_biteq!(0f32.abs(), 0f32); + assert_biteq!((-0f32).abs(), 0f32); + assert_biteq!((-1f32).abs(), 1f32); + assert_biteq!(f32::NEG_INFINITY.abs(), f32::INFINITY); + assert_biteq!((1f32 / f32::NEG_INFINITY).abs(), 0f32); assert!(f32::NAN.abs().is_nan()); } #[test] fn test_signum() { - assert_eq!(f32::INFINITY.signum(), 1f32); - assert_eq!(1f32.signum(), 1f32); - assert_eq!(0f32.signum(), 1f32); - assert_eq!((-0f32).signum(), -1f32); - assert_eq!((-1f32).signum(), -1f32); - assert_eq!(f32::NEG_INFINITY.signum(), -1f32); - assert_eq!((1f32 / f32::NEG_INFINITY).signum(), -1f32); + assert_biteq!(f32::INFINITY.signum(), 1f32); + assert_biteq!(1f32.signum(), 1f32); + assert_biteq!(0f32.signum(), 1f32); + assert_biteq!((-0f32).signum(), -1f32); + assert_biteq!((-1f32).signum(), -1f32); + assert_biteq!(f32::NEG_INFINITY.signum(), -1f32); + assert_biteq!((1f32 / f32::NEG_INFINITY).signum(), -1f32); assert!(f32::NAN.signum().is_nan()); } @@ -406,15 +407,15 @@ fn test_mul_add() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(f32::math::mul_add(12.3f32, 4.5, 6.7), 62.05); - assert_eq!(f32::math::mul_add(-12.3f32, -4.5, -6.7), 48.65); - assert_eq!(f32::math::mul_add(0.0f32, 8.9, 1.2), 1.2); - assert_eq!(f32::math::mul_add(3.4f32, -0.0, 5.6), 5.6); + assert_biteq!(f32::math::mul_add(12.3f32, 4.5, 6.7), 62.05); + assert_biteq!(f32::math::mul_add(-12.3f32, -4.5, -6.7), 48.65); + assert_biteq!(f32::math::mul_add(0.0f32, 8.9, 1.2), 1.2); + assert_biteq!(f32::math::mul_add(3.4f32, -0.0, 5.6), 5.6); assert!(f32::math::mul_add(nan, 7.8, 9.0).is_nan()); - assert_eq!(f32::math::mul_add(inf, 7.8, 9.0), inf); - assert_eq!(f32::math::mul_add(neg_inf, 7.8, 9.0), neg_inf); - assert_eq!(f32::math::mul_add(8.9f32, inf, 3.2), inf); - assert_eq!(f32::math::mul_add(-3.2f32, 2.4, neg_inf), neg_inf); + assert_biteq!(f32::math::mul_add(inf, 7.8, 9.0), inf); + assert_biteq!(f32::math::mul_add(neg_inf, 7.8, 9.0), neg_inf); + assert_biteq!(f32::math::mul_add(8.9f32, inf, 3.2), inf); + assert_biteq!(f32::math::mul_add(-3.2f32, 2.4, neg_inf), neg_inf); } #[test] @@ -422,13 +423,13 @@ fn test_recip() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.recip(), 1.0); - assert_eq!(2.0f32.recip(), 0.5); - assert_eq!((-0.4f32).recip(), -2.5); - assert_eq!(0.0f32.recip(), inf); + assert_biteq!(1.0f32.recip(), 1.0); + assert_biteq!(2.0f32.recip(), 0.5); + assert_biteq!((-0.4f32).recip(), -2.5); + assert_biteq!(0.0f32.recip(), inf); assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); + assert_biteq!(inf.recip(), 0.0); + assert_biteq!(neg_inf.recip(), -0.0); } #[test] @@ -436,13 +437,13 @@ fn test_powi() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.powi(1), 1.0); + assert_biteq!(1.0f32.powi(1), 1.0); assert_approx_eq!((-3.1f32).powi(2), 9.61); assert_approx_eq!(5.9f32.powi(-2), 0.028727); - assert_eq!(8.3f32.powi(0), 1.0); + assert_biteq!(8.3f32.powi(0), 1.0); assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); + assert_biteq!(inf.powi(3), inf); + assert_biteq!(neg_inf.powi(2), inf); } #[test] @@ -450,10 +451,10 @@ fn test_sqrt_domain() { assert!(f32::NAN.sqrt().is_nan()); assert!(f32::NEG_INFINITY.sqrt().is_nan()); assert!((-1.0f32).sqrt().is_nan()); - assert_eq!((-0.0f32).sqrt(), -0.0); - assert_eq!(0.0f32.sqrt(), 0.0); - assert_eq!(1.0f32.sqrt(), 1.0); - assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); + assert_biteq!((-0.0f32).sqrt(), -0.0); + assert_biteq!(0.0f32.sqrt(), 0.0); + assert_biteq!(1.0f32.sqrt(), 1.0); + assert_biteq!(f32::INFINITY.sqrt(), f32::INFINITY); } #[test] @@ -462,13 +463,13 @@ fn test_to_degrees() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(0.0f32.to_degrees(), 0.0); + assert_biteq!(0.0f32.to_degrees(), 0.0); assert_approx_eq!((-5.8f32).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); + assert_biteq!(pi.to_degrees(), 180.0); assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703); + assert_biteq!(inf.to_degrees(), inf); + assert_biteq!(neg_inf.to_degrees(), neg_inf); + assert_biteq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703); } #[test] @@ -477,13 +478,13 @@ fn test_to_radians() { let nan: f32 = f32::NAN; let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(0.0f32.to_radians(), 0.0); + assert_biteq!(0.0f32.to_radians(), 0.0); assert_approx_eq!(154.6f32.to_radians(), 2.698279); assert_approx_eq!((-332.31f32).to_radians(), -5.799903); - assert_eq!(180.0f32.to_radians(), pi); + assert_biteq!(180.0f32.to_radians(), pi); assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); + assert_biteq!(inf.to_radians(), inf); + assert_biteq!(neg_inf.to_radians(), neg_inf); } #[test] @@ -492,10 +493,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f32).to_bits(), 0x41480000); assert_eq!((1337f32).to_bits(), 0x44a72000); assert_eq!((-14.25f32).to_bits(), 0xc1640000); - assert_eq!(f32::from_bits(0x3f800000), 1.0); - assert_eq!(f32::from_bits(0x41480000), 12.5); - assert_eq!(f32::from_bits(0x44a72000), 1337.0); - assert_eq!(f32::from_bits(0xc1640000), -14.25); + assert_biteq!(f32::from_bits(0x3f800000), 1.0); + assert_biteq!(f32::from_bits(0x41480000), 12.5); + assert_biteq!(f32::from_bits(0x44a72000), 1337.0); + assert_biteq!(f32::from_bits(0xc1640000), -14.25); // 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/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 1cc7b3e13226f..74202a3740916 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -30,14 +30,14 @@ fn test_num_f64() { #[test] fn test_min_nan() { - assert_eq!(f64::NAN.min(2.0), 2.0); - assert_eq!(2.0f64.min(f64::NAN), 2.0); + assert_biteq!(f64::NAN.min(2.0), 2.0); + assert_biteq!(2.0f64.min(f64::NAN), 2.0); } #[test] fn test_max_nan() { - assert_eq!(f64::NAN.max(2.0), 2.0); - assert_eq!(2.0f64.max(f64::NAN), 2.0); + assert_biteq!(f64::NAN.max(2.0), 2.0); + assert_biteq!(2.0f64.max(f64::NAN), 2.0); } #[test] @@ -81,7 +81,7 @@ fn test_neg_infinity() { #[test] fn test_zero() { let zero: f64 = 0.0f64; - assert_eq!(0.0, zero); + assert_biteq!(0.0, zero); assert!(!zero.is_infinite()); assert!(zero.is_finite()); assert!(zero.is_sign_positive()); @@ -95,6 +95,7 @@ fn test_zero() { fn test_neg_zero() { let neg_zero: f64 = -0.0; assert_eq!(0.0, neg_zero); + assert_biteq!(-0.0, neg_zero); assert!(!neg_zero.is_infinite()); assert!(neg_zero.is_finite()); assert!(!neg_zero.is_sign_positive()); @@ -107,7 +108,7 @@ fn test_neg_zero() { #[test] fn test_one() { let one: f64 = 1.0f64; - assert_eq!(1.0, one); + assert_biteq!(1.0, one); assert!(!one.is_infinite()); assert!(one.is_finite()); assert!(one.is_sign_positive()); @@ -191,111 +192,111 @@ fn test_classify() { #[test] fn test_floor() { - assert_eq!(f64::math::floor(1.0f64), 1.0f64); - assert_eq!(f64::math::floor(1.3f64), 1.0f64); - assert_eq!(f64::math::floor(1.5f64), 1.0f64); - assert_eq!(f64::math::floor(1.7f64), 1.0f64); - assert_eq!(f64::math::floor(0.0f64), 0.0f64); - assert_eq!(f64::math::floor(-0.0f64), -0.0f64); - assert_eq!(f64::math::floor(-1.0f64), -1.0f64); - assert_eq!(f64::math::floor(-1.3f64), -2.0f64); - assert_eq!(f64::math::floor(-1.5f64), -2.0f64); - assert_eq!(f64::math::floor(-1.7f64), -2.0f64); + assert_biteq!(f64::math::floor(1.0f64), 1.0f64); + assert_biteq!(f64::math::floor(1.3f64), 1.0f64); + assert_biteq!(f64::math::floor(1.5f64), 1.0f64); + assert_biteq!(f64::math::floor(1.7f64), 1.0f64); + assert_biteq!(f64::math::floor(0.0f64), 0.0f64); + assert_biteq!(f64::math::floor(-0.0f64), -0.0f64); + assert_biteq!(f64::math::floor(-1.0f64), -1.0f64); + assert_biteq!(f64::math::floor(-1.3f64), -2.0f64); + assert_biteq!(f64::math::floor(-1.5f64), -2.0f64); + assert_biteq!(f64::math::floor(-1.7f64), -2.0f64); } #[test] fn test_ceil() { - assert_eq!(f64::math::ceil(1.0f64), 1.0f64); - assert_eq!(f64::math::ceil(1.3f64), 2.0f64); - assert_eq!(f64::math::ceil(1.5f64), 2.0f64); - assert_eq!(f64::math::ceil(1.7f64), 2.0f64); - assert_eq!(f64::math::ceil(0.0f64), 0.0f64); - assert_eq!(f64::math::ceil(-0.0f64), -0.0f64); - assert_eq!(f64::math::ceil(-1.0f64), -1.0f64); - assert_eq!(f64::math::ceil(-1.3f64), -1.0f64); - assert_eq!(f64::math::ceil(-1.5f64), -1.0f64); - assert_eq!(f64::math::ceil(-1.7f64), -1.0f64); + assert_biteq!(f64::math::ceil(1.0f64), 1.0f64); + assert_biteq!(f64::math::ceil(1.3f64), 2.0f64); + assert_biteq!(f64::math::ceil(1.5f64), 2.0f64); + assert_biteq!(f64::math::ceil(1.7f64), 2.0f64); + assert_biteq!(f64::math::ceil(0.0f64), 0.0f64); + assert_biteq!(f64::math::ceil(-0.0f64), -0.0f64); + assert_biteq!(f64::math::ceil(-1.0f64), -1.0f64); + assert_biteq!(f64::math::ceil(-1.3f64), -1.0f64); + assert_biteq!(f64::math::ceil(-1.5f64), -1.0f64); + assert_biteq!(f64::math::ceil(-1.7f64), -1.0f64); } #[test] fn test_round() { - assert_eq!(f64::math::round(2.5f64), 3.0f64); - assert_eq!(f64::math::round(1.0f64), 1.0f64); - assert_eq!(f64::math::round(1.3f64), 1.0f64); - assert_eq!(f64::math::round(1.5f64), 2.0f64); - assert_eq!(f64::math::round(1.7f64), 2.0f64); - assert_eq!(f64::math::round(0.0f64), 0.0f64); - assert_eq!(f64::math::round(-0.0f64), -0.0f64); - assert_eq!(f64::math::round(-1.0f64), -1.0f64); - assert_eq!(f64::math::round(-1.3f64), -1.0f64); - assert_eq!(f64::math::round(-1.5f64), -2.0f64); - assert_eq!(f64::math::round(-1.7f64), -2.0f64); + assert_biteq!(f64::math::round(2.5f64), 3.0f64); + assert_biteq!(f64::math::round(1.0f64), 1.0f64); + assert_biteq!(f64::math::round(1.3f64), 1.0f64); + assert_biteq!(f64::math::round(1.5f64), 2.0f64); + assert_biteq!(f64::math::round(1.7f64), 2.0f64); + assert_biteq!(f64::math::round(0.0f64), 0.0f64); + assert_biteq!(f64::math::round(-0.0f64), -0.0f64); + assert_biteq!(f64::math::round(-1.0f64), -1.0f64); + assert_biteq!(f64::math::round(-1.3f64), -1.0f64); + assert_biteq!(f64::math::round(-1.5f64), -2.0f64); + assert_biteq!(f64::math::round(-1.7f64), -2.0f64); } #[test] fn test_round_ties_even() { - assert_eq!(f64::math::round_ties_even(2.5f64), 2.0f64); - assert_eq!(f64::math::round_ties_even(1.0f64), 1.0f64); - assert_eq!(f64::math::round_ties_even(1.3f64), 1.0f64); - assert_eq!(f64::math::round_ties_even(1.5f64), 2.0f64); - assert_eq!(f64::math::round_ties_even(1.7f64), 2.0f64); - assert_eq!(f64::math::round_ties_even(0.0f64), 0.0f64); - assert_eq!(f64::math::round_ties_even(-0.0f64), -0.0f64); - assert_eq!(f64::math::round_ties_even(-1.0f64), -1.0f64); - assert_eq!(f64::math::round_ties_even(-1.3f64), -1.0f64); - assert_eq!(f64::math::round_ties_even(-1.5f64), -2.0f64); - assert_eq!(f64::math::round_ties_even(-1.7f64), -2.0f64); + assert_biteq!(f64::math::round_ties_even(2.5f64), 2.0f64); + assert_biteq!(f64::math::round_ties_even(1.0f64), 1.0f64); + assert_biteq!(f64::math::round_ties_even(1.3f64), 1.0f64); + assert_biteq!(f64::math::round_ties_even(1.5f64), 2.0f64); + assert_biteq!(f64::math::round_ties_even(1.7f64), 2.0f64); + assert_biteq!(f64::math::round_ties_even(0.0f64), 0.0f64); + assert_biteq!(f64::math::round_ties_even(-0.0f64), -0.0f64); + assert_biteq!(f64::math::round_ties_even(-1.0f64), -1.0f64); + assert_biteq!(f64::math::round_ties_even(-1.3f64), -1.0f64); + assert_biteq!(f64::math::round_ties_even(-1.5f64), -2.0f64); + assert_biteq!(f64::math::round_ties_even(-1.7f64), -2.0f64); } #[test] fn test_trunc() { - assert_eq!(f64::math::trunc(1.0f64), 1.0f64); - assert_eq!(f64::math::trunc(1.3f64), 1.0f64); - assert_eq!(f64::math::trunc(1.5f64), 1.0f64); - assert_eq!(f64::math::trunc(1.7f64), 1.0f64); - assert_eq!(f64::math::trunc(0.0f64), 0.0f64); - assert_eq!(f64::math::trunc(-0.0f64), -0.0f64); - assert_eq!(f64::math::trunc(-1.0f64), -1.0f64); - assert_eq!(f64::math::trunc(-1.3f64), -1.0f64); - assert_eq!(f64::math::trunc(-1.5f64), -1.0f64); - assert_eq!(f64::math::trunc(-1.7f64), -1.0f64); + assert_biteq!(f64::math::trunc(1.0f64), 1.0f64); + assert_biteq!(f64::math::trunc(1.3f64), 1.0f64); + assert_biteq!(f64::math::trunc(1.5f64), 1.0f64); + assert_biteq!(f64::math::trunc(1.7f64), 1.0f64); + assert_biteq!(f64::math::trunc(0.0f64), 0.0f64); + assert_biteq!(f64::math::trunc(-0.0f64), -0.0f64); + assert_biteq!(f64::math::trunc(-1.0f64), -1.0f64); + assert_biteq!(f64::math::trunc(-1.3f64), -1.0f64); + assert_biteq!(f64::math::trunc(-1.5f64), -1.0f64); + assert_biteq!(f64::math::trunc(-1.7f64), -1.0f64); } #[test] fn test_fract() { - assert_eq!(f64::math::fract(1.0f64), 0.0f64); - assert_eq!(f64::math::fract(1.3f64), 0.30000000000000004f64); - assert_eq!(f64::math::fract(1.5f64), 0.5f64); - assert_eq!(f64::math::fract(1.7f64), 0.7f64); - assert_eq!(f64::math::fract(0.0f64), 0.0f64); - assert_eq!(f64::math::fract(-0.0f64), -0.0f64); - assert_eq!(f64::math::fract(-1.0f64), -0.0f64); - assert_eq!(f64::math::fract(-1.3f64), -0.30000000000000004f64); - assert_eq!(f64::math::fract(-1.5f64), -0.5f64); - assert_eq!(f64::math::fract(-1.7f64), -0.69999999999999996f64); + assert_biteq!(f64::math::fract(1.0f64), 0.0f64); + assert_biteq!(f64::math::fract(1.3f64), 0.30000000000000004f64); + assert_biteq!(f64::math::fract(1.5f64), 0.5f64); + assert_biteq!(f64::math::fract(1.7f64), 0.7f64); + assert_biteq!(f64::math::fract(0.0f64), 0.0f64); + assert_biteq!(f64::math::fract(-0.0f64), 0.0f64); + assert_biteq!(f64::math::fract(-1.0f64), 0.0f64); + assert_biteq!(f64::math::fract(-1.3f64), -0.30000000000000004f64); + assert_biteq!(f64::math::fract(-1.5f64), -0.5f64); + assert_biteq!(f64::math::fract(-1.7f64), -0.69999999999999996f64); } #[test] fn test_abs() { - assert_eq!(f64::INFINITY.abs(), f64::INFINITY); - assert_eq!(1f64.abs(), 1f64); - assert_eq!(0f64.abs(), 0f64); - assert_eq!((-0f64).abs(), 0f64); - assert_eq!((-1f64).abs(), 1f64); - assert_eq!(f64::NEG_INFINITY.abs(), f64::INFINITY); - assert_eq!((1f64 / f64::NEG_INFINITY).abs(), 0f64); + assert_biteq!(f64::INFINITY.abs(), f64::INFINITY); + assert_biteq!(1f64.abs(), 1f64); + assert_biteq!(0f64.abs(), 0f64); + assert_biteq!((-0f64).abs(), 0f64); + assert_biteq!((-1f64).abs(), 1f64); + assert_biteq!(f64::NEG_INFINITY.abs(), f64::INFINITY); + assert_biteq!((1f64 / f64::NEG_INFINITY).abs(), 0f64); assert!(f64::NAN.abs().is_nan()); } #[test] fn test_signum() { - assert_eq!(f64::INFINITY.signum(), 1f64); - assert_eq!(1f64.signum(), 1f64); - assert_eq!(0f64.signum(), 1f64); - assert_eq!((-0f64).signum(), -1f64); - assert_eq!((-1f64).signum(), -1f64); - assert_eq!(f64::NEG_INFINITY.signum(), -1f64); - assert_eq!((1f64 / f64::NEG_INFINITY).signum(), -1f64); + assert_biteq!(f64::INFINITY.signum(), 1f64); + assert_biteq!(1f64.signum(), 1f64); + assert_biteq!(0f64.signum(), 1f64); + assert_biteq!((-0f64).signum(), -1f64); + assert_biteq!((-1f64).signum(), -1f64); + assert_biteq!(f64::NEG_INFINITY.signum(), -1f64); + assert_biteq!((1f64 / f64::NEG_INFINITY).signum(), -1f64); assert!(f64::NAN.signum().is_nan()); } @@ -391,15 +392,15 @@ fn test_mul_add() { let nan: f64 = f64::NAN; let inf: f64 = f64::INFINITY; let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(12.3f64.mul_add(4.5, 6.7), 62.050000000000004); - assert_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.650000000000006); - assert_eq!(0.0f64.mul_add(8.9, 1.2), 1.2); - assert_eq!(3.4f64.mul_add(-0.0, 5.6), 5.6); + assert_biteq!(12.3f64.mul_add(4.5, 6.7), 62.050000000000004); + assert_biteq!((-12.3f64).mul_add(-4.5, -6.7), 48.650000000000006); + assert_biteq!(0.0f64.mul_add(8.9, 1.2), 1.2); + assert_biteq!(3.4f64.mul_add(-0.0, 5.6), 5.6); 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.9f64.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); + assert_biteq!(inf.mul_add(7.8, 9.0), inf); + assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_biteq!(8.9f64.mul_add(inf, 3.2), inf); + assert_biteq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); } #[test] @@ -407,13 +408,13 @@ fn test_recip() { let nan: f64 = f64::NAN; let inf: f64 = f64::INFINITY; let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.recip(), 1.0); - assert_eq!(2.0f64.recip(), 0.5); - assert_eq!((-0.4f64).recip(), -2.5); - assert_eq!(0.0f64.recip(), inf); + assert_biteq!(1.0f64.recip(), 1.0); + assert_biteq!(2.0f64.recip(), 0.5); + assert_biteq!((-0.4f64).recip(), -2.5); + assert_biteq!(0.0f64.recip(), inf); assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); + assert_biteq!(inf.recip(), 0.0); + assert_biteq!(neg_inf.recip(), -0.0); } #[test] @@ -421,13 +422,13 @@ fn test_powi() { let nan: f64 = f64::NAN; let inf: f64 = f64::INFINITY; let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.powi(1), 1.0); + assert_biteq!(1.0f64.powi(1), 1.0); assert_approx_eq!((-3.1f64).powi(2), 9.61); assert_approx_eq!(5.9f64.powi(-2), 0.028727); - assert_eq!(8.3f64.powi(0), 1.0); + assert_biteq!(8.3f64.powi(0), 1.0); assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); + assert_biteq!(inf.powi(3), inf); + assert_biteq!(neg_inf.powi(2), inf); } #[test] @@ -435,10 +436,10 @@ fn test_sqrt_domain() { assert!(f64::NAN.sqrt().is_nan()); assert!(f64::NEG_INFINITY.sqrt().is_nan()); assert!((-1.0f64).sqrt().is_nan()); - assert_eq!((-0.0f64).sqrt(), -0.0); - assert_eq!(0.0f64.sqrt(), 0.0); - assert_eq!(1.0f64.sqrt(), 1.0); - assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); + assert_biteq!((-0.0f64).sqrt(), -0.0); + assert_biteq!(0.0f64.sqrt(), 0.0); + assert_biteq!(1.0f64.sqrt(), 1.0); + assert_biteq!(f64::INFINITY.sqrt(), f64::INFINITY); } #[test] @@ -447,12 +448,12 @@ fn test_to_degrees() { let nan: f64 = f64::NAN; let inf: f64 = f64::INFINITY; let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(0.0f64.to_degrees(), 0.0); + assert_biteq!(0.0f64.to_degrees(), 0.0); assert_approx_eq!((-5.8f64).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); + assert_biteq!(pi.to_degrees(), 180.0); assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_biteq!(inf.to_degrees(), inf); + assert_biteq!(neg_inf.to_degrees(), neg_inf); } #[test] @@ -461,13 +462,13 @@ fn test_to_radians() { let nan: f64 = f64::NAN; let inf: f64 = f64::INFINITY; let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(0.0f64.to_radians(), 0.0); + assert_biteq!(0.0f64.to_radians(), 0.0); assert_approx_eq!(154.6f64.to_radians(), 2.698279); assert_approx_eq!((-332.31f64).to_radians(), -5.799903); - assert_eq!(180.0f64.to_radians(), pi); + assert_biteq!(180.0f64.to_radians(), pi); assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); + assert_biteq!(inf.to_radians(), inf); + assert_biteq!(neg_inf.to_radians(), neg_inf); } #[test] @@ -476,10 +477,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f64).to_bits(), 0x4029000000000000); assert_eq!((1337f64).to_bits(), 0x4094e40000000000); assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000); - assert_approx_eq!(f64::from_bits(0x3ff0000000000000), 1.0); - assert_approx_eq!(f64::from_bits(0x4029000000000000), 12.5); - assert_approx_eq!(f64::from_bits(0x4094e40000000000), 1337.0); - assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25); + assert_biteq!(f64::from_bits(0x3ff0000000000000), 1.0); + assert_biteq!(f64::from_bits(0x4029000000000000), 12.5); + assert_biteq!(f64::from_bits(0x4094e40000000000), 1337.0); + assert_biteq!(f64::from_bits(0xc02c800000000000), -14.25); // Check that NaNs roundtrip their bits regardless of signaling-ness let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1; diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index c861b5ceff3f5..7e27028a2a2d1 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -37,6 +37,12 @@ macro_rules! assert_biteq { rb = r.to_bits(), width = ((bits / 4) + 2) as usize, ); + + if !l.is_nan() && !r.is_nan() { + // Also check that standard equality holds, since most tests use `assert_biteq` rather + // than `assert_eq`. + assert_eq!(l, r) + } }}; ($left:expr, $right:expr , $($tt:tt)*) => { assert_biteq!(@inner $left, $right, "\n", $($tt)*)