diff --git a/Cargo.lock b/Cargo.lock index 9d1481ec768d0..b4f623e626d60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5678,6 +5678,7 @@ dependencies = [ name = "tidy" version = "0.1.0" dependencies = [ + "build_helper", "cargo_metadata 0.15.4", "fluent-syntax", "ignore", diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index cc66cc87652d0..efe1956615216 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -1,6 +1,8 @@ pub use BinOpToken::*; pub use LitKind::*; pub use Nonterminal::*; +pub use NtExprKind::*; +pub use NtPatKind::*; pub use TokenKind::*; use crate::ast; @@ -871,6 +873,27 @@ impl PartialEq for Token { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable)] +pub enum NtPatKind { + // Matches or-patterns. Was written using `pat` in edition 2021 or later. + PatWithOr, + // Doesn't match or-patterns. + // - `inferred`: was written using `pat` in edition 2015 or 2018. + // - `!inferred`: was written using `pat_param`. + PatParam { inferred: bool }, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable)] +pub enum NtExprKind { + // Matches expressions using the post-edition 2024. Was written using + // `expr` in edition 2024 or later. + Expr, + // Matches expressions using the pre-edition 2024 rules. + // - `inferred`: was written using `expr` in edition 2021 or earlier. + // - `!inferred`: was written using `expr_2021`. + Expr2021 { inferred: bool }, +} + #[derive(Clone, Encodable, Decodable)] /// For interpolation during macro expansion. pub enum Nonterminal { @@ -892,19 +915,8 @@ pub enum NonterminalKind { Item, Block, Stmt, - PatParam { - /// Keep track of whether the user used `:pat_param` or `:pat` and we inferred it from the - /// edition of the span. This is used for diagnostics. - inferred: bool, - }, - PatWithOr, - Expr, - /// Matches an expression using the rules from edition 2021 and earlier. - Expr2021 { - /// Keep track of whether the user used `:expr` or `:expr_2021` and we inferred it from the - /// edition of the span. This is used for diagnostics AND feature gating. - inferred: bool, - }, + Pat(NtPatKind), + Expr(NtExprKind), Ty, Ident, Lifetime, @@ -926,20 +938,22 @@ impl NonterminalKind { sym::item => NonterminalKind::Item, sym::block => NonterminalKind::Block, sym::stmt => NonterminalKind::Stmt, - sym::pat => match edition() { - Edition::Edition2015 | Edition::Edition2018 => { - NonterminalKind::PatParam { inferred: true } + sym::pat => { + if edition().at_least_rust_2021() { + NonterminalKind::Pat(PatWithOr) + } else { + NonterminalKind::Pat(PatParam { inferred: true }) } - Edition::Edition2021 | Edition::Edition2024 => NonterminalKind::PatWithOr, - }, - sym::pat_param => NonterminalKind::PatParam { inferred: false }, - sym::expr => match edition() { - Edition::Edition2015 | Edition::Edition2018 | Edition::Edition2021 => { - NonterminalKind::Expr2021 { inferred: true } + } + sym::pat_param => NonterminalKind::Pat(PatParam { inferred: false }), + sym::expr => { + if edition().at_least_rust_2024() { + NonterminalKind::Expr(Expr) + } else { + NonterminalKind::Expr(Expr2021 { inferred: true }) } - Edition::Edition2024 => NonterminalKind::Expr, - }, - sym::expr_2021 => NonterminalKind::Expr2021 { inferred: false }, + } + sym::expr_2021 => NonterminalKind::Expr(Expr2021 { inferred: false }), sym::ty => NonterminalKind::Ty, sym::ident => NonterminalKind::Ident, sym::lifetime => NonterminalKind::Lifetime, @@ -951,15 +965,16 @@ impl NonterminalKind { _ => return None, }) } + fn symbol(self) -> Symbol { match self { NonterminalKind::Item => sym::item, NonterminalKind::Block => sym::block, NonterminalKind::Stmt => sym::stmt, - NonterminalKind::PatParam { inferred: false } => sym::pat_param, - NonterminalKind::PatParam { inferred: true } | NonterminalKind::PatWithOr => sym::pat, - NonterminalKind::Expr | NonterminalKind::Expr2021 { inferred: true } => sym::expr, - NonterminalKind::Expr2021 { inferred: false } => sym::expr_2021, + NonterminalKind::Pat(PatParam { inferred: true } | PatWithOr) => sym::pat, + NonterminalKind::Pat(PatParam { inferred: false }) => sym::pat_param, + NonterminalKind::Expr(Expr2021 { inferred: true } | Expr) => sym::expr, + NonterminalKind::Expr(Expr2021 { inferred: false }) => sym::expr_2021, NonterminalKind::Ty => sym::ty, NonterminalKind::Ident => sym::ident, NonterminalKind::Lifetime => sym::lifetime, diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 1476fe285ef5c..cd269810741e7 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -89,6 +89,8 @@ const_eval_exact_div_has_remainder = const_eval_extern_static = cannot access extern static ({$did}) +const_eval_extern_type_field = `extern type` field does not have a known offset + const_eval_fn_ptr_call = function pointers need an RFC before allowed to be called in {const_eval_const_context}s const_eval_for_loop_into_iter_non_const = diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index c60df06bb0efc..d8efaa66415fb 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -386,33 +386,8 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( CompileTimeMachine::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); let res = ecx.load_mir(cid.instance.def, cid.promoted); - res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)).map_err(|error| { - let (error, backtrace) = error.into_parts(); - backtrace.print_backtrace(); - - let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) { - ("static", String::new()) - } else { - // If the current item has generics, we'd like to enrich the message with the - // instance and its args: to show the actual compile-time values, in addition to - // the expression, leading to the const eval error. - let instance = &cid.instance; - if !instance.args.is_empty() { - let instance = with_no_trimmed_paths!(instance.to_string()); - ("const_with_path", instance) - } else { - ("const", String::new()) - } - }; - - super::report( - *ecx.tcx, - error, - DUMMY_SP, - || super::get_span_and_frames(ecx.tcx, ecx.stack()), - |span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames }, - ) - }) + res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)) + .map_err(|error| report_eval_error(&ecx, cid, error)) } #[inline(always)] @@ -438,24 +413,61 @@ fn const_validate_mplace<'tcx>( ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode) // Instead of just reporting the `InterpError` via the usual machinery, we give a more targeted // error about the validation failure. - .map_err(|error| report_validation_error(&ecx, error, alloc_id))?; + .map_err(|error| report_validation_error(&ecx, cid, error, alloc_id))?; inner = true; } Ok(()) } -#[inline(always)] +#[inline(never)] +fn report_eval_error<'tcx>( + ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>, + cid: GlobalId<'tcx>, + error: InterpErrorInfo<'tcx>, +) -> ErrorHandled { + let (error, backtrace) = error.into_parts(); + backtrace.print_backtrace(); + + let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) { + ("static", String::new()) + } else { + // If the current item has generics, we'd like to enrich the message with the + // instance and its args: to show the actual compile-time values, in addition to + // the expression, leading to the const eval error. + let instance = &cid.instance; + if !instance.args.is_empty() { + let instance = with_no_trimmed_paths!(instance.to_string()); + ("const_with_path", instance) + } else { + ("const", String::new()) + } + }; + + super::report( + *ecx.tcx, + error, + DUMMY_SP, + || super::get_span_and_frames(ecx.tcx, ecx.stack()), + |span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames }, + ) +} + +#[inline(never)] fn report_validation_error<'tcx>( ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>, + cid: GlobalId<'tcx>, error: InterpErrorInfo<'tcx>, alloc_id: AllocId, ) -> ErrorHandled { + if !matches!(error.kind(), InterpError::UndefinedBehavior(_)) { + // Some other error happened during validation, e.g. an unsupported operation. + return report_eval_error(ecx, cid, error); + } + let (error, backtrace) = error.into_parts(); backtrace.print_backtrace(); - let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {}); - let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id); let (size, align, _) = ecx.get_alloc_info(alloc_id); let raw_bytes = errors::RawBytesNote { size: size.bytes(), align: align.bytes(), bytes }; @@ -465,6 +477,6 @@ fn report_validation_error<'tcx>( error, DUMMY_SP, || crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()), - move |span, frames| errors::ValidationFailure { span, ub_note, frames, raw_bytes }, + move |span, frames| errors::ValidationFailure { span, ub_note: (), frames, raw_bytes }, ) } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 5fa48a59794be..292d6ba9d08a4 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -425,7 +425,7 @@ pub struct ValidationFailure { #[primary_span] pub span: Span, #[note(const_eval_validation_failure_note)] - pub ub_note: Option<()>, + pub ub_note: (), #[subdiagnostic] pub frames: Vec, #[subdiagnostic] @@ -825,6 +825,7 @@ impl ReportErrorExt for UnsupportedOpInfo { use crate::fluent_generated::*; match self { UnsupportedOpInfo::Unsupported(s) => s.clone().into(), + UnsupportedOpInfo::ExternTypeField => const_eval_extern_type_field, UnsupportedOpInfo::UnsizedLocal => const_eval_unsized_local, UnsupportedOpInfo::OverwritePartialPointer(_) => const_eval_partial_pointer_overwrite, UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_copy, @@ -845,7 +846,10 @@ impl ReportErrorExt for UnsupportedOpInfo { // `ReadPointerAsInt(Some(info))` is never printed anyway, it only serves as an error to // be further processed by validity checking which then turns it into something nice to // print. So it's not worth the effort of having diagnostics that can print the `info`. - UnsizedLocal | Unsupported(_) | ReadPointerAsInt(_) => {} + UnsizedLocal + | UnsupportedOpInfo::ExternTypeField + | Unsupported(_) + | ReadPointerAsInt(_) => {} OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => { diag.arg("ptr", ptr); } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index efa01b5434260..cfa814c810aff 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -21,7 +21,7 @@ use rustc_target::abi::{self, VariantIdx}; use tracing::{debug, instrument}; use super::{ - throw_ub, throw_unsup_format, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, + throw_ub, throw_unsup, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar, }; @@ -186,8 +186,8 @@ where (base_meta, offset) } None => { - // We don't know the alignment of this field, so we cannot adjust. - throw_unsup_format!("`extern type` does not have a known offset") + // We cannot know the alignment of this field, so we cannot adjust. + throw_unsup!(ExternTypeField) } } } else { diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index ca8b98849338b..add48e1b186cb 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -5,6 +5,7 @@ //! to be const-safe. use std::fmt::Write; +use std::hash::Hash; use std::num::NonZero; use either::{Left, Right}; @@ -17,7 +18,8 @@ use rustc_hir as hir; use rustc_middle::bug; use rustc_middle::mir::interpret::{ ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance, - ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*, + UnsupportedOpInfo, ValidationErrorInfo, + ValidationErrorKind::{self, *}, }; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -26,8 +28,6 @@ use rustc_target::abi::{ Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange, }; -use std::hash::Hash; - use super::{ err_ub, format_interp_error, machine::AllocMap, throw_ub, AllocId, AllocKind, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, @@ -1028,7 +1028,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Err(err) if matches!( err.kind(), - err_ub!(ValidationError { .. }) | InterpError::InvalidProgram(_) + err_ub!(ValidationError { .. }) + | InterpError::InvalidProgram(_) + | InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField) ) => { Err(err) diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 6113580491ef9..cc0b110d2bc6f 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -61,6 +61,9 @@ expand_feature_removed = expand_glob_delegation_outside_impls = glob delegation is only supported in impls +expand_glob_delegation_traitless_qpath = + qualified path without a trait in glob delegation + expand_helper_attribute_name_invalid = `{$name}` cannot be a name of derive helper attribute diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index c883121fb4065..0be7403f25b17 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -449,6 +449,13 @@ pub(crate) struct GlobDelegationOutsideImpls { pub span: Span, } +#[derive(Diagnostic)] +#[diag(expand_glob_delegation_traitless_qpath)] +pub(crate) struct GlobDelegationTraitlessQpath { + #[primary_span] + pub span: Span, +} + // This used to be the `proc_macro_back_compat` lint (#83125). It was later // turned into a hard error. #[derive(Diagnostic)] diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 716bfc8c26b1e..a331d83aab00a 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,8 +1,9 @@ use crate::base::*; use crate::config::StripUnconfigured; use crate::errors::{ - EmptyDelegationMac, GlobDelegationOutsideImpls, IncompleteParse, RecursionLimitReached, - RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind, + EmptyDelegationMac, GlobDelegationOutsideImpls, GlobDelegationTraitlessQpath, IncompleteParse, + RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, + WrongFragmentKind, }; use crate::mbe::diagnostics::annotate_err_with_kind; use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod}; @@ -1989,6 +1990,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } None if let Some((deleg, item)) = node.delegation() => { let Some(suffixes) = &deleg.suffixes else { + let traitless_qself = + matches!(&deleg.qself, Some(qself) if qself.position == 0); let item = match node.to_annotatable() { Annotatable::ImplItem(item) => item, ann @ (Annotatable::Item(_) @@ -2000,6 +2003,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } _ => unreachable!(), }; + if traitless_qself { + let span = item.span; + self.cx.dcx().emit_err(GlobDelegationTraitlessQpath { span }); + return Default::default(); + } return self.collect_glob_delegation(item, Node::KIND).make_ast::(); }; diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 0050ff10539a8..e43ba7c3a5a8f 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -10,7 +10,9 @@ use crate::mbe::transcribe::transcribe; use ast::token::IdentIsRaw; use rustc_ast as ast; -use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*}; +use rustc_ast::token::{ + self, Delimiter, NonterminalKind, NtPatKind::*, Token, TokenKind, TokenKind::*, +}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; @@ -1145,14 +1147,17 @@ fn check_matcher_core<'tt>( // Macros defined in the current crate have a real node id, // whereas macros from an external crate have a dummy id. if def.id != DUMMY_NODE_ID - && matches!(kind, NonterminalKind::PatParam { inferred: true }) - && matches!(next_token, TokenTree::Token(token) if token.kind == BinOp(token::BinOpToken::Or)) + && matches!(kind, NonterminalKind::Pat(PatParam { inferred: true })) + && matches!( + next_token, + TokenTree::Token(token) if token.kind == BinOp(token::BinOpToken::Or) + ) { // It is suggestion to use pat_param, for example: $x:pat -> $x:pat_param. let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( span, name, - Some(NonterminalKind::PatParam { inferred: false }), + Some(NonterminalKind::Pat(PatParam { inferred: false })), )); sess.psess.buffer_lint( RUST_2021_INCOMPATIBLE_OR_PATTERNS, @@ -1185,14 +1190,14 @@ fn check_matcher_core<'tt>( ); err.span_label(sp, format!("not allowed after `{kind}` fragments")); - if kind == NonterminalKind::PatWithOr + if kind == NonterminalKind::Pat(PatWithOr) && sess.psess.edition.at_least_rust_2021() && next_token.is_token(&BinOp(token::BinOpToken::Or)) { let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( span, name, - Some(NonterminalKind::PatParam { inferred: false }), + Some(NonterminalKind::Pat(PatParam { inferred: false })), )); err.span_suggestion( span, @@ -1292,9 +1297,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { // maintain IsInFollow::Yes } - NonterminalKind::Stmt - | NonterminalKind::Expr - | NonterminalKind::Expr2021 { inferred: _ } => { + NonterminalKind::Stmt | NonterminalKind::Expr(_) => { const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"]; match tok { TokenTree::Token(token) => match token.kind { @@ -1304,7 +1307,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } - NonterminalKind::PatParam { .. } => { + NonterminalKind::Pat(PatParam { .. }) => { const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { @@ -1317,7 +1320,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } - NonterminalKind::PatWithOr => { + NonterminalKind::Pat(PatWithOr) => { const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index fdf187438d3d7..9c480f17b4215 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -2,7 +2,7 @@ use crate::errors; use crate::mbe::macro_parser::count_metavar_decls; use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree}; -use rustc_ast::token::{self, Delimiter, IdentIsRaw, Token}; +use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, NtExprKind::*, Token}; use rustc_ast::{tokenstream, NodeId}; use rustc_ast_pretty::pprust; use rustc_feature::Features; @@ -85,36 +85,31 @@ pub(super) fn parse( span.edition() } }; - let kind = - token::NonterminalKind::from_symbol(fragment.name, edition) - .unwrap_or_else(|| { - let help = match fragment.name { - sym::expr_2021 => { - format!( - "fragment specifier `expr_2021` \ - requires Rust 2021 or later\n\ - {VALID_FRAGMENT_NAMES_MSG}" - ) - } - _ if edition().at_least_rust_2021() - && features - .expr_fragment_specifier_2024 => - { - VALID_FRAGMENT_NAMES_MSG_2021.into() - } - _ => VALID_FRAGMENT_NAMES_MSG.into(), - }; - sess.dcx().emit_err( - errors::InvalidFragmentSpecifier { - span, - fragment, - help, - }, - ); - token::NonterminalKind::Ident + let kind = NonterminalKind::from_symbol(fragment.name, edition) + .unwrap_or_else(|| { + let help = match fragment.name { + sym::expr_2021 => { + format!( + "fragment specifier `expr_2021` \ + requires Rust 2021 or later\n\ + {VALID_FRAGMENT_NAMES_MSG}" + ) + } + _ if edition().at_least_rust_2021() + && features.expr_fragment_specifier_2024 => + { + VALID_FRAGMENT_NAMES_MSG_2021.into() + } + _ => VALID_FRAGMENT_NAMES_MSG.into(), + }; + sess.dcx().emit_err(errors::InvalidFragmentSpecifier { + span, + fragment, + help, }); - if kind - == (token::NonterminalKind::Expr2021 { inferred: false }) + NonterminalKind::Ident + }); + if kind == NonterminalKind::Expr(Expr2021 { inferred: false }) && !features.expr_fragment_specifier_2024 { rustc_session::parse::feature_err( diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index fbd67657e3b4c..f66c9604cbe0d 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -629,7 +629,7 @@ declare_features! ( /// Allows unsafe on extern declarations and safety qualifiers over internal items. (unstable, unsafe_extern_blocks, "1.80.0", Some(123743)), /// Allows unsized fn parameters. - (unstable, unsized_fn_params, "1.49.0", Some(48055)), + (internal, unsized_fn_params, "1.49.0", Some(48055)), /// Allows unsized rvalues at arguments and parameters. (incomplete, unsized_locals, "1.30.0", Some(48055)), /// Allows unsized tuple coercion. diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 23680f143970d..6a8498abaf933 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -520,6 +520,8 @@ pub enum UnsupportedOpInfo { Unsupported(String), /// Unsized local variables. UnsizedLocal, + /// Extern type field with an indeterminate offset. + ExternTypeField, // // The variants below are only reachable from CTFE/const prop, miri will never emit them. // diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 59f6eff07b320..4a78b427832c5 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -1,5 +1,7 @@ use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Nonterminal::*, NonterminalKind, Token}; +use rustc_ast::token::{ + self, Delimiter, Nonterminal::*, NonterminalKind, NtExprKind::*, NtPatKind::*, Token, +}; use rustc_ast::HasTokens; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; @@ -36,14 +38,14 @@ impl<'a> Parser<'a> { } match kind { - NonterminalKind::Expr2021 { inferred: _ } => { + NonterminalKind::Expr(Expr2021 { .. }) => { token.can_begin_expr() // This exception is here for backwards compatibility. && !token.is_keyword(kw::Let) // This exception is here for backwards compatibility. && !token.is_keyword(kw::Const) } - NonterminalKind::Expr => { + NonterminalKind::Expr(Expr) => { token.can_begin_expr() // This exception is here for backwards compatibility. && !token.is_keyword(kw::Let) @@ -74,7 +76,7 @@ impl<'a> Parser<'a> { token::Interpolated(nt) => may_be_ident(nt), _ => false, }, - NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => match &token.kind { + NonterminalKind::Pat(pat_kind) => match &token.kind { // box, ref, mut, and other identifiers (can stricten) token::Ident(..) | token::NtIdent(..) | token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern @@ -89,7 +91,7 @@ impl<'a> Parser<'a> { token::Lt | // path (UFCS constant) token::BinOp(token::Shl) => true, // path (double UFCS) // leading vert `|` or-pattern - token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr), + token::BinOp(token::Or) => matches!(pat_kind, PatWithOr), token::Interpolated(nt) => may_be_ident(nt), _ => false, }, @@ -135,31 +137,25 @@ impl<'a> Parser<'a> { .create_err(UnexpectedNonterminal::Statement(self.token.span))); } }, - NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => { - NtPat(self.collect_tokens_no_attrs(|this| match kind { - NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None, None), - NonterminalKind::PatWithOr => this.parse_pat_allow_top_alt( + NonterminalKind::Pat(pat_kind) => { + NtPat(self.collect_tokens_no_attrs(|this| match pat_kind { + PatParam { .. } => this.parse_pat_no_top_alt(None, None), + PatWithOr => this.parse_pat_allow_top_alt( None, RecoverComma::No, RecoverColon::No, CommaRecoveryMode::EitherTupleOrPipe, ), - _ => unreachable!(), })?) } - - NonterminalKind::Expr | NonterminalKind::Expr2021 { inferred: _ } => { - NtExpr(self.parse_expr_force_collect()?) - } + NonterminalKind::Expr(_) => NtExpr(self.parse_expr_force_collect()?), NonterminalKind::Literal => { // The `:literal` matcher does not support attributes NtLiteral(self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?) } - NonterminalKind::Ty => { NtTy(self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?) } - // this could be handled like a token, since it is one NonterminalKind::Ident => { return if let Some((ident, is_raw)) = get_macro_ident(&self.token) { diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 129f62fb43d1a..64c1cda6e24b1 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -11,6 +11,7 @@ #![unstable(feature = "f128", issue = "116909")] +use crate::convert::FloatToInt; use crate::mem; /// Basic mathematical constants. @@ -220,21 +221,142 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] pub const MAX_10_EXP: i32 = 4_932; + /// Not a Number (NaN). + /// + /// Note that IEEE 754 doesn't define just a single NaN value; + /// a plethora of bit patterns are considered to be NaN. + /// Furthermore, the standard makes a difference + /// between a "signaling" and a "quiet" NaN, + /// and allows inspecting its "payload" (the unspecified bits in the bit pattern). + /// This constant isn't guaranteed to equal to any specific NaN bitpattern, + /// and the stability of its representation over Rust versions + /// and target platforms isn't guaranteed. + #[cfg(not(bootstrap))] + #[allow(clippy::eq_op)] + #[rustc_diagnostic_item = "f128_nan"] + #[unstable(feature = "f128", issue = "116909")] + pub const NAN: f128 = 0.0_f128 / 0.0_f128; + + /// Infinity (∞). + #[cfg(not(bootstrap))] + #[unstable(feature = "f128", issue = "116909")] + pub const INFINITY: f128 = 1.0_f128 / 0.0_f128; + + /// Negative infinity (−∞). + #[cfg(not(bootstrap))] + #[unstable(feature = "f128", issue = "116909")] + pub const NEG_INFINITY: f128 = -1.0_f128 / 0.0_f128; + + /// Sign bit + #[cfg(not(bootstrap))] + pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; + + /// Minimum representable positive value (min subnormal) + #[cfg(not(bootstrap))] + const TINY_BITS: u128 = 0x1; + + /// Minimum representable negative value (min negative subnormal) + #[cfg(not(bootstrap))] + const NEG_TINY_BITS: u128 = Self::TINY_BITS | Self::SIGN_MASK; + /// Returns `true` if this value is NaN. + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `unordtf2` is available + /// + /// let nan = f128::NAN; + /// let f = 7.0_f128; + /// + /// assert!(nan.is_nan()); + /// assert!(!f.is_nan()); + /// # } + /// ``` #[inline] #[must_use] + #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :) pub const fn is_nan(self) -> bool { self != self } + // FIXME(#50145): `abs` is publicly unavailable in core due to + // concerns about portability, so this implementation is for + // private use internally. + #[inline] + #[cfg(not(bootstrap))] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub(crate) const fn abs_private(self) -> f128 { + // SAFETY: This transmutation is fine. Probably. For the reasons std is using it. + unsafe { + mem::transmute::(mem::transmute::(self) & !Self::SIGN_MASK) + } + } + + /// Returns `true` if this value is positive infinity or negative infinity, and + /// `false` otherwise. + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `eqtf2` is available + /// + /// let f = 7.0f128; + /// let inf = f128::INFINITY; + /// let neg_inf = f128::NEG_INFINITY; + /// let nan = f128::NAN; + /// + /// assert!(!f.is_infinite()); + /// assert!(!nan.is_infinite()); + /// + /// assert!(inf.is_infinite()); + /// assert!(neg_inf.is_infinite()); + /// # } + /// ``` + #[inline] + #[must_use] + #[cfg(not(bootstrap))] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn is_infinite(self) -> bool { + (self == f128::INFINITY) | (self == f128::NEG_INFINITY) + } + + /// Returns `true` if this number is neither infinite nor NaN. + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `lttf2` is available + /// + /// let f = 7.0f128; + /// let inf: f128 = f128::INFINITY; + /// let neg_inf: f128 = f128::NEG_INFINITY; + /// let nan: f128 = f128::NAN; + /// + /// assert!(f.is_finite()); + /// + /// assert!(!nan.is_finite()); + /// assert!(!inf.is_finite()); + /// assert!(!neg_inf.is_finite()); + /// # } + /// ``` + #[inline] + #[must_use] + #[cfg(not(bootstrap))] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn is_finite(self) -> bool { + // There's no need to handle NaN separately: if self is NaN, + // the comparison is not true, exactly as desired. + self.abs_private() < Self::INFINITY + } + /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with /// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that /// the bit pattern of NaNs are conserved over arithmetic operations, the result of /// `is_sign_positive` on a NaN might produce an unexpected result in some cases. - /// See [explanation of NaN as a special value](f32) for more info. + /// See [explanation of NaN as a special value](f128) for more info. /// /// ``` /// #![feature(f128)] @@ -257,7 +379,7 @@ impl f128 { /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that /// the bit pattern of NaNs are conserved over arithmetic operations, the result of /// `is_sign_negative` on a NaN might produce an unexpected result in some cases. - /// See [explanation of NaN as a special value](f32) for more info. + /// See [explanation of NaN as a special value](f128) for more info. /// /// ``` /// #![feature(f128)] @@ -278,6 +400,216 @@ impl f128 { (self.to_bits() & (1 << 127)) != 0 } + /// Returns the least number greater than `self`. + /// + /// Let `TINY` be the smallest representable positive `f128`. Then, + /// - if `self.is_nan()`, this returns `self`; + /// - if `self` is [`NEG_INFINITY`], this returns [`MIN`]; + /// - if `self` is `-TINY`, this returns -0.0; + /// - if `self` is -0.0 or +0.0, this returns `TINY`; + /// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`]; + /// - otherwise the unique least value greater than `self` is returned. + /// + /// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x` + /// is finite `x == x.next_up().next_down()` also holds. + /// + /// ```rust + /// #![feature(f128)] + /// #![feature(float_next_up_down)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `eqtf2` is available + /// + /// // f128::EPSILON is the difference between 1.0 and the next number up. + /// assert_eq!(1.0f128.next_up(), 1.0 + f128::EPSILON); + /// // But not for most numbers. + /// assert!(0.1f128.next_up() < 0.1 + f128::EPSILON); + /// assert_eq!(4611686018427387904f128.next_up(), 4611686018427387904.000000000000001); + /// # } + /// ``` + /// + /// [`NEG_INFINITY`]: Self::NEG_INFINITY + /// [`INFINITY`]: Self::INFINITY + /// [`MIN`]: Self::MIN + /// [`MAX`]: Self::MAX + #[inline] + #[cfg(not(bootstrap))] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_next_up_down", issue = "91399")] + pub fn next_up(self) -> Self { + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. + let bits = self.to_bits(); + if self.is_nan() || bits == Self::INFINITY.to_bits() { + return self; + } + + let abs = bits & !Self::SIGN_MASK; + let next_bits = if abs == 0 { + Self::TINY_BITS + } else if bits == abs { + bits + 1 + } else { + bits - 1 + }; + Self::from_bits(next_bits) + } + + /// Returns the greatest number less than `self`. + /// + /// Let `TINY` be the smallest representable positive `f128`. Then, + /// - if `self.is_nan()`, this returns `self`; + /// - if `self` is [`INFINITY`], this returns [`MAX`]; + /// - if `self` is `TINY`, this returns 0.0; + /// - if `self` is -0.0 or +0.0, this returns `-TINY`; + /// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`]; + /// - otherwise the unique greatest value less than `self` is returned. + /// + /// The identity `x.next_down() == -(-x).next_up()` holds for all non-NaN `x`. When `x` + /// is finite `x == x.next_down().next_up()` also holds. + /// + /// ```rust + /// #![feature(f128)] + /// #![feature(float_next_up_down)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `eqtf2` is available + /// + /// let x = 1.0f128; + /// // Clamp value into range [0, 1). + /// let clamped = x.clamp(0.0, 1.0f128.next_down()); + /// assert!(clamped < 1.0); + /// assert_eq!(clamped.next_up(), 1.0); + /// # } + /// ``` + /// + /// [`NEG_INFINITY`]: Self::NEG_INFINITY + /// [`INFINITY`]: Self::INFINITY + /// [`MIN`]: Self::MIN + /// [`MAX`]: Self::MAX + #[inline] + #[cfg(not(bootstrap))] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_next_up_down", issue = "91399")] + pub fn next_down(self) -> Self { + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. + let bits = self.to_bits(); + if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() { + return self; + } + + let abs = bits & !Self::SIGN_MASK; + let next_bits = if abs == 0 { + Self::NEG_TINY_BITS + } else if bits == abs { + bits - 1 + } else { + bits + 1 + }; + Self::from_bits(next_bits) + } + + /// Takes the reciprocal (inverse) of a number, `1/x`. + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `eqtf2` is available + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.recip() - (1.0 / x)).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[cfg(not(bootstrap))] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn recip(self) -> Self { + 1.0 / self + } + + /// Converts radians to degrees. + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `eqtf2` is available + /// + /// let angle = std::f128::consts::PI; + /// + /// let abs_difference = (angle.to_degrees() - 180.0).abs(); + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[cfg(not(bootstrap))] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_degrees(self) -> Self { + // Use a literal for better precision. + const PIS_IN_180: f128 = 57.2957795130823208767981548141051703324054724665643215491602_f128; + self * PIS_IN_180 + } + + /// Converts degrees to radians. + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `eqtf2` is available + /// + /// let angle = 180.0f128; + /// + /// let abs_difference = (angle.to_radians() - std::f128::consts::PI).abs(); + /// + /// assert!(abs_difference <= 1e-30); + /// # } + /// ``` + #[inline] + #[cfg(not(bootstrap))] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_radians(self) -> f128 { + // Use a literal for better precision. + const RADS_PER_DEG: f128 = + 0.0174532925199432957692369076848861271344287188854172545609719_f128; + self * RADS_PER_DEG + } + + /// Rounds toward zero and converts to any primitive integer type, + /// assuming that the value is finite and fits in that type. + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `float*itf` is available + /// + /// let value = 4.6_f128; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, 4); + /// + /// let value = -128.9_f128; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, i8::MIN); + /// # } + /// ``` + /// + /// # Safety + /// + /// The value must: + /// + /// * Not be `NaN` + /// * Not be infinite + /// * Be representable in the return type `Int`, after truncating off its fractional part + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub unsafe fn to_int_unchecked(self) -> Int + where + Self: FloatToInt, + { + // SAFETY: the caller must uphold the safety contract for + // `FloatToInt::to_int_unchecked`. + unsafe { FloatToInt::::to_int_unchecked(self) } + } + /// Raw transmutation to `u128`. /// /// This is currently identical to `transmute::(self)` on all platforms. @@ -287,6 +619,14 @@ impl f128 { /// /// Note that this function is distinct from `as` casting, which attempts to /// preserve the *numeric* value, and not the bitwise value. + /// + /// ``` + /// #![feature(f128)] + /// + /// # // FIXME(f16_f128): enable this once const casting works + /// # // assert_ne!((1f128).to_bits(), 1f128 as u128); // to_bits() is not casting! + /// assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); + /// ``` #[inline] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] @@ -326,6 +666,15 @@ impl f128 { /// /// Note that this function is distinct from `as` casting, which attempts to /// preserve the *numeric* value, and not the bitwise value. + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `eqtf2` is available + /// + /// let v = f128::from_bits(0x40029000000000000000000000000000); + /// assert_eq!(v, 12.5); + /// # } + /// ``` #[inline] #[must_use] #[unstable(feature = "f128", issue = "116909")] @@ -335,4 +684,311 @@ impl f128 { // Stability concerns. unsafe { mem::transmute(v) } } + + /// Return the memory representation of this floating point number as a byte array in + /// big-endian (network) byte order. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// + /// let bytes = 12.5f128.to_be_bytes(); + /// assert_eq!( + /// bytes, + /// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// ); + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_be_bytes(self) -> [u8; 16] { + self.to_bits().to_be_bytes() + } + + /// Return the memory representation of this floating point number as a byte array in + /// little-endian byte order. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// + /// let bytes = 12.5f128.to_le_bytes(); + /// assert_eq!( + /// bytes, + /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40] + /// ); + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_le_bytes(self) -> [u8; 16] { + self.to_bits().to_le_bytes() + } + + /// Return the memory representation of this floating point number as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead. + /// + /// [`to_be_bytes`]: f128::to_be_bytes + /// [`to_le_bytes`]: f128::to_le_bytes + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// + /// let bytes = 12.5f128.to_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + /// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// } else { + /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40] + /// } + /// ); + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_ne_bytes(self) -> [u8; 16] { + self.to_bits().to_ne_bytes() + } + + /// Create a floating point value from its representation as a byte array in big endian. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `eqtf2` is available + /// + /// let value = f128::from_be_bytes( + /// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// ); + /// assert_eq!(value, 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + pub fn from_be_bytes(bytes: [u8; 16]) -> Self { + Self::from_bits(u128::from_be_bytes(bytes)) + } + + /// Create a floating point value from its representation as a byte array in little endian. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `eqtf2` is available + /// + /// let value = f128::from_le_bytes( + /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40] + /// ); + /// assert_eq!(value, 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + pub fn from_le_bytes(bytes: [u8; 16]) -> Self { + Self::from_bits(u128::from_le_bytes(bytes)) + } + + /// Create a floating point value from its representation as a byte array in native endian. + /// + /// As the target platform's native endianness is used, portable code + /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as + /// appropriate instead. + /// + /// [`from_be_bytes`]: f128::from_be_bytes + /// [`from_le_bytes`]: f128::from_le_bytes + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `eqtf2` is available + /// + /// let value = f128::from_ne_bytes(if cfg!(target_endian = "big") { + /// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// } else { + /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40] + /// }); + /// assert_eq!(value, 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + pub fn from_ne_bytes(bytes: [u8; 16]) -> Self { + Self::from_bits(u128::from_ne_bytes(bytes)) + } + + /// Return the ordering between `self` and `other`. + /// + /// Unlike the standard partial comparison between floating point numbers, + /// this comparison always produces an ordering in accordance to + /// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision) + /// floating point standard. The values are ordered in the following sequence: + /// + /// - negative quiet NaN + /// - negative signaling NaN + /// - negative infinity + /// - negative numbers + /// - negative subnormal numbers + /// - negative zero + /// - positive zero + /// - positive subnormal numbers + /// - positive numbers + /// - positive infinity + /// - positive signaling NaN + /// - positive quiet NaN. + /// + /// The ordering established by this function does not always agree with the + /// [`PartialOrd`] and [`PartialEq`] implementations of `f128`. For example, + /// they consider negative and positive zero equal, while `total_cmp` + /// doesn't. + /// + /// The interpretation of the signaling NaN bit follows the definition in + /// the IEEE 754 standard, which may not match the interpretation by some of + /// the older, non-conformant (e.g. MIPS) hardware implementations. + /// + /// # Example + /// + /// ``` + /// #![feature(f128)] + /// + /// struct GoodBoy { + /// name: &'static str, + /// weight: f128, + /// } + /// + /// let mut bois = vec![ + /// GoodBoy { name: "Pucci", weight: 0.1 }, + /// GoodBoy { name: "Woofer", weight: 99.0 }, + /// GoodBoy { name: "Yapper", weight: 10.0 }, + /// GoodBoy { name: "Chonk", weight: f128::INFINITY }, + /// GoodBoy { name: "Abs. Unit", weight: f128::NAN }, + /// GoodBoy { name: "Floaty", weight: -5.0 }, + /// ]; + /// + /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); + /// + /// // `f128::NAN` could be positive or negative, which will affect the sort order. + /// if f128::NAN.is_sign_negative() { + /// bois.into_iter().map(|b| b.weight) + /// .zip([f128::NAN, -5.0, 0.1, 10.0, 99.0, f128::INFINITY].iter()) + /// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits())) + /// } else { + /// bois.into_iter().map(|b| b.weight) + /// .zip([-5.0, 0.1, 10.0, 99.0, f128::INFINITY, f128::NAN].iter()) + /// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits())) + /// } + /// ``` + #[inline] + #[must_use] + #[cfg(not(bootstrap))] + #[unstable(feature = "f128", issue = "116909")] + pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { + let mut left = self.to_bits() as i128; + let mut right = other.to_bits() as i128; + + // In case of negatives, flip all the bits except the sign + // to achieve a similar layout as two's complement integers + // + // Why does this work? IEEE 754 floats consist of three fields: + // Sign bit, exponent and mantissa. The set of exponent and mantissa + // fields as a whole have the property that their bitwise order is + // equal to the numeric magnitude where the magnitude is defined. + // The magnitude is not normally defined on NaN values, but + // IEEE 754 totalOrder defines the NaN values also to follow the + // bitwise order. This leads to order explained in the doc comment. + // However, the representation of magnitude is the same for negative + // and positive numbers – only the sign bit is different. + // To easily compare the floats as signed integers, we need to + // flip the exponent and mantissa bits in case of negative numbers. + // We effectively convert the numbers to "two's complement" form. + // + // To do the flipping, we construct a mask and XOR against it. + // We branchlessly calculate an "all-ones except for the sign bit" + // mask from negative-signed values: right shifting sign-extends + // the integer, so we "fill" the mask with sign bits, and then + // convert to unsigned to push one more zero bit. + // On positive values, the mask is all zeros, so it's a no-op. + left ^= (((left >> 127) as u128) >> 1) as i128; + right ^= (((right >> 127) as u128) >> 1) as i128; + + left.cmp(&right) + } + + /// Restrict a value to a certain interval unless it is NaN. + /// + /// Returns `max` if `self` is greater than `max`, and `min` if `self` is + /// less than `min`. Otherwise this returns `self`. + /// + /// Note that this function returns NaN if the initial value was NaN as + /// well. + /// + /// # Panics + /// + /// Panics if `min > max`, `min` is NaN, or `max` is NaN. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(target_arch = "x86_64")] { // FIXME(f16_f128): remove when `{eq,gt,unord}tf` are available + /// + /// assert!((-3.0f128).clamp(-2.0, 1.0) == -2.0); + /// assert!((0.0f128).clamp(-2.0, 1.0) == 0.0); + /// assert!((2.0f128).clamp(-2.0, 1.0) == 1.0); + /// assert!((f128::NAN).clamp(-2.0, 1.0).is_nan()); + /// # } + /// ``` + #[inline] + #[cfg(not(bootstrap))] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn clamp(mut self, min: f128, max: f128) -> f128 { + assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}"); + if self < min { + self = min; + } + if self > max { + self = max; + } + self + } } diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 7a488cd6bf6fa..3c470b5ea97bf 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -11,6 +11,7 @@ #![unstable(feature = "f16", issue = "116909")] +use crate::convert::FloatToInt; use crate::mem; /// Basic mathematical constants. @@ -215,21 +216,140 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] pub const MAX_10_EXP: i32 = 4; + /// Not a Number (NaN). + /// + /// Note that IEEE 754 doesn't define just a single NaN value; + /// a plethora of bit patterns are considered to be NaN. + /// Furthermore, the standard makes a difference + /// between a "signaling" and a "quiet" NaN, + /// and allows inspecting its "payload" (the unspecified bits in the bit pattern). + /// This constant isn't guaranteed to equal to any specific NaN bitpattern, + /// and the stability of its representation over Rust versions + /// and target platforms isn't guaranteed. + #[cfg(not(bootstrap))] + #[allow(clippy::eq_op)] + #[rustc_diagnostic_item = "f16_nan"] + #[unstable(feature = "f16", issue = "116909")] + pub const NAN: f16 = 0.0_f16 / 0.0_f16; + + /// Infinity (∞). + #[cfg(not(bootstrap))] + #[unstable(feature = "f16", issue = "116909")] + pub const INFINITY: f16 = 1.0_f16 / 0.0_f16; + + /// Negative infinity (−∞). + #[cfg(not(bootstrap))] + #[unstable(feature = "f16", issue = "116909")] + pub const NEG_INFINITY: f16 = -1.0_f16 / 0.0_f16; + + /// Sign bit + #[cfg(not(bootstrap))] + const SIGN_MASK: u16 = 0x8000; + + /// Minimum representable positive value (min subnormal) + #[cfg(not(bootstrap))] + const TINY_BITS: u16 = 0x1; + + /// Minimum representable negative value (min negative subnormal) + #[cfg(not(bootstrap))] + const NEG_TINY_BITS: u16 = Self::TINY_BITS | Self::SIGN_MASK; + /// Returns `true` if this value is NaN. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let nan = f16::NAN; + /// let f = 7.0_f16; + /// + /// assert!(nan.is_nan()); + /// assert!(!f.is_nan()); + /// # } + /// ``` #[inline] #[must_use] + #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :) pub const fn is_nan(self) -> bool { self != self } + // FIXMxE(#50145): `abs` is publicly unavailable in core due to + // concerns about portability, so this implementation is for + // private use internally. + #[inline] + #[cfg(not(bootstrap))] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub(crate) const fn abs_private(self) -> f16 { + // SAFETY: This transmutation is fine. Probably. For the reasons std is using it. + unsafe { mem::transmute::(mem::transmute::(self) & !Self::SIGN_MASK) } + } + + /// Returns `true` if this value is positive infinity or negative infinity, and + /// `false` otherwise. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let f = 7.0f16; + /// let inf = f16::INFINITY; + /// let neg_inf = f16::NEG_INFINITY; + /// let nan = f16::NAN; + /// + /// assert!(!f.is_infinite()); + /// assert!(!nan.is_infinite()); + /// + /// assert!(inf.is_infinite()); + /// assert!(neg_inf.is_infinite()); + /// # } + /// ``` + #[inline] + #[must_use] + #[cfg(not(bootstrap))] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn is_infinite(self) -> bool { + (self == f16::INFINITY) | (self == f16::NEG_INFINITY) + } + + /// Returns `true` if this number is neither infinite nor NaN. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let f = 7.0f16; + /// let inf: f16 = f16::INFINITY; + /// let neg_inf: f16 = f16::NEG_INFINITY; + /// let nan: f16 = f16::NAN; + /// + /// assert!(f.is_finite()); + /// + /// assert!(!nan.is_finite()); + /// assert!(!inf.is_finite()); + /// assert!(!neg_inf.is_finite()); + /// # } + /// ``` + #[inline] + #[must_use] + #[cfg(not(bootstrap))] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn is_finite(self) -> bool { + // There's no need to handle NaN separately: if self is NaN, + // the comparison is not true, exactly as desired. + self.abs_private() < Self::INFINITY + } + /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with /// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that /// the bit pattern of NaNs are conserved over arithmetic operations, the result of /// `is_sign_positive` on a NaN might produce an unexpected result in some cases. - /// See [explanation of NaN as a special value](f32) for more info. + /// See [explanation of NaN as a special value](f16) for more info. /// /// ``` /// #![feature(f16)] @@ -252,7 +372,7 @@ impl f16 { /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that /// the bit pattern of NaNs are conserved over arithmetic operations, the result of /// `is_sign_negative` on a NaN might produce an unexpected result in some cases. - /// See [explanation of NaN as a special value](f32) for more info. + /// See [explanation of NaN as a special value](f16) for more info. /// /// ``` /// #![feature(f16)] @@ -273,6 +393,205 @@ impl f16 { (self.to_bits() & (1 << 15)) != 0 } + /// Returns the least number greater than `self`. + /// + /// Let `TINY` be the smallest representable positive `f16`. Then, + /// - if `self.is_nan()`, this returns `self`; + /// - if `self` is [`NEG_INFINITY`], this returns [`MIN`]; + /// - if `self` is `-TINY`, this returns -0.0; + /// - if `self` is -0.0 or +0.0, this returns `TINY`; + /// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`]; + /// - otherwise the unique least value greater than `self` is returned. + /// + /// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x` + /// is finite `x == x.next_up().next_down()` also holds. + /// + /// ```rust + /// #![feature(f16)] + /// #![feature(float_next_up_down)] + /// + /// // f16::EPSILON is the difference between 1.0 and the next number up. + /// assert_eq!(1.0f16.next_up(), 1.0 + f16::EPSILON); + /// // But not for most numbers. + /// assert!(0.1f16.next_up() < 0.1 + f16::EPSILON); + /// assert_eq!(4356f16.next_up(), 4360.0); + /// ``` + /// + /// [`NEG_INFINITY`]: Self::NEG_INFINITY + /// [`INFINITY`]: Self::INFINITY + /// [`MIN`]: Self::MIN + /// [`MAX`]: Self::MAX + #[inline] + #[cfg(not(bootstrap))] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_next_up_down", issue = "91399")] + pub fn next_up(self) -> Self { + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. + let bits = self.to_bits(); + if self.is_nan() || bits == Self::INFINITY.to_bits() { + return self; + } + + let abs = bits & !Self::SIGN_MASK; + let next_bits = if abs == 0 { + Self::TINY_BITS + } else if bits == abs { + bits + 1 + } else { + bits - 1 + }; + Self::from_bits(next_bits) + } + + /// Returns the greatest number less than `self`. + /// + /// Let `TINY` be the smallest representable positive `f16`. Then, + /// - if `self.is_nan()`, this returns `self`; + /// - if `self` is [`INFINITY`], this returns [`MAX`]; + /// - if `self` is `TINY`, this returns 0.0; + /// - if `self` is -0.0 or +0.0, this returns `-TINY`; + /// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`]; + /// - otherwise the unique greatest value less than `self` is returned. + /// + /// The identity `x.next_down() == -(-x).next_up()` holds for all non-NaN `x`. When `x` + /// is finite `x == x.next_down().next_up()` also holds. + /// + /// ```rust + /// #![feature(f16)] + /// #![feature(float_next_up_down)] + /// + /// let x = 1.0f16; + /// // Clamp value into range [0, 1). + /// let clamped = x.clamp(0.0, 1.0f16.next_down()); + /// assert!(clamped < 1.0); + /// assert_eq!(clamped.next_up(), 1.0); + /// ``` + /// + /// [`NEG_INFINITY`]: Self::NEG_INFINITY + /// [`INFINITY`]: Self::INFINITY + /// [`MIN`]: Self::MIN + /// [`MAX`]: Self::MAX + #[inline] + #[cfg(not(bootstrap))] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_next_up_down", issue = "91399")] + pub fn next_down(self) -> Self { + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. + let bits = self.to_bits(); + if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() { + return self; + } + + let abs = bits & !Self::SIGN_MASK; + let next_bits = if abs == 0 { + Self::NEG_TINY_BITS + } else if bits == abs { + bits - 1 + } else { + bits + 1 + }; + Self::from_bits(next_bits) + } + + /// Takes the reciprocal (inverse) of a number, `1/x`. + /// + /// ``` + /// #![feature(f16)] + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.recip() - (1.0 / x)).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// ``` + #[inline] + #[cfg(not(bootstrap))] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn recip(self) -> Self { + 1.0 / self + } + + /// Converts radians to degrees. + /// + /// ``` + /// #![feature(f16)] + /// + /// let angle = std::f16::consts::PI; + /// + /// let abs_difference = (angle.to_degrees() - 180.0).abs(); + /// assert!(abs_difference <= 0.5); + /// ``` + #[inline] + #[cfg(not(bootstrap))] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_degrees(self) -> Self { + // Use a literal for better precision. + const PIS_IN_180: f16 = 57.2957795130823208767981548141051703_f16; + self * PIS_IN_180 + } + + /// Converts degrees to radians. + /// + /// ``` + /// #![feature(f16)] + /// + /// let angle = 180.0f16; + /// + /// let abs_difference = (angle.to_radians() - std::f16::consts::PI).abs(); + /// + /// assert!(abs_difference <= 0.01); + /// ``` + #[inline] + #[cfg(not(bootstrap))] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_radians(self) -> f16 { + // Use a literal for better precision. + const RADS_PER_DEG: f16 = 0.017453292519943295769236907684886_f16; + self * RADS_PER_DEG + } + + /// Rounds toward zero and converts to any primitive integer type, + /// assuming that the value is finite and fits in that type. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let value = 4.6_f16; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, 4); + /// + /// let value = -128.9_f16; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, i8::MIN); + /// # } + /// ``` + /// + /// # Safety + /// + /// The value must: + /// + /// * Not be `NaN` + /// * Not be infinite + /// * Be representable in the return type `Int`, after truncating off its fractional part + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub unsafe fn to_int_unchecked(self) -> Int + where + Self: FloatToInt, + { + // SAFETY: the caller must uphold the safety contract for + // `FloatToInt::to_int_unchecked`. + unsafe { FloatToInt::::to_int_unchecked(self) } + } + /// Raw transmutation to `u16`. /// /// This is currently identical to `transmute::(self)` on all platforms. @@ -282,6 +601,16 @@ impl f16 { /// /// Note that this function is distinct from `as` casting, which attempts to /// preserve the *numeric* value, and not the bitwise value. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// # // FIXME(f16_f128): enable this once const casting works + /// # // assert_ne!((1f16).to_bits(), 1f16 as u128); // to_bits() is not casting! + /// assert_eq!((12.5f16).to_bits(), 0x4a40); + /// # } + /// ``` #[inline] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] @@ -321,6 +650,15 @@ impl f16 { /// /// Note that this function is distinct from `as` casting, which attempts to /// preserve the *numeric* value, and not the bitwise value. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let v = f16::from_bits(0x4a40); + /// assert_eq!(v, 12.5); + /// # } + /// ``` #[inline] #[must_use] #[unstable(feature = "f16", issue = "116909")] @@ -330,4 +668,293 @@ impl f16 { // Stability concerns. unsafe { mem::transmute(v) } } + + /// Return the memory representation of this floating point number as a byte array in + /// big-endian (network) byte order. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// + /// let bytes = 12.5f16.to_be_bytes(); + /// assert_eq!(bytes, [0x4a, 0x40]); + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_be_bytes(self) -> [u8; 2] { + self.to_bits().to_be_bytes() + } + + /// Return the memory representation of this floating point number as a byte array in + /// little-endian byte order. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// + /// let bytes = 12.5f16.to_le_bytes(); + /// assert_eq!(bytes, [0x40, 0x4a]); + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_le_bytes(self) -> [u8; 2] { + self.to_bits().to_le_bytes() + } + + /// Return the memory representation of this floating point number as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead. + /// + /// [`to_be_bytes`]: f16::to_be_bytes + /// [`to_le_bytes`]: f16::to_le_bytes + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// + /// let bytes = 12.5f16.to_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + /// [0x4a, 0x40] + /// } else { + /// [0x40, 0x4a] + /// } + /// ); + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_ne_bytes(self) -> [u8; 2] { + self.to_bits().to_ne_bytes() + } + + /// Create a floating point value from its representation as a byte array in big endian. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let value = f16::from_be_bytes([0x4a, 0x40]); + /// assert_eq!(value, 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + pub fn from_be_bytes(bytes: [u8; 2]) -> Self { + Self::from_bits(u16::from_be_bytes(bytes)) + } + + /// Create a floating point value from its representation as a byte array in little endian. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let value = f16::from_le_bytes([0x40, 0x4a]); + /// assert_eq!(value, 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + pub fn from_le_bytes(bytes: [u8; 2]) -> Self { + Self::from_bits(u16::from_le_bytes(bytes)) + } + + /// Create a floating point value from its representation as a byte array in native endian. + /// + /// As the target platform's native endianness is used, portable code + /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as + /// appropriate instead. + /// + /// [`from_be_bytes`]: f16::from_be_bytes + /// [`from_le_bytes`]: f16::from_le_bytes + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let value = f16::from_ne_bytes(if cfg!(target_endian = "big") { + /// [0x4a, 0x40] + /// } else { + /// [0x40, 0x4a] + /// }); + /// assert_eq!(value, 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + pub fn from_ne_bytes(bytes: [u8; 2]) -> Self { + Self::from_bits(u16::from_ne_bytes(bytes)) + } + + /// Return the ordering between `self` and `other`. + /// + /// Unlike the standard partial comparison between floating point numbers, + /// this comparison always produces an ordering in accordance to + /// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision) + /// floating point standard. The values are ordered in the following sequence: + /// + /// - negative quiet NaN + /// - negative signaling NaN + /// - negative infinity + /// - negative numbers + /// - negative subnormal numbers + /// - negative zero + /// - positive zero + /// - positive subnormal numbers + /// - positive numbers + /// - positive infinity + /// - positive signaling NaN + /// - positive quiet NaN. + /// + /// The ordering established by this function does not always agree with the + /// [`PartialOrd`] and [`PartialEq`] implementations of `f16`. For example, + /// they consider negative and positive zero equal, while `total_cmp` + /// doesn't. + /// + /// The interpretation of the signaling NaN bit follows the definition in + /// the IEEE 754 standard, which may not match the interpretation by some of + /// the older, non-conformant (e.g. MIPS) hardware implementations. + /// + /// # Example + /// + /// ``` + /// #![feature(f16)] + /// + /// struct GoodBoy { + /// name: &'static str, + /// weight: f16, + /// } + /// + /// let mut bois = vec![ + /// GoodBoy { name: "Pucci", weight: 0.1 }, + /// GoodBoy { name: "Woofer", weight: 99.0 }, + /// GoodBoy { name: "Yapper", weight: 10.0 }, + /// GoodBoy { name: "Chonk", weight: f16::INFINITY }, + /// GoodBoy { name: "Abs. Unit", weight: f16::NAN }, + /// GoodBoy { name: "Floaty", weight: -5.0 }, + /// ]; + /// + /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); + /// + /// // `f16::NAN` could be positive or negative, which will affect the sort order. + /// if f16::NAN.is_sign_negative() { + /// bois.into_iter().map(|b| b.weight) + /// .zip([f16::NAN, -5.0, 0.1, 10.0, 99.0, f16::INFINITY].iter()) + /// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits())) + /// } else { + /// bois.into_iter().map(|b| b.weight) + /// .zip([-5.0, 0.1, 10.0, 99.0, f16::INFINITY, f16::NAN].iter()) + /// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits())) + /// } + /// ``` + #[inline] + #[must_use] + #[cfg(not(bootstrap))] + #[unstable(feature = "f16", issue = "116909")] + pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { + let mut left = self.to_bits() as i16; + let mut right = other.to_bits() as i16; + + // In case of negatives, flip all the bits except the sign + // to achieve a similar layout as two's complement integers + // + // Why does this work? IEEE 754 floats consist of three fields: + // Sign bit, exponent and mantissa. The set of exponent and mantissa + // fields as a whole have the property that their bitwise order is + // equal to the numeric magnitude where the magnitude is defined. + // The magnitude is not normally defined on NaN values, but + // IEEE 754 totalOrder defines the NaN values also to follow the + // bitwise order. This leads to order explained in the doc comment. + // However, the representation of magnitude is the same for negative + // and positive numbers – only the sign bit is different. + // To easily compare the floats as signed integers, we need to + // flip the exponent and mantissa bits in case of negative numbers. + // We effectively convert the numbers to "two's complement" form. + // + // To do the flipping, we construct a mask and XOR against it. + // We branchlessly calculate an "all-ones except for the sign bit" + // mask from negative-signed values: right shifting sign-extends + // the integer, so we "fill" the mask with sign bits, and then + // convert to unsigned to push one more zero bit. + // On positive values, the mask is all zeros, so it's a no-op. + left ^= (((left >> 15) as u16) >> 1) as i16; + right ^= (((right >> 15) as u16) >> 1) as i16; + + left.cmp(&right) + } + + /// Restrict a value to a certain interval unless it is NaN. + /// + /// Returns `max` if `self` is greater than `max`, and `min` if `self` is + /// less than `min`. Otherwise this returns `self`. + /// + /// Note that this function returns NaN if the initial value was NaN as + /// well. + /// + /// # Panics + /// + /// Panics if `min > max`, `min` is NaN, or `max` is NaN. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// assert!((-3.0f16).clamp(-2.0, 1.0) == -2.0); + /// assert!((0.0f16).clamp(-2.0, 1.0) == 0.0); + /// assert!((2.0f16).clamp(-2.0, 1.0) == 1.0); + /// assert!((f16::NAN).clamp(-2.0, 1.0).is_nan()); + /// # } + /// ``` + #[inline] + #[cfg(not(bootstrap))] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn clamp(mut self, min: f16, max: f16) -> f16 { + assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}"); + if self < min { + self = min; + } + if self > max { + self = max; + } + self + } } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 9d34d3da9e955..b9c84a66ed138 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -490,6 +490,21 @@ impl f32 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32; + /// Sign bit + const SIGN_MASK: u32 = 0x8000_0000; + + /// Exponent mask + const EXP_MASK: u32 = 0x7f80_0000; + + /// Mantissa mask + const MAN_MASK: u32 = 0x007f_ffff; + + /// Minimum representable positive value (min subnormal) + const TINY_BITS: u32 = 0x1; + + /// Minimum representable negative value (min negative subnormal) + const NEG_TINY_BITS: u32 = Self::TINY_BITS | Self::SIGN_MASK; + /// Returns `true` if this value is NaN. /// /// ``` @@ -515,7 +530,7 @@ impl f32 { #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub(crate) const fn abs_private(self) -> f32 { // SAFETY: This transmutation is fine. Probably. For the reasons std is using it. - unsafe { mem::transmute::(mem::transmute::(self) & 0x7fff_ffff) } + unsafe { mem::transmute::(mem::transmute::(self) & !Self::SIGN_MASK) } } /// Returns `true` if this value is positive infinity or negative infinity, and @@ -682,12 +697,9 @@ impl f32 { // runtime-deviating logic which may or may not be acceptable. #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] const unsafe fn partial_classify(self) -> FpCategory { - const EXP_MASK: u32 = 0x7f800000; - const MAN_MASK: u32 = 0x007fffff; - // SAFETY: The caller is not asking questions for which this will tell lies. let b = unsafe { mem::transmute::(self) }; - match (b & MAN_MASK, b & EXP_MASK) { + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, _ => FpCategory::Normal, @@ -699,12 +711,9 @@ impl f32 { // plus a transmute. We do not live in a just world, but we can make it more so. #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] const fn classify_bits(b: u32) -> FpCategory { - const EXP_MASK: u32 = 0x7f800000; - const MAN_MASK: u32 = 0x007fffff; - - match (b & MAN_MASK, b & EXP_MASK) { - (0, EXP_MASK) => FpCategory::Infinite, - (_, EXP_MASK) => FpCategory::Nan, + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { + (0, Self::EXP_MASK) => FpCategory::Infinite, + (_, Self::EXP_MASK) => FpCategory::Nan, (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, _ => FpCategory::Normal, @@ -787,19 +796,17 @@ impl f32 { #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { - // We must use strictly integer arithmetic to prevent denormals from - // flushing to zero after an arithmetic operation on some platforms. - const TINY_BITS: u32 = 0x1; // Smallest positive f32. - const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff; - + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. let bits = self.to_bits(); if self.is_nan() || bits == Self::INFINITY.to_bits() { return self; } - let abs = bits & CLEAR_SIGN_MASK; + let abs = bits & !Self::SIGN_MASK; let next_bits = if abs == 0 { - TINY_BITS + Self::TINY_BITS } else if bits == abs { bits + 1 } else { @@ -837,19 +844,17 @@ impl f32 { #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { - // We must use strictly integer arithmetic to prevent denormals from - // flushing to zero after an arithmetic operation on some platforms. - const NEG_TINY_BITS: u32 = 0x8000_0001; // Smallest (in magnitude) negative f32. - const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff; - + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. let bits = self.to_bits(); if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() { return self; } - let abs = bits & CLEAR_SIGN_MASK; + let abs = bits & !Self::SIGN_MASK; let next_bits = if abs == 0 { - NEG_TINY_BITS + Self::NEG_TINY_BITS } else if bits == abs { bits - 1 } else { diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 95f021b2541ab..f8e4555fc44f2 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -489,6 +489,21 @@ impl f64 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64; + /// Sign bit + const SIGN_MASK: u64 = 0x8000_0000_0000_0000; + + /// Exponent mask + const EXP_MASK: u64 = 0x7ff0_0000_0000_0000; + + /// Mantissa mask + const MAN_MASK: u64 = 0x000f_ffff_ffff_ffff; + + /// Minimum representable positive value (min subnormal) + const TINY_BITS: u64 = 0x1; + + /// Minimum representable negative value (min negative subnormal) + const NEG_TINY_BITS: u64 = Self::TINY_BITS | Self::SIGN_MASK; + /// Returns `true` if this value is NaN. /// /// ``` @@ -514,9 +529,7 @@ impl f64 { #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub(crate) const fn abs_private(self) -> f64 { // SAFETY: This transmutation is fine. Probably. For the reasons std is using it. - unsafe { - mem::transmute::(mem::transmute::(self) & 0x7fff_ffff_ffff_ffff) - } + unsafe { mem::transmute::(mem::transmute::(self) & !Self::SIGN_MASK) } } /// Returns `true` if this value is positive infinity or negative infinity, and @@ -673,13 +686,10 @@ impl f64 { // and some normal floating point numbers truncated from an x87 FPU. #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] const unsafe fn partial_classify(self) -> FpCategory { - const EXP_MASK: u64 = 0x7ff0000000000000; - const MAN_MASK: u64 = 0x000fffffffffffff; - // SAFETY: The caller is not asking questions for which this will tell lies. let b = unsafe { mem::transmute::(self) }; - match (b & MAN_MASK, b & EXP_MASK) { - (0, EXP_MASK) => FpCategory::Infinite, + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { + (0, Self::EXP_MASK) => FpCategory::Infinite, (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, _ => FpCategory::Normal, @@ -691,12 +701,9 @@ impl f64 { // plus a transmute. We do not live in a just world, but we can make it more so. #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] const fn classify_bits(b: u64) -> FpCategory { - const EXP_MASK: u64 = 0x7ff0000000000000; - const MAN_MASK: u64 = 0x000fffffffffffff; - - match (b & MAN_MASK, b & EXP_MASK) { - (0, EXP_MASK) => FpCategory::Infinite, - (_, EXP_MASK) => FpCategory::Nan, + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { + (0, Self::EXP_MASK) => FpCategory::Infinite, + (_, Self::EXP_MASK) => FpCategory::Nan, (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, _ => FpCategory::Normal, @@ -756,7 +763,7 @@ impl f64 { // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus // applies to zeros and NaNs as well. // SAFETY: This is just transmuting to get the sign bit, it's fine. - unsafe { mem::transmute::(self) & 0x8000_0000_0000_0000 != 0 } + unsafe { mem::transmute::(self) & Self::SIGN_MASK != 0 } } #[must_use] @@ -797,19 +804,17 @@ impl f64 { #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { - // We must use strictly integer arithmetic to prevent denormals from - // flushing to zero after an arithmetic operation on some platforms. - const TINY_BITS: u64 = 0x1; // Smallest positive f64. - const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff; - + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. let bits = self.to_bits(); if self.is_nan() || bits == Self::INFINITY.to_bits() { return self; } - let abs = bits & CLEAR_SIGN_MASK; + let abs = bits & !Self::SIGN_MASK; let next_bits = if abs == 0 { - TINY_BITS + Self::TINY_BITS } else if bits == abs { bits + 1 } else { @@ -847,19 +852,17 @@ impl f64 { #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { - // We must use strictly integer arithmetic to prevent denormals from - // flushing to zero after an arithmetic operation on some platforms. - const NEG_TINY_BITS: u64 = 0x8000_0000_0000_0001; // Smallest (in magnitude) negative f64. - const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff; - + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. let bits = self.to_bits(); if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() { return self; } - let abs = bits & CLEAR_SIGN_MASK; + let abs = bits & !Self::SIGN_MASK; let next_bits = if abs == 0 { - NEG_TINY_BITS + Self::NEG_TINY_BITS } else if bits == abs { bits - 1 } else { diff --git a/library/std/build.rs b/library/std/build.rs index 7d975df545ecf..625070506ea7d 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -7,6 +7,10 @@ fn main() { let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").expect("CARGO_CFG_TARGET_VENDOR was not set"); let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("CARGO_CFG_TARGET_ENV was not set"); + let target_pointer_width: u32 = env::var("CARGO_CFG_TARGET_POINTER_WIDTH") + .expect("CARGO_CFG_TARGET_POINTER_WIDTH was not set") + .parse() + .unwrap(); println!("cargo:rustc-check-cfg=cfg(netbsd10)"); if target_os == "netbsd" && env::var("RUSTC_STD_NETBSD10").is_ok() { @@ -70,4 +74,52 @@ fn main() { println!("cargo:rustc-cfg=backtrace_in_libstd"); println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap()); + + // Emit these on platforms that have no known ABI bugs, LLVM selection bugs, lowering bugs, + // missing symbols, or other problems, to determine when tests get run. + // If more broken platforms are found, please update the tracking issue at + // + println!("cargo:rustc-check-cfg=cfg(reliable_f16)"); + println!("cargo:rustc-check-cfg=cfg(reliable_f128)"); + + let has_reliable_f16 = match (target_arch.as_str(), target_os.as_str()) { + // Selection failure until recent LLVM + // FIXME(llvm19): can probably be removed at the version bump + ("loongarch64", _) => false, + // Selection failure + ("s390x", _) => false, + // Unsupported + ("arm64ec", _) => false, + // MinGW ABI bugs + ("x86", "windows") => false, + // x86 has ABI bugs that show up with optimizations. This should be partially fixed with + // the compiler-builtins update. + ("x86" | "x86_64", _) => false, + _ => true, + }; + + let has_reliable_f128 = match (target_arch.as_str(), target_os.as_str()) { + // Unsupported + ("arm64ec", _) => false, + // ABI and precision bugs + // + ("powerpc" | "powerpc64", _) => false, + // Selection bug + ("nvptx64", _) => false, + // ABI unsupported + ("sparc", _) => false, + // 64-bit Linux is about the only platform to have f128 symbols by default + (_, "linux") if target_pointer_width == 64 => true, + // Almost all OSs besides Linux are missing symbols until compiler-builtins can be + // updated will get some of these, the + // next CB update should get the rest. + _ => false, + }; + + if has_reliable_f16 { + println!("cargo:rustc-cfg=reliable_f16"); + } + if has_reliable_f128 { + println!("cargo:rustc-cfg=reliable_f128"); + } } diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index 491235a872eaf..0591c6f517b44 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -32,4 +32,34 @@ impl f128 { pub fn powi(self, n: i32) -> f128 { unsafe { intrinsics::powif128(self, n) } } + + /// Computes the absolute value of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128)] { // FIXME(f16_f128): reliable_f128 + /// + /// let x = 3.5_f128; + /// let y = -3.5_f128; + /// + /// assert_eq!(x.abs(), x); + /// assert_eq!(y.abs(), -y); + /// + /// assert!(f128::NAN.abs().is_nan()); + /// # } + /// ``` + #[inline] + #[cfg(not(bootstrap))] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn abs(self) -> Self { + // FIXME(f16_f128): replace with `intrinsics::fabsf128` when available + // We don't do this now because LLVM has lowering bugs for f128 math. + Self::from_bits(self.to_bits() & !(1 << 127)) + } } diff --git a/library/std/src/f128/tests.rs b/library/std/src/f128/tests.rs index b64c7f856a15f..bd7a921c502a7 100644 --- a/library/std/src/f128/tests.rs +++ b/library/std/src/f128/tests.rs @@ -1,29 +1,31 @@ -#![allow(dead_code)] // FIXME(f16_f128): remove once constants are used +#![cfg(not(bootstrap))] +// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy +#![cfg(reliable_f128)] + +use crate::f128::consts; +use crate::num::*; /// Smallest number const TINY_BITS: u128 = 0x1; + /// Next smallest number const TINY_UP_BITS: u128 = 0x2; + /// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u128 = 0x7ffeffffffffffffffffffffffffffff; +const MAX_DOWN_BITS: u128 = 0x7ffefffffffffffffffffffffffffffe; + /// Zeroed exponent, full significant const LARGEST_SUBNORMAL_BITS: u128 = 0x0000ffffffffffffffffffffffffffff; + /// Exponent = 0b1, zeroed significand const SMALLEST_NORMAL_BITS: u128 = 0x00010000000000000000000000000000; + /// First pattern over the mantissa const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; + /// Second pattern over the mantissa const NAN_MASK2: u128 = 0x00005555555555555555555555555555; -/// Compare by value -#[allow(unused_macros)] -macro_rules! assert_f128_eq { - ($a:expr, $b:expr) => { - let (l, r): (&f128, &f128) = (&$a, &$b); - assert_eq!(*l, *r, "\na: {:#0130x}\nb: {:#0130x}", l.to_bits(), r.to_bits()) - }; -} - /// Compare by representation #[allow(unused_macros)] macro_rules! assert_f128_biteq { @@ -31,10 +33,503 @@ macro_rules! assert_f128_biteq { let (l, r): (&f128, &f128) = (&$a, &$b); let lb = l.to_bits(); let rb = r.to_bits(); - assert_eq!( - lb, rb, - "float {:?} is not bitequal to {:?}.\na: {:#0130x}\nb: {:#0130x}", - *l, *r, lb, rb - ); + assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}"); }; } + +#[test] +fn test_num_f128() { + test_num(10f128, 2f128); +} + +// FIXME(f16_f128): add min and max tests when available + +#[test] +fn test_nan() { + let nan: f128 = f128::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + // FIXME(f16_f128): classify + // assert!(!nan.is_normal()); + // assert_eq!(Fp::Nan, nan.classify()); +} + +#[test] +fn test_infinity() { + let inf: f128 = f128::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + // FIXME(f16_f128): classify + // assert!(!inf.is_normal()); + // assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + // FIXME(f16_f128): classify + // assert!(!neg_inf.is_normal()); + // assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f128 = 0.0f128; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + // FIXME(f16_f128): classify + // assert!(!zero.is_normal()); + // assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f128 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + // FIXME(f16_f128): classify + // assert!(!neg_zero.is_normal()); + // assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f128 = 1.0f128; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + // FIXME(f16_f128): classify + // assert!(one.is_normal()); + // assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f128.is_nan()); + assert!(!5.3f128.is_nan()); + assert!(!(-10.732f128).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f128.is_infinite()); + assert!(!42.8f128.is_infinite()); + assert!(!(-109.2f128).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f128.is_finite()); + assert!(42.8f128.is_finite()); + assert!((-109.2f128).is_finite()); +} + +// FIXME(f16_f128): add `test_is_normal` and `test_classify` when classify is working +// FIXME(f16_f128): add missing math functions when available + +#[test] +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!(f128::NAN.abs().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f128::INFINITY.is_sign_positive()); + assert!(1f128.is_sign_positive()); + assert!(0f128.is_sign_positive()); + assert!(!(-0f128).is_sign_positive()); + assert!(!(-1f128).is_sign_positive()); + assert!(!f128::NEG_INFINITY.is_sign_positive()); + assert!(!(1f128 / f128::NEG_INFINITY).is_sign_positive()); + assert!(f128::NAN.is_sign_positive()); + assert!(!(-f128::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f128::INFINITY.is_sign_negative()); + assert!(!1f128.is_sign_negative()); + assert!(!0f128.is_sign_negative()); + assert!((-0f128).is_sign_negative()); + assert!((-1f128).is_sign_negative()); + assert!(f128::NEG_INFINITY.is_sign_negative()); + assert!((1f128 / f128::NEG_INFINITY).is_sign_negative()); + assert!(!f128::NAN.is_sign_negative()); + assert!((-f128::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f128::from_bits(TINY_BITS); + let tiny_up = f128::from_bits(TINY_UP_BITS); + 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); + + // 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); +} + +#[test] +fn test_next_down() { + let tiny = f128::from_bits(TINY_BITS); + let tiny_up = f128::from_bits(TINY_UP_BITS); + 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); + + // 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); +} + +#[test] +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!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +fn test_to_degrees() { + let pi: f128 = consts::PI; + 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_approx_eq!((-5.8f128).to_degrees(), -332.315521); + assert_eq!(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_f128.to_degrees(), 57.2957795130823208767981548141051703); +} + +#[test] +fn test_to_radians() { + let pi: f128 = consts::PI; + 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_approx_eq!(154.6f128.to_radians(), 2.698279); + assert_approx_eq!((-332.31f128).to_radians(), -5.799903); + // check approx rather than exact because round trip for pi doesn't fall on an exactly + // representable value (unlike `f32` and `f64`). + assert_approx_eq!(180.0f128.to_radians(), pi); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_real_consts() { + // FIXME(f16_f128): add math tests when available + use super::consts; + + let pi: f128 = consts::PI; + let frac_pi_2: f128 = consts::FRAC_PI_2; + let frac_pi_3: f128 = consts::FRAC_PI_3; + let frac_pi_4: f128 = consts::FRAC_PI_4; + let frac_pi_6: f128 = consts::FRAC_PI_6; + let frac_pi_8: f128 = consts::FRAC_PI_8; + let frac_1_pi: f128 = consts::FRAC_1_PI; + let frac_2_pi: f128 = consts::FRAC_2_PI; + // let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; + // let sqrt2: f128 = consts::SQRT_2; + // let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; + // let e: f128 = consts::E; + // let log2_e: f128 = consts::LOG2_E; + // let log10_e: f128 = consts::LOG10_E; + // let ln_2: f128 = consts::LN_2; + // let ln_10: f128 = consts::LN_10; + + assert_approx_eq!(frac_pi_2, pi / 2f128); + assert_approx_eq!(frac_pi_3, pi / 3f128); + assert_approx_eq!(frac_pi_4, pi / 4f128); + assert_approx_eq!(frac_pi_6, pi / 6f128); + assert_approx_eq!(frac_pi_8, pi / 8f128); + assert_approx_eq!(frac_1_pi, 1f128 / pi); + assert_approx_eq!(frac_2_pi, 2f128 / pi); + // assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt()); + // assert_approx_eq!(sqrt2, 2f128.sqrt()); + // assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt()); + // assert_approx_eq!(log2_e, e.log2()); + // assert_approx_eq!(log10_e, e.log10()); + // assert_approx_eq!(ln_2, 2f128.ln()); + // assert_approx_eq!(ln_10, 10f128.ln()); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000); + assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); + assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); + assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); + assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); + assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5); + assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); + assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); + + // 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 + let masked_nan1 = f128::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f128::NAN.to_bits() ^ NAN_MASK2; + assert!(f128::from_bits(masked_nan1).is_nan()); + assert!(f128::from_bits(masked_nan2).is_nan()); + + assert_eq!(f128::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f128.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f128.clamp(f128::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f128.clamp(3.0, f128::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u128 { + 1 << (f128::MANTISSA_DIGITS - 2) + } + + // FIXME(f16_f128): test subnormals when powf is available + // fn min_subnorm() -> f128 { + // f128::MIN_POSITIVE / f128::powf(2.0, f128::MANTISSA_DIGITS as f128 - 1.0) + // } + + // fn max_subnorm() -> f128 { + // f128::MIN_POSITIVE - min_subnorm() + // } + + fn q_nan() -> f128 { + f128::from_bits(f128::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f128 { + f128::from_bits((f128::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f128::INFINITY).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Equal, (-f128::MAX).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f128).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f128).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f128::MIN_POSITIVE).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f128).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f128.total_cmp(&0.0)); + // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f128::MIN_POSITIVE.total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f128.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f128.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f128::MAX.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Equal, f128::INFINITY.total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-f128::INFINITY).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-f128::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f128).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f128).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-f128::MIN_POSITIVE).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f128).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, 0.0_f128.total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f128::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f128.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f128.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, f128::MAX.total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, f128::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f128::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f128::MAX).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f128).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f128).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f128::MIN_POSITIVE).total_cmp(&-0.5)); + // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Greater, (-0.0_f128).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f128.total_cmp(&-0.0)); + // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Greater, f128::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f128.total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f128.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f128::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f128::INFINITY.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index 1cb655ffabd84..d48518622999a 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -32,4 +32,33 @@ impl f16 { pub fn powi(self, n: i32) -> f16 { unsafe { intrinsics::powif16(self, n) } } + + /// Computes the absolute value of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16)] { + /// + /// let x = 3.5_f16; + /// let y = -3.5_f16; + /// + /// assert_eq!(x.abs(), x); + /// assert_eq!(y.abs(), -y); + /// + /// assert!(f16::NAN.abs().is_nan()); + /// # } + /// ``` + #[inline] + #[cfg(not(bootstrap))] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn abs(self) -> Self { + // FIXME(f16_f128): replace with `intrinsics::fabsf16` when available + Self::from_bits(self.to_bits() & !(1 << 15)) + } } diff --git a/library/std/src/f16/tests.rs b/library/std/src/f16/tests.rs index d65c43eca4bb8..bb6a811529e17 100644 --- a/library/std/src/f16/tests.rs +++ b/library/std/src/f16/tests.rs @@ -1,35 +1,37 @@ -#![allow(dead_code)] // FIXME(f16_f128): remove once constants are used +#![cfg(not(bootstrap))] +// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy +#![cfg(reliable_f16)] + +use crate::f16::consts; +use crate::num::*; // We run out of precision pretty quickly with f16 -const F16_APPROX_L1: f16 = 0.001; +// const F16_APPROX_L1: f16 = 0.001; const F16_APPROX_L2: f16 = 0.01; -const F16_APPROX_L3: f16 = 0.1; +// const F16_APPROX_L3: f16 = 0.1; const F16_APPROX_L4: f16 = 0.5; /// Smallest number const TINY_BITS: u16 = 0x1; + /// Next smallest number const TINY_UP_BITS: u16 = 0x2; + /// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 const MAX_DOWN_BITS: u16 = 0x7bfe; + /// Zeroed exponent, full significant const LARGEST_SUBNORMAL_BITS: u16 = 0x03ff; + /// Exponent = 0b1, zeroed significand const SMALLEST_NORMAL_BITS: u16 = 0x0400; + /// First pattern over the mantissa const NAN_MASK1: u16 = 0x02aa; + /// Second pattern over the mantissa const NAN_MASK2: u16 = 0x0155; -/// Compare by value -#[allow(unused_macros)] -macro_rules! assert_f16_eq { - ($a:expr, $b:expr) => { - let (l, r): (&f16, &f16) = (&$a, &$b); - assert_eq!(*l, *r, "\na: {:#018x}\nb: {:#018x}", l.to_bits(), r.to_bits()) - }; -} - /// Compare by representation #[allow(unused_macros)] macro_rules! assert_f16_biteq { @@ -37,10 +39,500 @@ macro_rules! assert_f16_biteq { let (l, r): (&f16, &f16) = (&$a, &$b); let lb = l.to_bits(); let rb = r.to_bits(); - assert_eq!( - lb, rb, - "float {:?} is not bitequal to {:?}.\na: {:#018x}\nb: {:#018x}", - *l, *r, lb, rb - ); + assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})"); }; } + +#[test] +fn test_num_f16() { + test_num(10f16, 2f16); +} + +// FIXME(f16_f128): add min and max tests when available + +#[test] +fn test_nan() { + let nan: f16 = f16::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + // FIXME(f16_f128): classify + // assert!(!nan.is_normal()); + // assert_eq!(Fp::Nan, nan.classify()); +} + +#[test] +fn test_infinity() { + let inf: f16 = f16::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + // FIXME(f16_f128): classify + // assert!(!inf.is_normal()); + // assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + // FIXME(f16_f128): classify + // assert!(!neg_inf.is_normal()); + // assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f16 = 0.0f16; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + // FIXME(f16_f128): classify + // assert!(!zero.is_normal()); + // assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f16 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + // FIXME(f16_f128): classify + // assert!(!neg_zero.is_normal()); + // assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f16 = 1.0f16; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + // FIXME(f16_f128): classify + // assert!(one.is_normal()); + // assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f16.is_nan()); + assert!(!5.3f16.is_nan()); + assert!(!(-10.732f16).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f16.is_infinite()); + assert!(!42.8f16.is_infinite()); + assert!(!(-109.2f16).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f16.is_finite()); + assert!(42.8f16.is_finite()); + assert!((-109.2f16).is_finite()); +} + +// FIXME(f16_f128): add `test_is_normal` and `test_classify` when classify is working +// FIXME(f16_f128): add missing math functions when available + +#[test] +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!(f16::NAN.abs().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f16::INFINITY.is_sign_positive()); + assert!(1f16.is_sign_positive()); + assert!(0f16.is_sign_positive()); + assert!(!(-0f16).is_sign_positive()); + assert!(!(-1f16).is_sign_positive()); + assert!(!f16::NEG_INFINITY.is_sign_positive()); + assert!(!(1f16 / f16::NEG_INFINITY).is_sign_positive()); + assert!(f16::NAN.is_sign_positive()); + assert!(!(-f16::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f16::INFINITY.is_sign_negative()); + assert!(!1f16.is_sign_negative()); + assert!(!0f16.is_sign_negative()); + assert!((-0f16).is_sign_negative()); + assert!((-1f16).is_sign_negative()); + assert!(f16::NEG_INFINITY.is_sign_negative()); + assert!((1f16 / f16::NEG_INFINITY).is_sign_negative()); + assert!(!f16::NAN.is_sign_negative()); + assert!((-f16::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f16::from_bits(TINY_BITS); + let tiny_up = f16::from_bits(TINY_UP_BITS); + 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); + + // 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); +} + +#[test] +fn test_next_down() { + let tiny = f16::from_bits(TINY_BITS); + let tiny_up = f16::from_bits(TINY_UP_BITS); + 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); + + // 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); +} + +#[test] +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!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +fn test_to_degrees() { + let pi: f16 = consts::PI; + 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_approx_eq!((-5.8f16).to_degrees(), -332.315521); + assert_approx_eq!(pi.to_degrees(), 180.0, F16_APPROX_L4); + 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); +} + +#[test] +fn test_to_radians() { + let pi: f16 = consts::PI; + 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_approx_eq!(154.6f16.to_radians(), 2.698279); + assert_approx_eq!((-332.31f16).to_radians(), -5.799903); + assert_approx_eq!(180.0f16.to_radians(), pi, F16_APPROX_L2); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_real_consts() { + // FIXME(f16_f128): add math tests when available + use super::consts; + + let pi: f16 = consts::PI; + let frac_pi_2: f16 = consts::FRAC_PI_2; + let frac_pi_3: f16 = consts::FRAC_PI_3; + let frac_pi_4: f16 = consts::FRAC_PI_4; + let frac_pi_6: f16 = consts::FRAC_PI_6; + let frac_pi_8: f16 = consts::FRAC_PI_8; + let frac_1_pi: f16 = consts::FRAC_1_PI; + let frac_2_pi: f16 = consts::FRAC_2_PI; + // let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; + // let sqrt2: f16 = consts::SQRT_2; + // let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; + // let e: f16 = consts::E; + // let log2_e: f16 = consts::LOG2_E; + // let log10_e: f16 = consts::LOG10_E; + // let ln_2: f16 = consts::LN_2; + // let ln_10: f16 = consts::LN_10; + + assert_approx_eq!(frac_pi_2, pi / 2f16); + assert_approx_eq!(frac_pi_3, pi / 3f16); + assert_approx_eq!(frac_pi_4, pi / 4f16); + assert_approx_eq!(frac_pi_6, pi / 6f16); + assert_approx_eq!(frac_pi_8, pi / 8f16); + assert_approx_eq!(frac_1_pi, 1f16 / pi); + assert_approx_eq!(frac_2_pi, 2f16 / pi); + // assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt()); + // assert_approx_eq!(sqrt2, 2f16.sqrt()); + // assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt()); + // assert_approx_eq!(log2_e, e.log2()); + // assert_approx_eq!(log10_e, e.log10()); + // assert_approx_eq!(ln_2, 2f16.ln()); + // assert_approx_eq!(ln_10, 10f16.ln()); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f16).to_bits(), 0x3c00); + assert_eq!((12.5f16).to_bits(), 0x4a40); + assert_eq!((1337f16).to_bits(), 0x6539); + assert_eq!((-14.25f16).to_bits(), 0xcb20); + assert_approx_eq!(f16::from_bits(0x3c00), 1.0); + assert_approx_eq!(f16::from_bits(0x4a40), 12.5); + assert_approx_eq!(f16::from_bits(0x6539), 1337.0); + assert_approx_eq!(f16::from_bits(0xcb20), -14.25); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f16::NAN.to_bits() ^ NAN_MASK2; + assert!(f16::from_bits(masked_nan1).is_nan()); + assert!(f16::from_bits(masked_nan2).is_nan()); + + assert_eq!(f16::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f16.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f16.clamp(f16::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f16.clamp(3.0, f16::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u16 { + 1 << (f16::MANTISSA_DIGITS - 2) + } + + // FIXME(f16_f128): test subnormals when powf is available + // fn min_subnorm() -> f16 { + // f16::MIN_POSITIVE / f16::powf(2.0, f16::MANTISSA_DIGITS as f16 - 1.0) + // } + + // fn max_subnorm() -> f16 { + // f16::MIN_POSITIVE - min_subnorm() + // } + + fn q_nan() -> f16 { + f16::from_bits(f16::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f16 { + f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f16::INFINITY).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Equal, (-f16::MAX).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f16).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f16).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f16::MIN_POSITIVE).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f16).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f16.total_cmp(&0.0)); + // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f16::MIN_POSITIVE.total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f16.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f16.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f16::MAX.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Equal, f16::INFINITY.total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-f16::INFINITY).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-f16::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f16).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f16).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-f16::MIN_POSITIVE).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f16).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, 0.0_f16.total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f16::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f16.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f16.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, f16::MAX.total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f16::MAX).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f16).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f16).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f16::MIN_POSITIVE).total_cmp(&-0.5)); + // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Greater, (-0.0_f16).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f16.total_cmp(&-0.0)); + // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Greater, f16::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f16.total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f16.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f16::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f16::INFINITY.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index 9ca4e8f2f45fe..63e65698374c8 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -2,6 +2,45 @@ use crate::f32::consts; use crate::num::FpCategory as Fp; use crate::num::*; +/// Smallest number +#[allow(dead_code)] // unused on x86 +const TINY_BITS: u32 = 0x1; + +/// Next smallest number +#[allow(dead_code)] // unused on x86 +const TINY_UP_BITS: u32 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +#[allow(dead_code)] // unused on x86 +const MAX_DOWN_BITS: u32 = 0x7f7f_fffe; + +/// Zeroed exponent, full significant +#[allow(dead_code)] // unused on x86 +const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff; + +/// Exponent = 0b1, zeroed significand +#[allow(dead_code)] // unused on x86 +const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000; + +/// First pattern over the mantissa +#[allow(dead_code)] // unused on x86 +const NAN_MASK1: u32 = 0x002a_aaaa; + +/// Second pattern over the mantissa +#[allow(dead_code)] // unused on x86 +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() { test_num(10f32, 2f32); @@ -315,27 +354,16 @@ fn test_is_sign_negative() { assert!((-f32::NAN).is_sign_negative()); } -#[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 {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb); - }; -} - // Ignore test on x87 floating point, these platforms do not guarantee NaN // payloads are preserved and flush denormals to zero, failing the tests. #[cfg(not(target_arch = "x86"))] #[test] fn test_next_up() { - let tiny = f32::from_bits(1); - let tiny_up = f32::from_bits(2); - let max_down = f32::from_bits(0x7f7f_fffe); - let largest_subnormal = f32::from_bits(0x007f_ffff); - let smallest_normal = f32::from_bits(0x0080_0000); + let tiny = f32::from_bits(TINY_BITS); + let tiny_up = f32::from_bits(TINY_UP_BITS); + 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); @@ -352,8 +380,8 @@ fn test_next_up() { // Check that NaNs roundtrip. let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); + 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); @@ -364,11 +392,11 @@ fn test_next_up() { #[cfg(not(target_arch = "x86"))] #[test] fn test_next_down() { - let tiny = f32::from_bits(1); - let tiny_up = f32::from_bits(2); - let max_down = f32::from_bits(0x7f7f_fffe); - let largest_subnormal = f32::from_bits(0x007f_ffff); - let smallest_normal = f32::from_bits(0x0080_0000); + let tiny = f32::from_bits(TINY_BITS); + let tiny_up = f32::from_bits(TINY_UP_BITS); + 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); @@ -386,8 +414,8 @@ fn test_next_down() { // Check that NaNs roundtrip. let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); + 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); @@ -734,8 +762,8 @@ fn test_float_bits_conv() { // 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 - let masked_nan1 = f32::NAN.to_bits() ^ 0x002A_AAAA; - let masked_nan2 = f32::NAN.to_bits() ^ 0x0055_5555; + let masked_nan1 = f32::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2; assert!(f32::from_bits(masked_nan1).is_nan()); assert!(f32::from_bits(masked_nan2).is_nan()); diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index f88d01593b5e4..d9e17fd601d2d 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -2,6 +2,45 @@ use crate::f64::consts; use crate::num::FpCategory as Fp; use crate::num::*; +/// Smallest number +#[allow(dead_code)] // unused on x86 +const TINY_BITS: u64 = 0x1; + +/// Next smallest number +#[allow(dead_code)] // unused on x86 +const TINY_UP_BITS: u64 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +#[allow(dead_code)] // unused on x86 +const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe; + +/// Zeroed exponent, full significant +#[allow(dead_code)] // unused on x86 +const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff; + +/// Exponent = 0b1, zeroed significand +#[allow(dead_code)] // unused on x86 +const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000; + +/// First pattern over the mantissa +#[allow(dead_code)] // unused on x86 +const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; + +/// Second pattern over the mantissa +#[allow(dead_code)] // unused on x86 +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() { test_num(10f64, 2f64); @@ -305,27 +344,16 @@ fn test_is_sign_negative() { assert!((-f64::NAN).is_sign_negative()); } -#[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 {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb); - }; -} - // Ignore test on x87 floating point, these platforms do not guarantee NaN // payloads are preserved and flush denormals to zero, failing the tests. #[cfg(not(target_arch = "x86"))] #[test] fn test_next_up() { - let tiny = f64::from_bits(1); - let tiny_up = f64::from_bits(2); - let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe); - let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff); - let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); + let tiny = f64::from_bits(TINY_BITS); + let tiny_up = f64::from_bits(TINY_UP_BITS); + 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); @@ -341,8 +369,8 @@ fn test_next_up() { assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY); let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); + 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); @@ -353,11 +381,11 @@ fn test_next_up() { #[cfg(not(target_arch = "x86"))] #[test] fn test_next_down() { - let tiny = f64::from_bits(1); - let tiny_up = f64::from_bits(2); - let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe); - let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff); - let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); + let tiny = f64::from_bits(TINY_BITS); + let tiny_up = f64::from_bits(TINY_UP_BITS); + 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); @@ -374,8 +402,8 @@ fn test_next_down() { assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX); let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); + 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); @@ -715,9 +743,8 @@ fn test_float_bits_conv() { assert_approx_eq!(f64::from_bits(0xc02c800000000000), -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 - let masked_nan1 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA; - let masked_nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; + let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f64::NAN.to_bits() ^ NAN_MASK2; assert!(f64::from_bits(masked_nan1).is_nan()); assert!(f64::from_bits(masked_nan2).is_nan()); diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 58df83bd79d23..972b6015932db 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -373,10 +373,17 @@ macro_rules! dbg { }; } +/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. #[cfg(test)] macro_rules! assert_approx_eq { - ($a:expr, $b:expr) => {{ + ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; + ($a:expr, $b:expr, $lim:expr) => {{ let (a, b) = (&$a, &$b); - assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b); + let diff = (*a - *b).abs(); + assert!( + diff < $lim, + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, actual {diff:?})", + lim = $lim + ); }}; } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index cc5b007321350..5b18cb3f7e1e6 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1290,15 +1290,21 @@ fn needs_codegen_config(run: &RunConfig<'_>) -> bool { pub(crate) const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_"; fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool { - if path.path.to_str().unwrap().contains(CODEGEN_BACKEND_PREFIX) { + let path = path.path.to_str().unwrap(); + + let is_explicitly_called = |p| -> bool { run.builder.paths.contains(p) }; + let should_enforce = run.builder.kind == Kind::Dist || run.builder.kind == Kind::Install; + + if path.contains(CODEGEN_BACKEND_PREFIX) { let mut needs_codegen_backend_config = true; for backend in run.builder.config.codegen_backends(run.target) { - if path.path.to_str().unwrap().ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + backend)) - { + if path.ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + backend)) { needs_codegen_backend_config = false; } } - if needs_codegen_backend_config { + if (is_explicitly_called(&PathBuf::from(path)) || should_enforce) + && needs_codegen_backend_config + { run.builder.info( "WARNING: no codegen-backends config matched the requested path to build a codegen backend. \ HELP: add backend to codegen-backends in config.toml.", diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 4fb64595504e6..2dc7cd7de6a05 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1018,7 +1018,7 @@ impl Step for PlainSourceTarball { // perhaps it should be removed in favor of making `dist` perform the `vendor` step? // Ensure we have all submodules from src and other directories checked out. - for submodule in builder.get_all_submodules() { + for submodule in build_helper::util::parse_gitmodules(&builder.src) { builder.update_submodule(Path::new(submodule)); } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 8a2bc3b9d484b..efc09c41bf429 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1048,8 +1048,6 @@ impl Step for Tidy { /// Once tidy passes, this step also runs `fmt --check` if tests are being run /// for the `dev` or `nightly` channels. fn run(self, builder: &Builder<'_>) { - builder.build.update_submodule(Path::new("src/tools/rustc-perf")); - let mut cmd = builder.tool_cmd(Tool::Tidy); cmd.arg(&builder.src); cmd.arg(&builder.initial_cargo); diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 2e9c04624c783..14ccbfe0267e8 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -4,13 +4,11 @@ use std::collections::BTreeSet; use std::env; use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Write}; -use std::fs::{self, File}; +use std::fs; use std::hash::Hash; -use std::io::{BufRead, BufReader}; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::process::Command; -use std::sync::OnceLock; use std::time::{Duration, Instant}; use crate::core::build_steps::tool::{self, SourceType}; @@ -594,7 +592,7 @@ impl<'a> ShouldRun<'a> { /// /// [`path`]: ShouldRun::path pub fn paths(mut self, paths: &[&str]) -> Self { - let submodules_paths = self.builder.get_all_submodules(); + let submodules_paths = build_helper::util::parse_gitmodules(&self.builder.src); self.paths.insert(PathSet::Set( paths @@ -2243,28 +2241,6 @@ impl<'a> Builder<'a> { out } - /// Return paths of all submodules. - pub fn get_all_submodules(&self) -> &[String] { - static SUBMODULES_PATHS: OnceLock> = OnceLock::new(); - - let init_submodules_paths = |src: &PathBuf| { - let file = File::open(src.join(".gitmodules")).unwrap(); - - let mut submodules_paths = vec![]; - for line in BufReader::new(file).lines().map_while(Result::ok) { - let line = line.trim(); - if line.starts_with("path") { - let actual_path = line.split(' ').last().expect("Couldn't get value of path"); - submodules_paths.push(actual_path.to_owned()); - } - } - - submodules_paths - }; - - SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.src)) - } - /// Ensure that a given step is built *only if it's supposed to be built by default*, returning /// its output. This will cache the step, so it's safe (and good!) to call this as often as /// needed to ensure that all dependencies are build. diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 4b6dc45b436d6..59b29eedb7971 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -135,7 +135,7 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result< if config.dry_run() { return Ok(()); } - let _ = fs::remove_dir(link); + let _ = fs::remove_dir_all(link); return symlink_dir_inner(original, link); #[cfg(not(windows))] diff --git a/src/etc/pre-push.sh b/src/etc/pre-push.sh index c9e1a2733fdc5..6f86c7ab8a448 100755 --- a/src/etc/pre-push.sh +++ b/src/etc/pre-push.sh @@ -7,8 +7,6 @@ set -Euo pipefail -# https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570 -unset GIT_DIR ROOT_DIR="$(git rev-parse --show-toplevel)" echo "Running pre-push script $ROOT_DIR/x test tidy" diff --git a/src/tools/build-manifest/README.md b/src/tools/build-manifest/README.md index 9d30c554186be..2ea1bffb35f4d 100644 --- a/src/tools/build-manifest/README.md +++ b/src/tools/build-manifest/README.md @@ -4,7 +4,7 @@ This tool generates the manifests uploaded to static.rust-lang.org and used by r You can see a full list of all manifests at . This listing is updated by every 7 days. -This gets called by `promote-release` via `x.py dist hash-and-sign`. +This gets called by `promote-release` . `promote-release` downloads a pre-built binary of `build-manifest` which is generated in the dist-x86_64-linux builder and uploaded to s3. ## Adding a new component diff --git a/src/tools/build_helper/src/util.rs b/src/tools/build_helper/src/util.rs index 5801a8648f227..72c05c4c48abf 100644 --- a/src/tools/build_helper/src/util.rs +++ b/src/tools/build_helper/src/util.rs @@ -1,4 +1,8 @@ +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::path::Path; use std::process::Command; +use std::sync::OnceLock; /// Invokes `build_helper::util::detail_exit` with `cfg!(test)` /// @@ -45,3 +49,27 @@ pub fn try_run(cmd: &mut Command, print_cmd_on_fail: bool) -> Result<(), ()> { Ok(()) } } + +/// Returns the submodule paths from the `.gitmodules` file in the given directory. +pub fn parse_gitmodules(target_dir: &Path) -> &[String] { + static SUBMODULES_PATHS: OnceLock> = OnceLock::new(); + let gitmodules = target_dir.join(".gitmodules"); + assert!(gitmodules.exists(), "'{}' file is missing.", gitmodules.display()); + + let init_submodules_paths = || { + let file = File::open(gitmodules).unwrap(); + + let mut submodules_paths = vec![]; + for line in BufReader::new(file).lines().map_while(Result::ok) { + let line = line.trim(); + if line.starts_with("path") { + let actual_path = line.split(' ').last().expect("Couldn't get value of path"); + submodules_paths.push(actual_path.to_owned()); + } + } + + submodules_paths + }; + + SUBMODULES_PATHS.get_or_init(|| init_submodules_paths()) +} diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 12fb76f397241..1b70a1a1cf08f 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -311,7 +311,9 @@ pub fn report_error<'tcx>( ResourceExhaustion(_) => "resource exhaustion", Unsupported( // We list only the ones that can actually happen. - UnsupportedOpInfo::Unsupported(_) | UnsupportedOpInfo::UnsizedLocal, + UnsupportedOpInfo::Unsupported(_) + | UnsupportedOpInfo::UnsizedLocal + | UnsupportedOpInfo::ExternTypeField, ) => "unsupported operation", InvalidProgram( // We list only the ones that can actually happen. diff --git a/src/tools/miri/tests/fail/extern-type-field-offset.stderr b/src/tools/miri/tests/fail/extern-type-field-offset.stderr index e0d6e9ebf1de7..3ed5732b4eb06 100644 --- a/src/tools/miri/tests/fail/extern-type-field-offset.stderr +++ b/src/tools/miri/tests/fail/extern-type-field-offset.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: `extern type` does not have a known offset +error: unsupported operation: `extern type` field does not have a known offset --> $DIR/extern-type-field-offset.rs:LL:CC | LL | let _field = &x.a; - | ^^^^ `extern type` does not have a known offset + | ^^^^ `extern type` field does not have a known offset | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support = note: BACKTRACE: diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 89169e10715b2..60c827fd03bbb 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -1,4 +1,4 @@ -use rustc_ast::token::{Delimiter, NonterminalKind, TokenKind}; +use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ast, ptr}; use rustc_parse::parser::{ForceCollect, Parser, Recovery}; @@ -48,7 +48,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { parse_macro_arg!( Expr, - NonterminalKind::Expr, + NonterminalKind::Expr(Expr), |parser: &mut Parser<'b>| parser.parse_expr(), |x: ptr::P| Some(x) ); @@ -60,7 +60,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { ); parse_macro_arg!( Pat, - NonterminalKind::PatParam { inferred: false }, + NonterminalKind::Pat(PatParam { inferred: false }), |parser: &mut Parser<'b>| parser.parse_pat_no_top_alt(None, None), |x: ptr::P| Some(x) ); diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index f39438bd9ace2..0b05d5add52d6 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" autobins = false [dependencies] +build_helper = { path = "../build_helper" } cargo_metadata = "0.15" regex = "1" miropt-test-tools = { path = "../miropt-test-tools" } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 7337c9843c7b6..aa119819aaa26 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -1,7 +1,9 @@ //! Checks the licenses of third-party dependencies. +use build_helper::ci::CiEnv; use cargo_metadata::{Metadata, Package, PackageId}; use std::collections::HashSet; +use std::fs::read_dir; use std::path::Path; /// These are licenses that are allowed for all crates, including the runtime, @@ -514,7 +516,19 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) { let mut checked_runtime_licenses = false; + let submodules = build_helper::util::parse_gitmodules(root); for &(workspace, exceptions, permitted_deps) in WORKSPACES { + // Skip if it's a submodule, not in a CI environment, and not initialized. + // + // This prevents enforcing developers to fetch submodules for tidy. + if submodules.contains(&workspace.into()) + && !CiEnv::is_ci() + // If the directory is empty, we can consider it as an uninitialized submodule. + && read_dir(root.join(workspace)).unwrap().next().is_none() + { + continue; + } + if !root.join(workspace).join("Cargo.lock").exists() { tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock"); continue; diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index 2118de5f20411..8bb80f1171184 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -1,6 +1,7 @@ //! Check for external package sources. Allow only vendorable packages. -use std::fs; +use build_helper::ci::CiEnv; +use std::fs::{self, read_dir}; use std::path::Path; /// List of allowed sources for packages. @@ -13,7 +14,19 @@ const ALLOWED_SOURCES: &[&str] = &[ /// Checks for external package sources. `root` is the path to the directory that contains the /// workspace `Cargo.toml`. pub fn check(root: &Path, bad: &mut bool) { + let submodules = build_helper::util::parse_gitmodules(root); for &(workspace, _, _) in crate::deps::WORKSPACES { + // Skip if it's a submodule, not in a CI environment, and not initialized. + // + // This prevents enforcing developers to fetch submodules for tidy. + if submodules.contains(&workspace.into()) + && !CiEnv::is_ci() + // If the directory is empty, we can consider it as an uninitialized submodule. + && read_dir(root.join(workspace)).unwrap().next().is_none() + { + continue; + } + // FIXME check other workspaces too // `Cargo.lock` of rust. let path = root.join(workspace).join("Cargo.lock"); diff --git a/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.stderr b/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.stderr index 99f37fedd3dd3..54d45ee8ffb24 100644 --- a/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.stderr +++ b/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/issue-91827-extern-types-field-offset.rs:38:17 | LL | let field = &x.a; - | ^^^^ `extern type` does not have a known offset + | ^^^^ `extern type` field does not have a known offset error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/validation-ice-extern-type-field.rs b/tests/ui/consts/const-eval/validation-ice-extern-type-field.rs new file mode 100644 index 0000000000000..3502409d576c2 --- /dev/null +++ b/tests/ui/consts/const-eval/validation-ice-extern-type-field.rs @@ -0,0 +1,15 @@ +#![feature(extern_types)] + +extern { + type Opaque; +} + +struct ThinDst { + x: u8, + tail: Opaque, +} + +const C1: &ThinDst = unsafe { std::mem::transmute(b"d".as_ptr()) }; +//~^ERROR: evaluation of constant value failed + +fn main() {} diff --git a/tests/ui/consts/const-eval/validation-ice-extern-type-field.stderr b/tests/ui/consts/const-eval/validation-ice-extern-type-field.stderr new file mode 100644 index 0000000000000..1ec36abc2eccf --- /dev/null +++ b/tests/ui/consts/const-eval/validation-ice-extern-type-field.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/validation-ice-extern-type-field.rs:12:1 + | +LL | const C1: &ThinDst = unsafe { std::mem::transmute(b"d".as_ptr()) }; + | ^^^^^^^^^^^^^^^^^^ `extern type` field does not have a known offset + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/delegation/glob-traitless-qpath.rs b/tests/ui/delegation/glob-traitless-qpath.rs new file mode 100644 index 0000000000000..abf4b3180ed21 --- /dev/null +++ b/tests/ui/delegation/glob-traitless-qpath.rs @@ -0,0 +1,11 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +struct S; + +impl S { + reuse ::*; //~ ERROR qualified path without a trait in glob delegation + reuse <()>::*; //~ ERROR qualified path without a trait in glob delegation +} + +fn main() {} diff --git a/tests/ui/delegation/glob-traitless-qpath.stderr b/tests/ui/delegation/glob-traitless-qpath.stderr new file mode 100644 index 0000000000000..e3257de347a76 --- /dev/null +++ b/tests/ui/delegation/glob-traitless-qpath.stderr @@ -0,0 +1,14 @@ +error: qualified path without a trait in glob delegation + --> $DIR/glob-traitless-qpath.rs:7:5 + | +LL | reuse ::*; + | ^^^^^^^^^^^^^^ + +error: qualified path without a trait in glob delegation + --> $DIR/glob-traitless-qpath.rs:8:5 + | +LL | reuse <()>::*; + | ^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr index 3f8011d961ae7..6bbd81ae3e163 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr @@ -9,15 +9,11 @@ LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0080]: it is undefined behavior to use this value +error[E0080]: evaluation of constant value failed --> $DIR/stack-overflow-trait-infer-98842.rs:15:1 | LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation - | - = note: the raw bytes of the constant (size: 4, align: 4) { - 00 00 00 00 │ .... - } error: aborting due to 2 previous errors diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr index 04e2c4483bf64..6bbd81ae3e163 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr @@ -9,15 +9,11 @@ LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0080]: it is undefined behavior to use this value +error[E0080]: evaluation of constant value failed --> $DIR/stack-overflow-trait-infer-98842.rs:15:1 | LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation - | - = note: the raw bytes of the constant (size: 8, align: 8) { - 00 00 00 00 00 00 00 00 │ ........ - } error: aborting due to 2 previous errors diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.rs b/tests/ui/sized/stack-overflow-trait-infer-98842.rs index 54d50346cc831..be4807b2e4aa7 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.rs +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.rs @@ -13,6 +13,6 @@ struct Foo(<&'static Foo as ::core::ops::Deref>::Target); // and it will infinitely recurse somewhere trying to figure out the // size of this pointer (is my guess): const _: *const Foo = 0 as _; -//~^ ERROR it is undefined behavior to use this value +//~^ ERROR evaluation of constant value failed pub fn main() {} diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.stderr deleted file mode 100644 index 8ddddeb5bf2d1..0000000000000 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0391]: cycle detected when computing layout of `Foo` - | - = note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... - = note: ...which again requires computing layout of `Foo`, completing the cycle -note: cycle used when const-evaluating + checking `_` - --> $DIR/stack-overflow-trait-infer-98842.rs:13:1 - | -LL | const _: *const Foo = 0 as _; - | ^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error[E0080]: it is undefined behavior to use this value - --> $DIR/stack-overflow-trait-infer-98842.rs:13:1 - | -LL | const _: *const Foo = 0 as _; - | ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation - | - = note: the raw bytes of the constant (size: 8, align: 8) { - 00 00 00 00 00 00 00 00 │ ........ - } - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0080, E0391. -For more information about an error, try `rustc --explain E0080`.