From 58292e2a531a42bdf0ffe3ef594caa42baba5140 Mon Sep 17 00:00:00 2001 From: Alan Egerton Date: Wed, 4 Feb 2026 12:35:34 +0000 Subject: [PATCH 01/13] Consider captures to be used by closures that unwind Assignments to a captured variable within a diverging closure should not be considered unused if the divergence is caught. This patch considers such assignments/captures to be used by diverging closures irrespective of whether the divergence is caught, but better a false negative unused lint than a false positive one (the latter having caused a stable-to-stable regression). --- compiler/rustc_mir_transform/src/liveness.rs | 1 + tests/ui/lint/unused/diverging-path.rs | 21 ++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/ui/lint/unused/diverging-path.rs diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index 9bafee9f31c39..9d951f0874376 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1291,6 +1291,7 @@ impl<'tcx> Visitor<'tcx> for TransferFunction<'_, 'tcx> { TerminatorKind::Return | TerminatorKind::Yield { .. } | TerminatorKind::Goto { target: START_BLOCK } // Inserted for the `FnMut` case. + | TerminatorKind::Call { target: None, .. } // unwinding could be caught if self.capture_kind != CaptureKind::None => { // All indirect captures have an effect on the environment, so we mark them as live. diff --git a/tests/ui/lint/unused/diverging-path.rs b/tests/ui/lint/unused/diverging-path.rs new file mode 100644 index 0000000000000..7f364518fe976 --- /dev/null +++ b/tests/ui/lint/unused/diverging-path.rs @@ -0,0 +1,21 @@ +//! Assignments to a captured variable within a diverging closure should not be considered unused if +//! the divergence is caught. +//! +//! Regression test for https://github.com/rust-lang/rust/issues/152079 +//@ compile-flags: -Wunused +//@ check-pass + +fn main() { + let mut x = 1; + catch(|| { + x = 2; + panic!(); + }); + dbg!(x); +} + +fn catch(f: F) { + if let Ok(true) = std::fs::exists("may_or_may_not_call_f") { + _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)); + } +} From a7888c1342c5a30df5d45bbcb073269e3e163b4c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 2 Jan 2026 15:55:35 +0100 Subject: [PATCH 02/13] allow `const fn` to be c-variadic however `Drop` for `VaList` is not yet available in const fn --- .../rustc_ast_passes/src/ast_validation.rs | 9 --- compiler/rustc_ast_passes/src/errors.rs | 11 ---- .../variadic-ffi-semantic-restrictions.rs | 10 +-- .../variadic-ffi-semantic-restrictions.stderr | 66 ++++++------------- 4 files changed, 23 insertions(+), 73 deletions(-) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index b9fb20b68971d..f9a64ffe09c42 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -698,15 +698,6 @@ impl<'a> AstValidator<'a> { unreachable!("C variable argument list cannot be used in closures") }; - // C-variadics are not yet implemented in const evaluation. - if let Const::Yes(const_span) = sig.header.constness { - self.dcx().emit_err(errors::ConstAndCVariadic { - spans: vec![const_span, variadic_param.span], - const_span, - variadic_span: variadic_param.span, - }); - } - if let Some(coroutine_kind) = sig.header.coroutine_kind { self.dcx().emit_err(errors::CoroutineAndCVariadic { spans: vec![coroutine_kind.span(), variadic_param.span], diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index dd260aede4894..30b2e66674471 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -823,17 +823,6 @@ pub(crate) struct ConstAndCoroutine { pub coroutine_kind: &'static str, } -#[derive(Diagnostic)] -#[diag("functions cannot be both `const` and C-variadic")] -pub(crate) struct ConstAndCVariadic { - #[primary_span] - pub spans: Vec, - #[label("`const` because of this")] - pub const_span: Span, - #[label("C-variadic because of this")] - pub variadic_span: Span, -} - #[derive(Diagnostic)] #[diag("functions cannot be both `{$coroutine_kind}` and C-variadic")] pub(crate) struct CoroutineAndCVariadic { diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 6f61425a8bd6c..41d9e73b69e8d 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -31,17 +31,14 @@ extern "C" fn f3_3(_: ..., x: isize) {} //~^ ERROR `...` must be the last argument of a C-variadic function const unsafe extern "C" fn f4_1(x: isize, _: ...) {} -//~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time +//~^ ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time const extern "C" fn f4_2(x: isize, _: ...) {} -//~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR functions with a C variable argument list must be unsafe +//~^ ERROR functions with a C variable argument list must be unsafe //~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time const extern "C" fn f4_3(_: ..., x: isize, _: ...) {} -//~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR functions with a C variable argument list must be unsafe +//~^ ERROR functions with a C variable argument list must be unsafe //~| ERROR `...` must be the last argument of a C-variadic function extern "C" { @@ -64,7 +61,6 @@ impl X { //~| ERROR `...` must be the last argument of a C-variadic function const fn i_f5(x: isize, _: ...) {} //~^ ERROR `...` is not supported for non-extern functions - //~| ERROR functions cannot be both `const` and C-variadic //~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time } diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 318015737fa1b..20a182b8c49f3 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -80,20 +80,8 @@ error: `...` must be the last argument of a C-variadic function LL | extern "C" fn f3_3(_: ..., x: isize) {} | ^^^^^^ -error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:33:1 - | -LL | const unsafe extern "C" fn f4_1(x: isize, _: ...) {} - | ^^^^^ `const` because of this ^^^^^^ C-variadic because of this - -error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:37:1 - | -LL | const extern "C" fn f4_2(x: isize, _: ...) {} - | ^^^^^ `const` because of this ^^^^^^ C-variadic because of this - error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:37:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 | LL | const extern "C" fn f4_2(x: isize, _: ...) {} | ^^^^^^ @@ -104,19 +92,13 @@ LL | const unsafe extern "C" fn f4_2(x: isize, _: ...) {} | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:42:26 + --> $DIR/variadic-ffi-semantic-restrictions.rs:40:26 | LL | const extern "C" fn f4_3(_: ..., x: isize, _: ...) {} | ^^^^^^ -error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:42:1 - | -LL | const extern "C" fn f4_3(_: ..., x: isize, _: ...) {} - | ^^^^^ `const` because of this ^^^^^^ C-variadic because of this - error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:42:44 + --> $DIR/variadic-ffi-semantic-restrictions.rs:40:44 | LL | const extern "C" fn f4_3(_: ..., x: isize, _: ...) {} | ^^^^^^ @@ -127,13 +109,13 @@ LL | const unsafe extern "C" fn f4_3(_: ..., x: isize, _: ...) {} | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:48:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:45:13 | LL | fn e_f2(..., x: isize); | ^^^ error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:55:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:52:23 | LL | fn i_f1(x: isize, _: ...) {} | ^^^^^^ @@ -141,7 +123,7 @@ LL | fn i_f1(x: isize, _: ...) {} = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:57:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 | LL | fn i_f2(_: ...) {} | ^^^^^^ @@ -149,13 +131,13 @@ LL | fn i_f2(_: ...) {} = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:56:13 | LL | fn i_f3(_: ..., x: isize, _: ...) {} | ^^^^^^ error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:59:31 + --> $DIR/variadic-ffi-semantic-restrictions.rs:56:31 | LL | fn i_f3(_: ..., x: isize, _: ...) {} | ^^^^^^ @@ -163,29 +145,21 @@ LL | fn i_f3(_: ..., x: isize, _: ...) {} = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 | LL | fn i_f4(_: ..., x: isize, _: ...) {} | ^^^^^^ error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:31 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:31 | LL | fn i_f4(_: ..., x: isize, _: ...) {} | ^^^^^^ | = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:65:5 - | -LL | const fn i_f5(x: isize, _: ...) {} - | ^^^^^ ^^^^^^ C-variadic because of this - | | - | `const` because of this - error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 | LL | const fn i_f5(x: isize, _: ...) {} | ^^^^^^ @@ -193,7 +167,7 @@ LL | const fn i_f5(x: isize, _: ...) {} = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:72:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:68:23 | LL | fn t_f1(x: isize, _: ...) {} | ^^^^^^ @@ -201,7 +175,7 @@ LL | fn t_f1(x: isize, _: ...) {} = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:74:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:70:23 | LL | fn t_f2(x: isize, _: ...); | ^^^^^^ @@ -209,7 +183,7 @@ LL | fn t_f2(x: isize, _: ...); = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:76:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:72:13 | LL | fn t_f3(_: ...) {} | ^^^^^^ @@ -217,7 +191,7 @@ LL | fn t_f3(_: ...) {} = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:78:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:74:13 | LL | fn t_f4(_: ...); | ^^^^^^ @@ -225,13 +199,13 @@ LL | fn t_f4(_: ...); = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:80:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:76:13 | LL | fn t_f5(_: ..., x: isize) {} | ^^^^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:78:13 | LL | fn t_f6(_: ..., x: isize); | ^^^^^^ @@ -245,7 +219,7 @@ LL | const unsafe extern "C" fn f4_1(x: isize, _: ...) {} | the destructor for this type cannot be evaluated in constant functions error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:37:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 | LL | const extern "C" fn f4_2(x: isize, _: ...) {} | ^ - value is dropped here @@ -253,13 +227,13 @@ LL | const extern "C" fn f4_2(x: isize, _: ...) {} | the destructor for this type cannot be evaluated in constant functions error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 | LL | const fn i_f5(x: isize, _: ...) {} | ^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions -error: aborting due to 33 previous errors +error: aborting due to 29 previous errors For more information about this error, try `rustc --explain E0493`. From ce693807f66a5dc7ee269b0b73860c5f0796eb19 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 2 Jan 2026 16:02:28 +0100 Subject: [PATCH 03/13] make `Va::arg` and `VaList::drop` `const fn`s --- library/core/src/ffi/va_list.rs | 6 ++++-- library/core/src/intrinsics/mod.rs | 4 ++-- .../parser/variadic-ffi-semantic-restrictions.stderr | 12 ++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index d0f155316a109..a761b24895cf5 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -216,7 +216,8 @@ impl Clone for VaList<'_> { } } -impl<'f> Drop for VaList<'f> { +#[rustc_const_unstable(feature = "c_variadic_const", issue = "none")] +impl<'f> const Drop for VaList<'f> { fn drop(&mut self) { // SAFETY: this variable argument list is being dropped, so won't be read from again. unsafe { va_end(self) } @@ -291,7 +292,8 @@ impl<'f> VaList<'f> { /// /// [valid]: https://doc.rust-lang.org/nightly/nomicon/what-unsafe-does.html #[inline] - pub unsafe fn arg(&mut self) -> T { + #[rustc_const_unstable(feature = "c_variadic_const", issue = "none")] + pub const unsafe fn arg(&mut self) -> T { // SAFETY: the caller must uphold the safety contract for `va_arg`. unsafe { va_arg(self) } } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 3ddea90652d16..4aacafb75bd22 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3472,7 +3472,7 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize /// #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn va_arg(ap: &mut VaList<'_>) -> T; +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; /// Duplicates a variable argument list. The returned list is initially at the same position as /// the one in `src`, but can be advanced independently. @@ -3503,6 +3503,6 @@ pub fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> { /// #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn va_end(ap: &mut VaList<'_>) { +pub const unsafe fn va_end(ap: &mut VaList<'_>) { /* deliberately does nothing */ } diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 20a182b8c49f3..26d5cdaf995aa 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -217,6 +217,10 @@ LL | const unsafe extern "C" fn f4_1(x: isize, _: ...) {} | ^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions + | + = note: see issue #133214 for more information + = help: add `#![feature(const_destruct)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 @@ -225,6 +229,10 @@ LL | const extern "C" fn f4_2(x: isize, _: ...) {} | ^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions + | + = note: see issue #133214 for more information + = help: add `#![feature(const_destruct)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 @@ -233,6 +241,10 @@ LL | const fn i_f5(x: isize, _: ...) {} | ^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions + | + = note: see issue #133214 for more information + = help: add `#![feature(const_destruct)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 29 previous errors From 02c4af397e3a15cfde560907c3a541a438a4e70e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 2 Jan 2026 16:10:28 +0100 Subject: [PATCH 04/13] c-variadic functions in `rustc_const_eval` --- .../src/const_eval/machine.rs | 4 +- compiler/rustc_const_eval/src/errors.rs | 4 + .../rustc_const_eval/src/interpret/call.rs | 78 ++++- .../src/interpret/intrinsics.rs | 112 +++++- .../rustc_const_eval/src/interpret/memory.rs | 49 ++- .../rustc_const_eval/src/interpret/stack.rs | 33 +- .../rustc_middle/src/mir/interpret/error.rs | 4 + .../rustc_middle/src/mir/interpret/mod.rs | 2 +- library/core/src/ffi/va_list.rs | 7 +- library/core/src/intrinsics/mod.rs | 2 +- src/tools/miri/src/alloc_addresses/mod.rs | 6 +- .../src/borrow_tracker/stacked_borrows/mod.rs | 8 +- .../src/borrow_tracker/tree_borrows/mod.rs | 6 +- tests/ui/consts/const-eval/c-variadic-fail.rs | 147 ++++++++ .../consts/const-eval/c-variadic-fail.stderr | 324 ++++++++++++++++++ tests/ui/consts/const-eval/c-variadic.rs | 155 +++++++++ 16 files changed, 908 insertions(+), 33 deletions(-) create mode 100644 tests/ui/consts/const-eval/c-variadic-fail.rs create mode 100644 tests/ui/consts/const-eval/c-variadic-fail.stderr create mode 100644 tests/ui/consts/const-eval/c-variadic.rs diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index e017362b8c4b5..8c182186db89b 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -9,7 +9,7 @@ use rustc_errors::msg; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem}; use rustc_middle::mir::AssertMessage; -use rustc_middle::mir::interpret::{Pointer, ReportedErrorInfo}; +use rustc_middle::mir::interpret::ReportedErrorInfo; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -22,7 +22,7 @@ use super::error::*; use crate::errors::{LongRunning, LongRunningWarn}; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, - GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, RangeSet, Scalar, + GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar, compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index b477c998a278b..9de7d02184b38 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -757,6 +757,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { WriteToReadOnly(_) => msg!("writing to {$allocation} which is read-only"), DerefFunctionPointer(_) => msg!("accessing {$allocation} which contains a function"), DerefVTablePointer(_) => msg!("accessing {$allocation} which contains a vtable"), + DerefVaListPointer(_) => msg!("accessing {$allocation} which contains a variable argument list"), DerefTypeIdPointer(_) => msg!("accessing {$allocation} which contains a `TypeId`"), InvalidBool(_) => msg!("interpreting an invalid 8-bit value as a bool: 0x{$value}"), InvalidChar(_) => msg!("interpreting an invalid 32-bit value as a char: 0x{$value}"), @@ -776,6 +777,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { } AbiMismatchArgument { .. } => msg!("calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty}"), AbiMismatchReturn { .. } => msg!("calling a function with return type {$callee_ty} passing return place of type {$caller_ty}"), + VaArgOutOfBounds => "more C-variadic arguments read than were passed".into(), } } @@ -800,6 +802,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { | InvalidMeta(InvalidMetaKind::TooBig) | InvalidUninitBytes(None) | DeadLocal + | VaArgOutOfBounds | UninhabitedEnumVariantWritten(_) | UninhabitedEnumVariantRead(_) => {} @@ -874,6 +877,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) + | DerefVaListPointer(alloc) | DerefTypeIdPointer(alloc) => { diag.arg("allocation", alloc); } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index b17740f57f786..3efac883b9c4b 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use either::{Left, Right}; -use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx}; +use rustc_abi::{self as abi, ExternAbi, FieldIdx, HasDataLayout, Integer, Size, VariantIdx}; use rustc_data_structures::assert_matches; use rustc_errors::msg; use rustc_hir::def_id::DefId; @@ -17,9 +17,9 @@ use tracing::field::Empty; use tracing::{info, instrument, trace}; use super::{ - CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, - Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo, interp_ok, - throw_ub, throw_ub_custom, throw_unsup_format, + CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, MemoryKind, OpTy, + PlaceTy, Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo, + interp_ok, throw_ub, throw_ub_custom, }; use crate::enter_trace_span; use crate::interpret::EnteredTraceSpan; @@ -354,12 +354,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx> { let _trace = enter_trace_span!(M, step::init_stack_frame, %instance, tracing_separate_thread = Empty); - // Compute callee information. - // FIXME: for variadic support, do we have to somehow determine callee's extra_args? - let callee_fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; + let (fixed_count, c_variadic_args) = if caller_fn_abi.c_variadic { + let sig = self.tcx.fn_sig(instance.def_id()).skip_binder(); + let fixed_count = sig.inputs().skip_binder().len(); + assert!(caller_fn_abi.args.len() >= fixed_count); + let extra_tys: Vec> = + caller_fn_abi.args[fixed_count..].iter().map(|arg_abi| arg_abi.layout.ty).collect(); - if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic { - throw_unsup_format!("calling a c-variadic function is not supported"); + (fixed_count, self.tcx.mk_type_list(&extra_tys)) + } else { + (caller_fn_abi.args.len(), ty::List::empty()) + }; + + let callee_fn_abi = self.fn_abi_of_instance(instance, c_variadic_args)?; + + if callee_fn_abi.c_variadic ^ caller_fn_abi.c_variadic { + unreachable!("caller and callee disagree on being c-variadic"); } if caller_fn_abi.conv != callee_fn_abi.conv { @@ -443,8 +453,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // this is a single iterator (that handles `spread_arg`), then // `pass_argument` would be the loop body. It takes care to // not advance `caller_iter` for ignored arguments. - let mut callee_args_abis = callee_fn_abi.args.iter().enumerate(); - for local in body.args_iter() { + let mut callee_args_abis = if caller_fn_abi.c_variadic { + callee_fn_abi.args[..fixed_count].iter().enumerate() + } else { + callee_fn_abi.args.iter().enumerate() + }; + + let mut it = body.args_iter().peekable(); + while let Some(local) = it.next() { // Construct the destination place for this argument. At this point all // locals are still dead, so we cannot construct a `PlaceTy`. let dest = mir::Place::from(local); @@ -452,7 +468,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // type, but the result gets cached so this avoids calling the instantiation // query *again* the next time this local is accessed. let ty = self.layout_of_local(self.frame(), local, None)?.ty; - if Some(local) == body.spread_arg { + if caller_fn_abi.c_variadic && it.peek().is_none() { + // The callee's signature has an additional VaList argument, that the caller + // won't actually pass. Here we synthesize a `VaList` value, whose leading bytes + // are a pointer that can be mapped to the corresponding variable argument list. + self.storage_live(local)?; + + let place = self.eval_place(dest)?; + let mplace = self.force_allocation(&place)?; + + // Consume the remaining arguments and store them in a global allocation. + let mut varargs = Vec::new(); + for (fn_arg, abi) in &mut caller_args { + let op = self.copy_fn_arg(fn_arg); + let mplace = self.allocate(abi.layout, MemoryKind::Stack)?; + self.copy_op(&op, &mplace)?; + + varargs.push(mplace); + } + + // When the frame is dropped, this ID is used to deallocate the variable arguments list. + self.frame_mut().va_list = varargs.clone(); + + // This is a new VaList, so start at index 0. + let ptr = self.va_list_ptr(varargs, 0); + let addr = Scalar::from_pointer(ptr, self); + + // Zero the mplace, so it is fully initialized. + self.write_bytes_ptr( + mplace.ptr(), + (0..mplace.layout.size.bytes()).map(|_| 0u8), + )?; + + // Store the pointer to the global variable arguments list allocation in the + // first bytes of the `VaList` value. + let mut alloc = self + .get_ptr_alloc_mut(mplace.ptr(), self.data_layout().pointer_size())? + .expect("not a ZST"); + alloc.write_ptr_sized(Size::ZERO, addr)?; + } else if Some(local) == body.spread_arg { // Make the local live once, then fill in the value field by field. self.storage_live(local)?; // Must be a tuple diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index c9a3b578e7933..aa7c889b3f3f2 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -24,7 +24,7 @@ use super::util::ensure_monomorphic_enough; use super::{ AllocId, CheckInAllocMsg, ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format, interp_ok, throw_inval, - throw_ub_custom, throw_ub_format, + throw_ub, throw_ub_custom, throw_ub_format, throw_unsup_format, }; use crate::interpret::Writeable; @@ -750,6 +750,116 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.float_muladd_intrinsic::(args, dest, MulAddType::Nondeterministic)? } + sym::va_copy => { + // fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> + let src_ptr = self.read_pointer(&args[0])?; + + // Read the token pointer from the src VaList (alloc_id + offset-as-index). + let src_va_list_ptr = { + let pointer_size = tcx.data_layout.pointer_size(); + let alloc = self + .get_ptr_alloc(src_ptr, pointer_size)? + .expect("va_list storage should not be a ZST"); + let scalar = alloc.read_pointer(Size::ZERO)?; + scalar.to_pointer(self)? + }; + + let (prov, offset) = src_va_list_ptr.into_raw_parts(); + let src_alloc_id = prov.unwrap().get_alloc_id().unwrap(); + let index = offset.bytes(); + + // Look up arguments without consuming src. + let Some(arguments) = self.get_va_list_alloc(src_alloc_id) else { + throw_unsup_format!("va_copy on unknown va_list allocation {:?}", src_alloc_id); + }; + + // Create a new allocation pointing at the same index. + let new_va_list_ptr = self.va_list_ptr(arguments.to_vec(), index); + let addr = Scalar::from_pointer(new_va_list_ptr, self); + + // Now overwrite the token pointer stored inside the VaList. + let mplace = self.force_allocation(dest)?; + let mut alloc = self.get_place_alloc_mut(&mplace)?.unwrap(); + alloc.write_ptr_sized(Size::ZERO, addr)?; + } + + sym::va_end => { + let ptr_size = self.tcx.data_layout.pointer_size(); + + // The only argument is a `&mut VaList`. + let ap_ref = self.read_pointer(&args[0])?; + + // The first bytes of the `VaList` value store a pointer. The `AllocId` of this + // pointer is a key into a global map of variable argument lists. The offset is + // used as the index of the argument to read. + let va_list_ptr = { + let alloc = self + .get_ptr_alloc(ap_ref, ptr_size)? + .expect("va_list storage should not be a ZST"); + let scalar = alloc.read_pointer(Size::ZERO)?; + scalar.to_pointer(self)? + }; + + let (prov, _offset) = va_list_ptr.into_raw_parts(); + let alloc_id = prov.unwrap().get_alloc_id().unwrap(); + + let Some(_) = self.remove_va_list_alloc(alloc_id) else { + throw_unsup_format!("va_end on unknown va_list allocation {:?}", alloc_id) + }; + } + + sym::va_arg => { + let ptr_size = self.tcx.data_layout.pointer_size(); + + // The only argument is a `&mut VaList`. + let ap_ref = self.read_pointer(&args[0])?; + + // The first bytes of the `VaList` value store a pointer. The `AllocId` of this + // pointer is a key into a global map of variable argument lists. The offset is + // used as the index of the argument to read. + let va_list_ptr = { + let alloc = self + .get_ptr_alloc(ap_ref, ptr_size)? + .expect("va_list storage should not be a ZST"); + let scalar = alloc.read_pointer(Size::ZERO)?; + scalar.to_pointer(self)? + }; + + let (prov, offset) = va_list_ptr.into_raw_parts(); + let alloc_id = prov.unwrap().get_alloc_id().unwrap(); + let index = offset.bytes(); + + let Some(varargs) = self.remove_va_list_alloc(alloc_id) else { + throw_unsup_format!("va_arg on unknown va_list allocation {:?}", alloc_id) + }; + + let Some(src_mplace) = varargs.get(offset.bytes_usize()).cloned() else { + throw_ub!(VaArgOutOfBounds) + }; + + // Update the offset in this `VaList` value so that a subsequent call to `va_arg` + // reads the next argument. + let new_va_list_ptr = self.va_list_ptr(varargs, index + 1); + let addr = Scalar::from_pointer(new_va_list_ptr, self); + let mut alloc = self + .get_ptr_alloc_mut(ap_ref, ptr_size)? + .expect("va_list storage should not be a ZST"); + alloc.write_ptr_sized(Size::ZERO, addr)?; + + // NOTE: In C some type conversions are allowed (e.g. casting between signed and + // unsigned integers). For now we require c-variadic arguments to be read with the + // exact type they were passed as. + if src_mplace.layout.ty != dest.layout.ty { + throw_unsup_format!( + "va_arg type mismatch: requested `{}`, but next argument is `{}`", + dest.layout.ty, + src_mplace.layout.ty + ); + } + + self.copy_op(&src_mplace, dest)?; + } + // Unsupported intrinsic: skip the return_to_block below. _ => return interp_ok(false), } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 0049feee523ca..aadbe84ca8093 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -23,8 +23,8 @@ use tracing::{debug, instrument, trace}; use super::{ AllocBytes, AllocId, AllocInit, AllocMap, AllocRange, Allocation, CheckAlignMsg, - CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, - Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, + CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine, + MayLeak, Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; use crate::const_eval::ConstEvalErrKind; @@ -67,6 +67,8 @@ pub enum AllocKind { LiveData, /// A function allocation (that fn ptrs point to). Function, + /// A variable argument list allocation (used by c-variadic functions). + VaList, /// A vtable allocation. VTable, /// A TypeId allocation. @@ -126,6 +128,9 @@ pub struct Memory<'tcx, M: Machine<'tcx>> { /// Map for "extra" function pointers. extra_fn_ptr_map: FxIndexMap, + /// Map storing variable argument lists. + va_list_map: FxIndexMap>>, + /// To be able to compare pointers with null, and to check alignment for accesses /// to ZSTs (where pointers may dangle), we keep track of the size even for allocations /// that do not exist any more. @@ -161,6 +166,7 @@ impl<'tcx, M: Machine<'tcx>> Memory<'tcx, M> { Memory { alloc_map: M::MemoryMap::default(), extra_fn_ptr_map: FxIndexMap::default(), + va_list_map: FxIndexMap::default(), dead_alloc_map: FxIndexMap::default(), validation_in_progress: Cell::new(false), } @@ -199,9 +205,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return M::extern_static_pointer(self, def_id); } None => { + let is_fn_ptr = self.memory.extra_fn_ptr_map.contains_key(&alloc_id); + let is_va_list = self.memory.va_list_map.contains_key(&alloc_id); assert!( - self.memory.extra_fn_ptr_map.contains_key(&alloc_id), - "{alloc_id:?} is neither global nor a function pointer" + is_fn_ptr || is_va_list, + "{alloc_id:?} is neither global, va_list nor a function pointer" ); } _ => {} @@ -229,6 +237,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.global_root_pointer(Pointer::from(id)).unwrap() } + pub fn va_list_ptr( + &mut self, + varargs: Vec>, + index: u64, + ) -> Pointer { + let id = self.tcx.reserve_alloc_id(); + let old = self.memory.va_list_map.insert(id, varargs); + assert!(old.is_none()); + // The offset is used to store the current index. + let ptr = Pointer::new(id.into(), Size::from_bytes(index)); + // Variable argument lists are global allocations, so make sure we get the right root + // pointer. We know this is not an `extern static` so this cannot fail. + self.global_root_pointer(ptr).unwrap() + } + pub fn allocate_ptr( &mut self, size: Size, @@ -956,6 +979,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn is_alloc_live(&self, id: AllocId) -> bool { self.memory.alloc_map.contains_key_ref(&id) || self.memory.extra_fn_ptr_map.contains_key(&id) + || self.memory.va_list_map.contains_key(&id) // We check `tcx` last as that has to acquire a lock in `many-seeds` mode. // This also matches the order in `get_alloc_info`. || self.tcx.try_get_global_alloc(id).is_some() @@ -995,6 +1019,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return AllocInfo::new(Size::ZERO, align, AllocKind::Function, Mutability::Not); } + // # Variable argument lists + if let Some(_) = self.get_va_list_alloc(id) { + return AllocInfo::new(Size::ZERO, Align::ONE, AllocKind::VaList, Mutability::Not); + } + // # Global allocations if let Some(global_alloc) = self.tcx.try_get_global_alloc(id) { // NOTE: `static` alignment from attributes has already been applied to the allocation. @@ -1042,6 +1071,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + pub fn get_va_list_alloc(&self, id: AllocId) -> Option<&[MPlaceTy<'tcx, M::Provenance>]> { + self.memory.va_list_map.get(&id).map(|v| &**v) + } + + pub fn remove_va_list_alloc( + &mut self, + id: AllocId, + ) -> Option>> { + self.memory.dead_alloc_map.insert(id, (Size::ZERO, Align::ONE)); + self.memory.va_list_map.swap_remove(&id) + } + /// Takes a pointer that is the first chunk of a `TypeId` and return the type that its /// provenance refers to, as well as the segment of the hash that this pointer covers. pub fn get_ptr_type_id( diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 1c1c59da9d886..9b2d99bb297f4 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -16,9 +16,9 @@ use tracing::field::Empty; use tracing::{info_span, instrument, trace}; use super::{ - AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, Machine, MemPlace, MemPlaceMeta, - MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout, - interp_ok, throw_ub, throw_unsup, + AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, + MemPlaceMeta, MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, + from_known_layout, interp_ok, throw_ub, throw_unsup, }; use crate::{enter_trace_span, errors}; @@ -91,6 +91,10 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { /// Do *not* access this directly; always go through the machine hook! pub locals: IndexVec>, + /// The complete variable argument list of this frame. Its elements must be dropped when the + /// frame is popped. + pub(super) va_list: Vec>, + /// The span of the `tracing` crate is stored here. /// When the guard is dropped, the span is exited. This gives us /// a full stack trace on all tracing statements. @@ -259,6 +263,7 @@ impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> { return_cont: self.return_cont, return_place: self.return_place, locals: self.locals, + va_list: self.va_list, loc: self.loc, extra, tracing_span: self.tracing_span, @@ -377,6 +382,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return_cont, return_place: return_place.clone(), locals, + va_list: vec![], instance, tracing_span: SpanGuard::new(), extra: (), @@ -454,6 +460,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.deallocate_local(local.value)?; } + // Deallocate any c-variadic arguments. + for mplace in &frame.va_list { + self.deallocate_vararg(mplace)?; + } + // Call the machine hook, which determines the next steps. let return_action = M::after_stack_pop(self, frame, unwinding)?; assert_ne!(return_action, ReturnAction::NoCleanup); @@ -599,6 +610,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(()) } + fn deallocate_vararg(&mut self, vararg: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { + let ptr = vararg.ptr(); + + // FIXME: is the `unwrap` valid here? + trace!( + "deallocating vararg {:?}: {:?}", + vararg, + // FIXME: what do we do with this comment? + // Locals always have a `alloc_id` (they are never the result of a int2ptr). + self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap()) + ); + self.deallocate_ptr(ptr, None, MemoryKind::Stack)?; + + interp_ok(()) + } + /// This is public because it is used by [Aquascope](https://github.com/cognitive-engineering-lab/aquascope/) /// to analyze all the locals in a stack frame. #[inline(always)] diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 66c928f518aa3..970dbd95f7cc3 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -392,6 +392,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { DerefFunctionPointer(AllocId), /// Trying to access the data behind a vtable pointer. DerefVTablePointer(AllocId), + /// Trying to access the data behind a va_list pointer. + DerefVaListPointer(AllocId), /// Trying to access the actual type id. DerefTypeIdPointer(AllocId), /// Using a non-boolean `u8` as bool. @@ -434,6 +436,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { }, /// ABI-incompatible return types. AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> }, + /// `va_arg` was called on an exhausted `VaList`. + VaArgOutOfBounds, } #[derive(Debug, Clone, Copy)] diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 0abc3aec7e000..a4ab1f364c168 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -396,7 +396,7 @@ impl<'tcx> GlobalAlloc<'tcx> { // No data to be accessed here. But vtables are pointer-aligned. (Size::ZERO, tcx.data_layout.pointer_align().abi) } - // Fake allocation, there's nothing to access here + // Fake allocation, there's nothing to access here. GlobalAlloc::TypeId { .. } => (Size::ZERO, Align::ONE), } } diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index a761b24895cf5..45a9b7ba5293e 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -200,12 +200,13 @@ impl fmt::Debug for VaList<'_> { impl VaList<'_> { // Helper used in the implementation of the `va_copy` intrinsic. - pub(crate) fn duplicate(&self) -> Self { - Self { inner: self.inner.clone(), _marker: self._marker } + pub(crate) const fn duplicate(&self) -> Self { + Self { inner: self.inner, _marker: self._marker } } } -impl Clone for VaList<'_> { +#[rustc_const_unstable(feature = "c_variadic_const", issue = "none")] +impl<'f> const Clone for VaList<'f> { #[inline] fn clone(&self) -> Self { // We only implement Clone and not Copy because some future target might not be able to diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 4aacafb75bd22..66e68cb866db9 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3484,7 +3484,7 @@ pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; /// when a variable argument list is used incorrectly. #[rustc_intrinsic] #[rustc_nounwind] -pub fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> { +pub const fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> { src.duplicate() } diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index fed51ed86433c..59799ac613b81 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -185,7 +185,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { #[cfg(not(all(unix, feature = "native-lib")))] AllocKind::Function => dummy_alloc(params), AllocKind::VTable => dummy_alloc(params), - AllocKind::TypeId | AllocKind::Dead => unreachable!(), + AllocKind::TypeId | AllocKind::Dead | AllocKind::VaList => unreachable!(), }; // We don't have to expose this pointer yet, we do that in `prepare_for_native_call`. return interp_ok(base_ptr.addr().to_u64()); @@ -363,8 +363,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ProvenanceMode::Default => { // The first time this happens at a particular location, print a warning. static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new(); - this.dedup_diagnostic(&DEDUP, |first| { - NonHaltingDiagnostic::Int2Ptr { details: first } + this.dedup_diagnostic(&DEDUP, |first| NonHaltingDiagnostic::Int2Ptr { + details: first, }); } ProvenanceMode::Strict => { diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index a21898c506ab9..c9f26d5fefaad 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -651,7 +651,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { dcx.log_protector(); } }, - AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead => { + AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead | AllocKind::VaList => { // No stacked borrows on these allocations. } } @@ -1010,7 +1010,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}"); alloc_extra.borrow_tracker_sb().borrow_mut().exposed_tags.insert(tag); } - AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead => { + AllocKind::Function + | AllocKind::VTable + | AllocKind::TypeId + | AllocKind::Dead + | AllocKind::VaList => { // No stacked borrows on these allocations. } } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 173145788ee39..d5d57115ebcbc 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -576,7 +576,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let protected = protected_tags.contains_key(&tag); alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag, protected); } - AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead => { + AllocKind::Function + | AllocKind::VTable + | AllocKind::TypeId + | AllocKind::Dead + | AllocKind::VaList => { // No tree borrows on these allocations. } } diff --git a/tests/ui/consts/const-eval/c-variadic-fail.rs b/tests/ui/consts/const-eval/c-variadic-fail.rs new file mode 100644 index 0000000000000..37810ddb356df --- /dev/null +++ b/tests/ui/consts/const-eval/c-variadic-fail.rs @@ -0,0 +1,147 @@ +//@ build-fail + +#![feature(c_variadic)] +#![feature(c_variadic_const)] +#![feature(const_trait_impl)] +#![feature(const_destruct)] +#![feature(const_clone)] + +use std::ffi::VaList; + +const unsafe extern "C" fn read_n(mut ap: ...) { + let mut i = N; + while i > 0 { + i -= 1; + let _ = ap.arg::(); + } +} + +unsafe fn read_too_many() { + // None passed, none read. + const { read_n::<0>() } + + // One passed, none read. Ignoring arguments is fine. + const { read_n::<0>(1) } + + // None passed, one read. + const { read_n::<1>() } + //~^ ERROR more C-variadic arguments read than were passed + + // One passed, two read. + const { read_n::<2>(1) } + //~^ ERROR more C-variadic arguments read than were passed +} + +const unsafe extern "C" fn read_as(mut ap: ...) -> T { + ap.arg::() +} + +unsafe fn read_cast() { + const { read_as::(1i32) }; + const { read_as::(1u32) }; + + const { read_as::(1i32, 2u64, 3.0f64) }; + const { read_as::(1u32, 2u64, 3.0f64) }; + + const { read_as::(1i64) }; + const { read_as::(1u64) }; + + const { read_as::(1i32) }; + //~^ ERROR va_arg type mismatch: requested `u32`, but next argument is `i32` + + const { read_as::(1u32) }; + //~^ ERROR va_arg type mismatch: requested `i32`, but next argument is `u32` + + const { read_as::(1u64) }; + //~^ ERROR va_arg type mismatch: requested `i32`, but next argument is `u64` + + const { read_as::(1i32) }; + //~^ ERROR va_arg type mismatch: requested `f64`, but next argument is `i32` + + const { read_as::<*const u8>(1i32) }; + //~^ ERROR va_arg type mismatch: requested `*const u8`, but next argument is `i32` +} + +fn use_after_free() { + const unsafe extern "C" fn helper(ap: ...) -> [u8; size_of::()] { + unsafe { std::mem::transmute(ap) } + } + + const { + unsafe { + let ap = helper(1, 2, 3); + let mut ap = std::mem::transmute::<_, VaList>(ap); + ap.arg::(); + //~^ ERROR memory access failed: ALLOC0 has been freed, so this pointer is dangling [E0080] + } + }; +} + +macro_rules! va_list_copy { + ($ap:expr) => {{ + // A copy created using Clone is valid, and can be used to read arguments. + let mut copy = $ap.clone(); + assert!(copy.arg::() == 1i32); + + let mut u = core::mem::MaybeUninit::uninit(); + unsafe { core::ptr::copy_nonoverlapping(&$ap, u.as_mut_ptr(), 1) }; + + // Manually creating the copy is fine. + unsafe { u.assume_init() } + }}; +} + +fn manual_copy_drop() { + const unsafe extern "C" fn helper(ap: ...) { + let mut copy: VaList = va_list_copy!(ap); + + // Using the copy is actually fine. + let _ = copy.arg::(); + drop(copy); + + // But then using the original is UB. + drop(ap); + } + + const { unsafe { helper(1, 2, 3) } }; + //~^ ERROR va_end on unknown va_list allocation ALLOC0 [E0080] +} + +fn manual_copy_forget() { + const unsafe extern "C" fn helper(ap: ...) { + let mut copy: VaList = va_list_copy!(ap); + + // Using the copy is actually fine. + let _ = copy.arg::(); + std::mem::forget(copy); + + // The read (via `copy`) deallocated the original allocation. + drop(ap); + } + + const { unsafe { helper(1, 2, 3) } }; + //~^ ERROR va_end on unknown va_list allocation ALLOC0 [E0080] +} + +fn manual_copy_read() { + const unsafe extern "C" fn helper(mut ap: ...) { + let mut copy: VaList = va_list_copy!(ap); + + // Reading from `ap` after reading from `copy` is UB. + let _ = copy.arg::(); + let _ = ap.arg::(); + } + + const { unsafe { helper(1, 2, 3) } }; + //~^ ERROR va_arg on unknown va_list allocation ALLOC0 +} + +fn main() { + unsafe { + read_too_many(); + read_cast(); + manual_copy_read(); + manual_copy_drop(); + manual_copy_forget(); + } +} diff --git a/tests/ui/consts/const-eval/c-variadic-fail.stderr b/tests/ui/consts/const-eval/c-variadic-fail.stderr new file mode 100644 index 0000000000000..4c569a4492d47 --- /dev/null +++ b/tests/ui/consts/const-eval/c-variadic-fail.stderr @@ -0,0 +1,324 @@ +error[E0080]: more C-variadic arguments read than were passed + --> $DIR/c-variadic-fail.rs:27:13 + | +LL | const { read_n::<1>() } + | ^^^^^^^^^^^^^ evaluation of `read_too_many::{constant#2}` failed inside this call + | +note: inside `read_n::<1>` + --> $DIR/c-variadic-fail.rs:15:17 + | +LL | let _ = ap.arg::(); + | ^^^^^^^^^^^^^^^ +note: inside `VaList::<'_>::arg::` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:27:5 + | +LL | const { read_n::<1>() } + | ^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:27:5 + | +LL | const { read_n::<1>() } + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: more C-variadic arguments read than were passed + --> $DIR/c-variadic-fail.rs:31:13 + | +LL | const { read_n::<2>(1) } + | ^^^^^^^^^^^^^^ evaluation of `read_too_many::{constant#3}` failed inside this call + | +note: inside `read_n::<2>` + --> $DIR/c-variadic-fail.rs:15:17 + | +LL | let _ = ap.arg::(); + | ^^^^^^^^^^^^^^^ +note: inside `VaList::<'_>::arg::` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:31:5 + | +LL | const { read_n::<2>(1) } + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:31:5 + | +LL | const { read_n::<2>(1) } + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg type mismatch: requested `u32`, but next argument is `i32` + --> $DIR/c-variadic-fail.rs:49:13 + | +LL | const { read_as::(1i32) }; + | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#6}` failed inside this call + | +note: inside `read_as::` + --> $DIR/c-variadic-fail.rs:36:5 + | +LL | ap.arg::() + | ^^^^^^^^^^^^^ +note: inside `VaList::<'_>::arg::` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:49:5 + | +LL | const { read_as::(1i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:49:5 + | +LL | const { read_as::(1i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg type mismatch: requested `i32`, but next argument is `u32` + --> $DIR/c-variadic-fail.rs:52:13 + | +LL | const { read_as::(1u32) }; + | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#7}` failed inside this call + | +note: inside `read_as::` + --> $DIR/c-variadic-fail.rs:36:5 + | +LL | ap.arg::() + | ^^^^^^^^^^^^^ +note: inside `VaList::<'_>::arg::` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:52:5 + | +LL | const { read_as::(1u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:52:5 + | +LL | const { read_as::(1u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg type mismatch: requested `i32`, but next argument is `u64` + --> $DIR/c-variadic-fail.rs:55:13 + | +LL | const { read_as::(1u64) }; + | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#8}` failed inside this call + | +note: inside `read_as::` + --> $DIR/c-variadic-fail.rs:36:5 + | +LL | ap.arg::() + | ^^^^^^^^^^^^^ +note: inside `VaList::<'_>::arg::` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:55:5 + | +LL | const { read_as::(1u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:55:5 + | +LL | const { read_as::(1u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg type mismatch: requested `f64`, but next argument is `i32` + --> $DIR/c-variadic-fail.rs:58:13 + | +LL | const { read_as::(1i32) }; + | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#9}` failed inside this call + | +note: inside `read_as::` + --> $DIR/c-variadic-fail.rs:36:5 + | +LL | ap.arg::() + | ^^^^^^^^^^^^^ +note: inside `VaList::<'_>::arg::` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:58:5 + | +LL | const { read_as::(1i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:58:5 + | +LL | const { read_as::(1i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg type mismatch: requested `*const u8`, but next argument is `i32` + --> $DIR/c-variadic-fail.rs:61:13 + | +LL | const { read_as::<*const u8>(1i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#10}` failed inside this call + | +note: inside `read_as::<*const u8>` + --> $DIR/c-variadic-fail.rs:36:5 + | +LL | ap.arg::() + | ^^^^^^^^^^^^^ +note: inside `VaList::<'_>::arg::<*const u8>` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:61:5 + | +LL | const { read_as::<*const u8>(1i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:61:5 + | +LL | const { read_as::<*const u8>(1i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: memory access failed: ALLOC0 has been freed, so this pointer is dangling + --> $DIR/c-variadic-fail.rs:74:13 + | +LL | ap.arg::(); + | ^^^^^^^^^^^^^^^ evaluation of `use_after_free::{constant#0}` failed inside this call + | +note: inside `VaList::<'_>::arg::` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:70:5 + | +LL | / const { +LL | | unsafe { +LL | | let ap = helper(1, 2, 3); +LL | | let mut ap = std::mem::transmute::<_, VaList>(ap); +... | +LL | | }; + | |_____^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:70:5 + | +LL | / const { +LL | | unsafe { +LL | | let ap = helper(1, 2, 3); +LL | | let mut ap = std::mem::transmute::<_, VaList>(ap); +... | +LL | | }; + | |_____^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_end on unknown va_list allocation ALLOC1 + --> $DIR/c-variadic-fail.rs:106:22 + | +LL | const { unsafe { helper(1, 2, 3) } }; + | ^^^^^^^^^^^^^^^ evaluation of `manual_copy_drop::{constant#0}` failed inside this call + | +note: inside `manual_copy_drop::helper` + --> $DIR/c-variadic-fail.rs:103:9 + | +LL | drop(ap); + | ^^^^^^^^ +note: inside `std::mem::drop::>` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL +note: inside `drop_in_place::> - shim(Some(VaList<'_>))` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL +note: inside ` as Drop>::drop` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:106:5 + | +LL | const { unsafe { helper(1, 2, 3) } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:106:5 + | +LL | const { unsafe { helper(1, 2, 3) } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_end on unknown va_list allocation ALLOC2 + --> $DIR/c-variadic-fail.rs:122:22 + | +LL | const { unsafe { helper(1, 2, 3) } }; + | ^^^^^^^^^^^^^^^ evaluation of `manual_copy_forget::{constant#0}` failed inside this call + | +note: inside `manual_copy_forget::helper` + --> $DIR/c-variadic-fail.rs:119:9 + | +LL | drop(ap); + | ^^^^^^^^ +note: inside `std::mem::drop::>` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL +note: inside `drop_in_place::> - shim(Some(VaList<'_>))` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL +note: inside ` as Drop>::drop` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:122:5 + | +LL | const { unsafe { helper(1, 2, 3) } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:122:5 + | +LL | const { unsafe { helper(1, 2, 3) } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0080]: va_arg on unknown va_list allocation ALLOC3 + --> $DIR/c-variadic-fail.rs:135:22 + | +LL | const { unsafe { helper(1, 2, 3) } }; + | ^^^^^^^^^^^^^^^ evaluation of `manual_copy_read::{constant#0}` failed inside this call + | +note: inside `manual_copy_read::helper` + --> $DIR/c-variadic-fail.rs:132:17 + | +LL | let _ = ap.arg::(); + | ^^^^^^^^^^^^^^^ +note: inside `VaList::<'_>::arg::` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:135:5 + | +LL | const { unsafe { helper(1, 2, 3) } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:135:5 + | +LL | const { unsafe { helper(1, 2, 3) } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 11 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/c-variadic.rs b/tests/ui/consts/const-eval/c-variadic.rs new file mode 100644 index 0000000000000..ec49b5cc5831d --- /dev/null +++ b/tests/ui/consts/const-eval/c-variadic.rs @@ -0,0 +1,155 @@ +//@ edition: 2021 +//@ run-pass +//@ ignore-backends: gcc + +#![feature(c_variadic)] +#![feature(const_destruct)] +#![feature(c_variadic_const)] +#![feature(const_cmp)] +#![feature(const_trait_impl)] + +use std::ffi::*; + +fn ignores_arguments() { + const unsafe extern "C" fn variadic(_: ...) {} + + const { + unsafe { variadic() }; + unsafe { variadic(1, 2, 3) }; + } +} + +fn echo() { + const unsafe extern "C" fn variadic(mut ap: ...) -> i32 { + ap.arg() + } + + assert_eq!(unsafe { variadic(1) }, 1); + assert_eq!(unsafe { variadic(3, 2, 1) }, 3); + + const { + assert!(unsafe { variadic(1) } == 1); + assert!(unsafe { variadic(3, 2, 1) } == 3); + } +} + +fn forward_by_val() { + const unsafe fn helper(mut ap: VaList) -> i32 { + ap.arg() + } + + const unsafe extern "C" fn variadic(ap: ...) -> i32 { + helper(ap) + } + + assert_eq!(unsafe { variadic(1) }, 1); + assert_eq!(unsafe { variadic(3, 2, 1) }, 3); + + const { + assert!(unsafe { variadic(1) } == 1); + assert!(unsafe { variadic(3, 2, 1) } == 3); + } +} + +fn forward_by_ref() { + const unsafe fn helper(ap: &mut VaList) -> i32 { + ap.arg() + } + + const unsafe extern "C" fn variadic(mut ap: ...) -> i32 { + helper(&mut ap) + } + + assert_eq!(unsafe { variadic(1) }, 1); + assert_eq!(unsafe { variadic(3, 2, 1) }, 3); + + const { + assert!(unsafe { variadic(1) } == 1); + assert!(unsafe { variadic(3, 2, 1) } == 3); + } +} + +#[allow(improper_ctypes_definitions)] +fn nested() { + const unsafe fn helper(mut ap1: VaList, mut ap2: VaList) -> (i32, i32) { + (ap1.arg(), ap2.arg()) + } + + const unsafe extern "C" fn variadic2(ap1: VaList, ap2: ...) -> (i32, i32) { + helper(ap1, ap2) + } + + const unsafe extern "C" fn variadic1(ap1: ...) -> (i32, i32) { + variadic2(ap1, 2, 2) + } + + assert_eq!(unsafe { variadic1(1) }, (1, 2)); + + const { + let (a, b) = unsafe { variadic1(1, 1) }; + + assert!(a != 2); + assert!(a == 1); + assert!(b != 1); + assert!(b == 2); + } +} + +fn various_types() { + const unsafe extern "C" fn check_list_2(mut ap: ...) { + macro_rules! continue_if { + ($cond:expr) => { + if !($cond) { + panic!(); + } + }; + } + + const unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool { + match CStr::from_ptr(ptr).to_str() { + Ok(cstr) => cstr == val, + Err(_) => panic!(), + } + } + + continue_if!(ap.arg::().floor() == 3.14f64.floor()); + continue_if!(ap.arg::() == 12); + continue_if!(ap.arg::() == 'a' as c_int); + continue_if!(ap.arg::().floor() == 6.18f64.floor()); + continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Hello")); + continue_if!(ap.arg::() == 42); + continue_if!(compare_c_str(ap.arg::<*const c_char>(), "World")); + } + + unsafe { + check_list_2( + 3.14 as c_double, + 12 as c_long, + b'a' as c_int, + 6.28 as c_double, + c"Hello".as_ptr(), + 42 as c_int, + c"World".as_ptr(), + ); + const { + check_list_2( + 3.14 as c_double, + 12 as c_long, + b'a' as c_int, + 6.28 as c_double, + c"Hello".as_ptr(), + 42 as c_int, + c"World".as_ptr(), + ) + }; + } +} + +fn main() { + ignores_arguments(); + echo(); + forward_by_val(); + forward_by_ref(); + nested(); + various_types(); +} From ca3d1a3211d70c1ce838e8c68eb24e3e340cac04 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 22 Jan 2026 19:23:51 +0100 Subject: [PATCH 05/13] add c-variadic miri test --- src/tools/miri/tests/pass/c-variadic.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/tools/miri/tests/pass/c-variadic.rs diff --git a/src/tools/miri/tests/pass/c-variadic.rs b/src/tools/miri/tests/pass/c-variadic.rs new file mode 100644 index 0000000000000..a1f03db6872fa --- /dev/null +++ b/src/tools/miri/tests/pass/c-variadic.rs @@ -0,0 +1,18 @@ +#![feature(c_variadic)] + +use core::ffi::VaList; + +fn helper(ap: VaList) -> i32 { + // unsafe { ap.arg::() } + let _ = ap; + 0 +} + +unsafe extern "C" fn variadic(a: i32, ap: ...) -> i32 { + assert_eq!(a, 42); + helper(ap) +} + +fn main() { + assert_eq!(unsafe { variadic(42, 1) }, 1); +} From f5bf3353e6dc78176e2f4f4324f526524e601a92 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 22 Jan 2026 22:24:26 +0100 Subject: [PATCH 06/13] move va_list operations into `InterpCx` --- compiler/rustc_const_eval/src/errors.rs | 16 +- .../rustc_const_eval/src/interpret/call.rs | 100 ++++++------- .../src/interpret/intrinsics.rs | 137 +++++++----------- .../rustc_const_eval/src/interpret/memory.rs | 61 +++++--- .../rustc_const_eval/src/interpret/stack.rs | 76 +++++++--- .../rustc_middle/src/mir/interpret/error.rs | 6 + src/tools/miri/src/alloc_addresses/mod.rs | 8 +- .../tests/fail/c-variadic-mismatch-count.rs | 13 ++ .../fail/c-variadic-mismatch-count.stderr | 13 ++ .../miri/tests/fail/c-variadic-mismatch.rs | 13 ++ .../tests/fail/c-variadic-mismatch.stderr | 13 ++ src/tools/miri/tests/fail/c-variadic.rs | 15 ++ src/tools/miri/tests/fail/c-variadic.stderr | 22 +++ src/tools/miri/tests/pass/c-variadic.rs | 115 +++++++++++++-- tests/ui/consts/const-eval/c-variadic-fail.rs | 38 ++--- .../consts/const-eval/c-variadic-fail.stderr | 125 ++++++++++------ .../variadic-ffi-semantic-restrictions.rs | 4 + .../variadic-ffi-semantic-restrictions.stderr | 83 ++++++++--- 18 files changed, 576 insertions(+), 282 deletions(-) create mode 100644 src/tools/miri/tests/fail/c-variadic-mismatch-count.rs create mode 100644 src/tools/miri/tests/fail/c-variadic-mismatch-count.stderr create mode 100644 src/tools/miri/tests/fail/c-variadic-mismatch.rs create mode 100644 src/tools/miri/tests/fail/c-variadic-mismatch.stderr create mode 100644 src/tools/miri/tests/fail/c-variadic.rs create mode 100644 src/tools/miri/tests/fail/c-variadic.stderr diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 9de7d02184b38..061ed38c9df1d 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -763,6 +763,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { InvalidChar(_) => msg!("interpreting an invalid 32-bit value as a char: 0x{$value}"), InvalidTag(_) => msg!("enum value has invalid tag: {$tag}"), InvalidFunctionPointer(_) => msg!("using {$pointer} as function pointer but it does not point to a function"), + InvalidVaListPointer(_) => msg!("using {$pointer} as variable argument list pointer but it does not point to a variable argument list"), InvalidVTablePointer(_) => msg!("using {$pointer} as vtable pointer but it does not point to a vtable"), InvalidVTableTrait { .. } => msg!("using vtable for `{$vtable_dyn_type}` but `{$expected_dyn_type}` was expected"), InvalidStr(_) => msg!("this string is not valid UTF-8: {$err}"), @@ -778,6 +779,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { AbiMismatchArgument { .. } => msg!("calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty}"), AbiMismatchReturn { .. } => msg!("calling a function with return type {$callee_ty} passing return place of type {$caller_ty}"), VaArgOutOfBounds => "more C-variadic arguments read than were passed".into(), + CVariadicMismatch { ..} => "calling a function where the caller and callee disagree on whether the function is C-variadic".into(), + CVariadicFixedCountMismatch { .. } => msg!("calling a C-variadic function with {$caller} fixed arguments, but the function expects {$callee}"), } } @@ -823,7 +826,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { diag.arg("len", len); diag.arg("index", index); } - UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => { + UnterminatedCString(ptr) + | InvalidFunctionPointer(ptr) + | InvalidVaListPointer(ptr) + | InvalidVTablePointer(ptr) => { diag.arg("pointer", ptr); } InvalidVTableTrait { expected_dyn_type, vtable_dyn_type } => { @@ -914,6 +920,14 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { diag.arg("caller_ty", caller_ty); diag.arg("callee_ty", callee_ty); } + CVariadicMismatch { caller_is_c_variadic, callee_is_c_variadic } => { + diag.arg("caller_is_c_variadic", caller_is_c_variadic); + diag.arg("callee_is_c_variadic", callee_is_c_variadic); + } + CVariadicFixedCountMismatch { caller, callee } => { + diag.arg("caller", caller); + diag.arg("callee", callee); + } } } } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 3efac883b9c4b..dabee7fa19b0d 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use either::{Left, Right}; -use rustc_abi::{self as abi, ExternAbi, FieldIdx, HasDataLayout, Integer, Size, VariantIdx}; +use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx}; use rustc_data_structures::assert_matches; use rustc_errors::msg; use rustc_hir::def_id::DefId; @@ -17,9 +17,9 @@ use tracing::field::Empty; use tracing::{info, instrument, trace}; use super::{ - CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, MemoryKind, OpTy, - PlaceTy, Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo, - interp_ok, throw_ub, throw_ub_custom, + CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, + Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo, interp_ok, + throw_ub, throw_ub_custom, }; use crate::enter_trace_span; use crate::interpret::EnteredTraceSpan; @@ -354,23 +354,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx> { let _trace = enter_trace_span!(M, step::init_stack_frame, %instance, tracing_separate_thread = Empty); - let (fixed_count, c_variadic_args) = if caller_fn_abi.c_variadic { - let sig = self.tcx.fn_sig(instance.def_id()).skip_binder(); - let fixed_count = sig.inputs().skip_binder().len(); - assert!(caller_fn_abi.args.len() >= fixed_count); - let extra_tys: Vec> = - caller_fn_abi.args[fixed_count..].iter().map(|arg_abi| arg_abi.layout.ty).collect(); - - (fixed_count, self.tcx.mk_type_list(&extra_tys)) + // The first order of business is to figure out the callee signature. + // However, that requires the list of variadic arguments. + // We use the *caller* information to determine where to split the list of arguments, + // and then later check that the callee indeed has the same number of fixed arguments. + let extra_tys = if caller_fn_abi.c_variadic { + let fixed_count = usize::try_from(caller_fn_abi.fixed_count).unwrap(); + let extra_tys = args[fixed_count..].iter().map(|arg| arg.layout().ty); + self.tcx.mk_type_list_from_iter(extra_tys) } else { - (caller_fn_abi.args.len(), ty::List::empty()) + ty::List::empty() }; - - let callee_fn_abi = self.fn_abi_of_instance(instance, c_variadic_args)?; - - if callee_fn_abi.c_variadic ^ caller_fn_abi.c_variadic { - unreachable!("caller and callee disagree on being c-variadic"); - } + let callee_fn_abi = self.fn_abi_of_instance(instance, extra_tys)?; if caller_fn_abi.conv != callee_fn_abi.conv { throw_ub_custom!( @@ -382,6 +377,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) } + if caller_fn_abi.c_variadic != callee_fn_abi.c_variadic { + throw_ub!(CVariadicMismatch { + caller_is_c_variadic: caller_fn_abi.c_variadic, + callee_is_c_variadic: callee_fn_abi.c_variadic, + }); + } + if caller_fn_abi.c_variadic && caller_fn_abi.fixed_count != callee_fn_abi.fixed_count { + throw_ub!(CVariadicFixedCountMismatch { + caller: caller_fn_abi.fixed_count, + callee: callee_fn_abi.fixed_count, + }); + } + // Check that all target features required by the callee (i.e., from // the attribute `#[target_feature(enable = ...)]`) are enabled at // compile time. @@ -453,14 +461,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // this is a single iterator (that handles `spread_arg`), then // `pass_argument` would be the loop body. It takes care to // not advance `caller_iter` for ignored arguments. - let mut callee_args_abis = if caller_fn_abi.c_variadic { - callee_fn_abi.args[..fixed_count].iter().enumerate() - } else { - callee_fn_abi.args.iter().enumerate() - }; - - let mut it = body.args_iter().peekable(); - while let Some(local) = it.next() { + let mut callee_args_abis = callee_fn_abi.args.iter().enumerate(); + // Determine whether there is a special VaList argument. This is always the + // last argument, and since arguments start at index 1 that's `arg_count`. + let va_list_arg = + callee_fn_abi.c_variadic.then(|| mir::Local::from_usize(body.arg_count)); + for local in body.args_iter() { // Construct the destination place for this argument. At this point all // locals are still dead, so we cannot construct a `PlaceTy`. let dest = mir::Place::from(local); @@ -468,44 +474,30 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // type, but the result gets cached so this avoids calling the instantiation // query *again* the next time this local is accessed. let ty = self.layout_of_local(self.frame(), local, None)?.ty; - if caller_fn_abi.c_variadic && it.peek().is_none() { - // The callee's signature has an additional VaList argument, that the caller - // won't actually pass. Here we synthesize a `VaList` value, whose leading bytes - // are a pointer that can be mapped to the corresponding variable argument list. + if Some(local) == va_list_arg { + // This is the last callee-side argument of a variadic function. + // This argument is a VaList holding the remaining caller-side arguments. self.storage_live(local)?; let place = self.eval_place(dest)?; let mplace = self.force_allocation(&place)?; - // Consume the remaining arguments and store them in a global allocation. - let mut varargs = Vec::new(); - for (fn_arg, abi) in &mut caller_args { - let op = self.copy_fn_arg(fn_arg); - let mplace = self.allocate(abi.layout, MemoryKind::Stack)?; - self.copy_op(&op, &mplace)?; - - varargs.push(mplace); - } - - // When the frame is dropped, this ID is used to deallocate the variable arguments list. + // Consume the remaining arguments by putting them into the variable argument + // list. + let varargs = self.allocate_varargs(&mut caller_args, &mut callee_args_abis)?; + // When the frame is dropped, these variable arguments are deallocated. self.frame_mut().va_list = varargs.clone(); + let key = self.va_list_ptr(varargs.into()); - // This is a new VaList, so start at index 0. - let ptr = self.va_list_ptr(varargs, 0); - let addr = Scalar::from_pointer(ptr, self); - - // Zero the mplace, so it is fully initialized. + // Zero the VaList, so it is fully initialized. self.write_bytes_ptr( mplace.ptr(), (0..mplace.layout.size.bytes()).map(|_| 0u8), )?; - // Store the pointer to the global variable arguments list allocation in the - // first bytes of the `VaList` value. - let mut alloc = self - .get_ptr_alloc_mut(mplace.ptr(), self.data_layout().pointer_size())? - .expect("not a ZST"); - alloc.write_ptr_sized(Size::ZERO, addr)?; + // Store the "key" pointer in the right field. + let key_mplace = self.va_list_key_field(&mplace)?; + self.write_pointer(key, &key_mplace)?; } else if Some(local) == body.spread_arg { // Make the local live once, then fill in the value field by field. self.storage_live(local)?; @@ -545,7 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if instance.def.requires_caller_location(*self.tcx) { callee_args_abis.next().unwrap(); } - // Now we should have no more caller args or callee arg ABIs + // Now we should have no more caller args or callee arg ABIs. assert!( callee_args_abis.next().is_none(), "mismatch between callee ABI and callee body arguments" diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index aa7c889b3f3f2..e0c75da87a4b6 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -23,8 +23,8 @@ use super::memory::MemoryKind; use super::util::ensure_monomorphic_enough; use super::{ AllocId, CheckInAllocMsg, ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Pointer, - PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format, interp_ok, throw_inval, - throw_ub, throw_ub_custom, throw_ub_format, throw_unsup_format, + PointerArithmetic, Projectable, Provenance, Scalar, err_ub_custom, err_unsup_format, interp_ok, + throw_inval, throw_ub, throw_ub_custom, throw_ub_format, throw_unsup_format, }; use crate::interpret::Writeable; @@ -751,113 +751,54 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } sym::va_copy => { - // fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> - let src_ptr = self.read_pointer(&args[0])?; - - // Read the token pointer from the src VaList (alloc_id + offset-as-index). - let src_va_list_ptr = { - let pointer_size = tcx.data_layout.pointer_size(); - let alloc = self - .get_ptr_alloc(src_ptr, pointer_size)? - .expect("va_list storage should not be a ZST"); - let scalar = alloc.read_pointer(Size::ZERO)?; - scalar.to_pointer(self)? - }; - - let (prov, offset) = src_va_list_ptr.into_raw_parts(); - let src_alloc_id = prov.unwrap().get_alloc_id().unwrap(); - let index = offset.bytes(); + let va_list = self.deref_pointer(&args[0])?; + let key_mplace = self.va_list_key_field(&va_list)?; + let key = self.read_pointer(&key_mplace)?; - // Look up arguments without consuming src. - let Some(arguments) = self.get_va_list_alloc(src_alloc_id) else { - throw_unsup_format!("va_copy on unknown va_list allocation {:?}", src_alloc_id); - }; + let varargs = self.get_ptr_va_list(key)?; + let copy_key = self.va_list_ptr(varargs.clone()); - // Create a new allocation pointing at the same index. - let new_va_list_ptr = self.va_list_ptr(arguments.to_vec(), index); - let addr = Scalar::from_pointer(new_va_list_ptr, self); - - // Now overwrite the token pointer stored inside the VaList. - let mplace = self.force_allocation(dest)?; - let mut alloc = self.get_place_alloc_mut(&mplace)?.unwrap(); - alloc.write_ptr_sized(Size::ZERO, addr)?; + let copy_key_mplace = self.va_list_key_field(dest)?; + self.write_pointer(copy_key, ©_key_mplace)?; } sym::va_end => { - let ptr_size = self.tcx.data_layout.pointer_size(); - - // The only argument is a `&mut VaList`. - let ap_ref = self.read_pointer(&args[0])?; - - // The first bytes of the `VaList` value store a pointer. The `AllocId` of this - // pointer is a key into a global map of variable argument lists. The offset is - // used as the index of the argument to read. - let va_list_ptr = { - let alloc = self - .get_ptr_alloc(ap_ref, ptr_size)? - .expect("va_list storage should not be a ZST"); - let scalar = alloc.read_pointer(Size::ZERO)?; - scalar.to_pointer(self)? - }; + let va_list = self.deref_pointer(&args[0])?; + let key_mplace = self.va_list_key_field(&va_list)?; + let key = self.read_pointer(&key_mplace)?; - let (prov, _offset) = va_list_ptr.into_raw_parts(); - let alloc_id = prov.unwrap().get_alloc_id().unwrap(); - - let Some(_) = self.remove_va_list_alloc(alloc_id) else { - throw_unsup_format!("va_end on unknown va_list allocation {:?}", alloc_id) - }; + self.deallocate_va_list(key)?; } sym::va_arg => { - let ptr_size = self.tcx.data_layout.pointer_size(); - - // The only argument is a `&mut VaList`. - let ap_ref = self.read_pointer(&args[0])?; - - // The first bytes of the `VaList` value store a pointer. The `AllocId` of this - // pointer is a key into a global map of variable argument lists. The offset is - // used as the index of the argument to read. - let va_list_ptr = { - let alloc = self - .get_ptr_alloc(ap_ref, ptr_size)? - .expect("va_list storage should not be a ZST"); - let scalar = alloc.read_pointer(Size::ZERO)?; - scalar.to_pointer(self)? - }; + let va_list = self.deref_pointer(&args[0])?; + let key_mplace = self.va_list_key_field(&va_list)?; + let key = self.read_pointer(&key_mplace)?; - let (prov, offset) = va_list_ptr.into_raw_parts(); - let alloc_id = prov.unwrap().get_alloc_id().unwrap(); - let index = offset.bytes(); - - let Some(varargs) = self.remove_va_list_alloc(alloc_id) else { - throw_unsup_format!("va_arg on unknown va_list allocation {:?}", alloc_id) - }; + // Invalidate the old list and get its content. We'll recreate the + // new list (one element shorter) below. + let mut varargs = self.deallocate_va_list(key)?; - let Some(src_mplace) = varargs.get(offset.bytes_usize()).cloned() else { - throw_ub!(VaArgOutOfBounds) + let Some(arg_mplace) = varargs.pop_front() else { + throw_ub!(VaArgOutOfBounds); }; - // Update the offset in this `VaList` value so that a subsequent call to `va_arg` - // reads the next argument. - let new_va_list_ptr = self.va_list_ptr(varargs, index + 1); - let addr = Scalar::from_pointer(new_va_list_ptr, self); - let mut alloc = self - .get_ptr_alloc_mut(ap_ref, ptr_size)? - .expect("va_list storage should not be a ZST"); - alloc.write_ptr_sized(Size::ZERO, addr)?; - // NOTE: In C some type conversions are allowed (e.g. casting between signed and // unsigned integers). For now we require c-variadic arguments to be read with the // exact type they were passed as. - if src_mplace.layout.ty != dest.layout.ty { + if arg_mplace.layout.ty != dest.layout.ty { throw_unsup_format!( "va_arg type mismatch: requested `{}`, but next argument is `{}`", dest.layout.ty, - src_mplace.layout.ty + arg_mplace.layout.ty ); } + // Copy the argument. + self.copy_op(&arg_mplace, dest)?; - self.copy_op(&src_mplace, dest)?; + // Update the VaList pointer. + let new_key = self.va_list_ptr(varargs); + self.write_pointer(new_key, &key_mplace)?; } // Unsupported intrinsic: skip the return_to_block below. @@ -1340,4 +1281,26 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(Some(ImmTy::from_scalar(val, cast_to))) } } + + /// Get the MPlace of the key from the place storing the VaList. + pub(super) fn va_list_key_field>( + &self, + va_list: &P, + ) -> InterpResult<'tcx, P> { + // The struct wrapped by VaList. + let va_list_inner = self.project_field(va_list, FieldIdx::ZERO)?; + + // Find the first pointer field in this struct. The exact index is target-specific. + let ty::Adt(adt, substs) = va_list_inner.layout().ty.kind() else { + bug!("invalid VaListImpl layout"); + }; + + for (i, field) in adt.non_enum_variant().fields.iter().enumerate() { + if field.ty(*self.tcx, substs).is_raw_ptr() { + return self.project_field(&va_list_inner, FieldIdx::from_usize(i)); + } + } + + bug!("no VaListImpl field is a pointer"); + } } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index aadbe84ca8093..7b67d3acd555e 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -129,7 +129,7 @@ pub struct Memory<'tcx, M: Machine<'tcx>> { extra_fn_ptr_map: FxIndexMap, /// Map storing variable argument lists. - va_list_map: FxIndexMap>>, + va_list_map: FxIndexMap>>, /// To be able to compare pointers with null, and to check alignment for accesses /// to ZSTs (where pointers may dangle), we keep track of the size even for allocations @@ -237,19 +237,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.global_root_pointer(Pointer::from(id)).unwrap() } + /// Insert a new variable argument list in the global map of variable argument lists. pub fn va_list_ptr( &mut self, - varargs: Vec>, - index: u64, + varargs: VecDeque>, ) -> Pointer { let id = self.tcx.reserve_alloc_id(); let old = self.memory.va_list_map.insert(id, varargs); assert!(old.is_none()); - // The offset is used to store the current index. - let ptr = Pointer::new(id.into(), Size::from_bytes(index)); // Variable argument lists are global allocations, so make sure we get the right root // pointer. We know this is not an `extern static` so this cannot fail. - self.global_root_pointer(ptr).unwrap() + self.global_root_pointer(Pointer::from(id)).unwrap() } pub fn allocate_ptr( @@ -1020,7 +1018,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // # Variable argument lists - if let Some(_) = self.get_va_list_alloc(id) { + if self.memory.va_list_map.contains_key(&id) { return AllocInfo::new(Size::ZERO, Align::ONE, AllocKind::VaList, Mutability::Not); } @@ -1071,18 +1069,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - pub fn get_va_list_alloc(&self, id: AllocId) -> Option<&[MPlaceTy<'tcx, M::Provenance>]> { - self.memory.va_list_map.get(&id).map(|v| &**v) - } - - pub fn remove_va_list_alloc( - &mut self, - id: AllocId, - ) -> Option>> { - self.memory.dead_alloc_map.insert(id, (Size::ZERO, Align::ONE)); - self.memory.va_list_map.swap_remove(&id) - } - /// Takes a pointer that is the first chunk of a `TypeId` and return the type that its /// provenance refers to, as well as the segment of the hash that this pointer covers. pub fn get_ptr_type_id( @@ -1110,6 +1096,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .into() } + pub fn get_ptr_va_list( + &self, + ptr: Pointer>, + ) -> InterpResult<'tcx, &VecDeque>> { + trace!("get_ptr_va_list({:?})", ptr); + let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?; + if offset.bytes() != 0 { + throw_ub!(InvalidVaListPointer(Pointer::new(alloc_id, offset))) + } + + let Some(va_list) = self.memory.va_list_map.get(&alloc_id) else { + throw_ub!(InvalidVaListPointer(Pointer::new(alloc_id, offset))) + }; + + interp_ok(va_list) + } + + /// Removes this VaList from the global map of variable argument lists. This does not deallocate + /// the VaList elements, that happens when the Frame is popped. + pub fn deallocate_va_list( + &mut self, + ptr: Pointer>, + ) -> InterpResult<'tcx, VecDeque>> { + trace!("deallocate_va_list({:?})", ptr); + let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?; + if offset.bytes() != 0 { + throw_ub!(InvalidVaListPointer(Pointer::new(alloc_id, offset))) + } + + let Some(va_list) = self.memory.va_list_map.swap_remove(&alloc_id) else { + throw_ub!(InvalidVaListPointer(Pointer::new(alloc_id, offset))) + }; + + self.memory.dead_alloc_map.insert(alloc_id, (Size::ZERO, Align::ONE)); + interp_ok(va_list) + } + /// Get the dynamic type of the given vtable pointer. /// If `expected_trait` is `Some`, it must be a vtable for the given trait. pub fn get_ptr_vtable_ty( diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 9b2d99bb297f4..1b8af1c44277c 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -12,11 +12,12 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_mir_dataflow::impls::always_storage_live_locals; use rustc_span::Span; +use rustc_target::callconv::ArgAbi; use tracing::field::Empty; use tracing::{info_span, instrument, trace}; use super::{ - AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, + AllocId, CtfeProvenance, FnArg, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout, interp_ok, throw_ub, throw_unsup, }; @@ -455,15 +456,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; let return_action = if cleanup { - // We need to take the locals out, since we need to mutate while iterating. for local in &frame.locals { self.deallocate_local(local.value)?; } // Deallocate any c-variadic arguments. - for mplace in &frame.va_list { - self.deallocate_vararg(mplace)?; - } + self.deallocate_varargs(&frame.va_list)?; // Call the machine hook, which determines the next steps. let return_action = M::after_stack_pop(self, frame, unwinding)?; @@ -610,22 +608,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(()) } - fn deallocate_vararg(&mut self, vararg: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { - let ptr = vararg.ptr(); - - // FIXME: is the `unwrap` valid here? - trace!( - "deallocating vararg {:?}: {:?}", - vararg, - // FIXME: what do we do with this comment? - // Locals always have a `alloc_id` (they are never the result of a int2ptr). - self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap()) - ); - self.deallocate_ptr(ptr, None, MemoryKind::Stack)?; - - interp_ok(()) - } - /// This is public because it is used by [Aquascope](https://github.com/cognitive-engineering-lab/aquascope/) /// to analyze all the locals in a stack frame. #[inline(always)] @@ -653,6 +635,58 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } +impl<'a, 'tcx: 'a, M: Machine<'tcx>> InterpCx<'tcx, M> { + /// Consume the arguments provided by the iterator and store them as a list + /// of variadic arguments. Return a list of the places that hold those arguments. + pub(crate) fn allocate_varargs( + &mut self, + caller_args: &mut I, + callee_abis: &mut J, + ) -> InterpResult<'tcx, Vec>> + where + I: Iterator, &'a ArgAbi<'tcx, Ty<'tcx>>)>, + J: Iterator>)>, + { + // Consume the remaining arguments and store them in fresh allocations. + let mut varargs = Vec::new(); + for (fn_arg, caller_abi) in caller_args { + // The callee ABI is entirely computed based on which arguments the caller has + // provided so it should not be possible to get a mismatch here. + let (_idx, callee_abi) = callee_abis.next().unwrap(); + assert!(self.check_argument_compat(caller_abi, callee_abi)?); + // FIXME: do we have to worry about in-place argument passing? + let op = self.copy_fn_arg(fn_arg); + let mplace = self.allocate(op.layout, MemoryKind::Stack)?; + self.copy_op(&op, &mplace)?; + + varargs.push(mplace); + } + assert!(callee_abis.next().is_none()); + + interp_ok(varargs) + } + + /// Deallocate the variadic arguments in the list (that must have been created with `allocate_varargs`). + fn deallocate_varargs( + &mut self, + varargs: &[MPlaceTy<'tcx, M::Provenance>], + ) -> InterpResult<'tcx> { + for vararg in varargs { + let ptr = vararg.ptr(); + + trace!( + "deallocating vararg {:?}: {:?}", + vararg, + // Locals always have a `alloc_id` (they are never the result of a int2ptr). + self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap()) + ); + self.deallocate_ptr(ptr, None, MemoryKind::Stack)?; + } + + interp_ok(()) + } +} + impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { pub(super) fn print( &self, diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 970dbd95f7cc3..035ffd362a6bb 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -404,6 +404,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { InvalidTag(Scalar), /// Using a pointer-not-to-a-function as function pointer. InvalidFunctionPointer(Pointer), + /// Using a pointer-not-to-a-va-list as variable argument list pointer. + InvalidVaListPointer(Pointer), /// Using a pointer-not-to-a-vtable as vtable pointer. InvalidVTablePointer(Pointer), /// Using a vtable for the wrong trait. @@ -438,6 +440,10 @@ pub enum UndefinedBehaviorInfo<'tcx> { AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> }, /// `va_arg` was called on an exhausted `VaList`. VaArgOutOfBounds, + /// The caller and callee disagree on whether they are c-variadic or not. + CVariadicMismatch { caller_is_c_variadic: bool, callee_is_c_variadic: bool }, + /// The caller and callee disagree on the number of fixed (i.e. non-c-variadic) arguments. + CVariadicFixedCountMismatch { caller: u32, callee: u32 }, } #[derive(Debug, Clone, Copy)] diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index 59799ac613b81..32897eb89a83c 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -184,8 +184,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } #[cfg(not(all(unix, feature = "native-lib")))] AllocKind::Function => dummy_alloc(params), - AllocKind::VTable => dummy_alloc(params), - AllocKind::TypeId | AllocKind::Dead | AllocKind::VaList => unreachable!(), + AllocKind::VTable | AllocKind::VaList => dummy_alloc(params), + AllocKind::TypeId | AllocKind::Dead => unreachable!(), }; // We don't have to expose this pointer yet, we do that in `prepare_for_native_call`. return interp_ok(base_ptr.addr().to_u64()); @@ -363,8 +363,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ProvenanceMode::Default => { // The first time this happens at a particular location, print a warning. static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new(); - this.dedup_diagnostic(&DEDUP, |first| NonHaltingDiagnostic::Int2Ptr { - details: first, + this.dedup_diagnostic(&DEDUP, |first| { + NonHaltingDiagnostic::Int2Ptr { details: first } }); } ProvenanceMode::Strict => { diff --git a/src/tools/miri/tests/fail/c-variadic-mismatch-count.rs b/src/tools/miri/tests/fail/c-variadic-mismatch-count.rs new file mode 100644 index 0000000000000..c01860cd1df90 --- /dev/null +++ b/src/tools/miri/tests/fail/c-variadic-mismatch-count.rs @@ -0,0 +1,13 @@ +#![feature(c_variadic)] + +unsafe extern "C" fn helper(_: i32, _: ...) {} + +fn main() { + unsafe { + let f = helper as *const (); + let f = std::mem::transmute::<_, unsafe extern "C" fn(...)>(f); + + f(1); + //~^ ERROR: Undefined Behavior + } +} diff --git a/src/tools/miri/tests/fail/c-variadic-mismatch-count.stderr b/src/tools/miri/tests/fail/c-variadic-mismatch-count.stderr new file mode 100644 index 0000000000000..9350b99861964 --- /dev/null +++ b/src/tools/miri/tests/fail/c-variadic-mismatch-count.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: calling a C-variadic function with 0 fixed arguments, but the function expects 1 + --> tests/fail/c-variadic-mismatch-count.rs:LL:CC + | +LL | f(1); + | ^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/c-variadic-mismatch.rs b/src/tools/miri/tests/fail/c-variadic-mismatch.rs new file mode 100644 index 0000000000000..bf44fb00bceec --- /dev/null +++ b/src/tools/miri/tests/fail/c-variadic-mismatch.rs @@ -0,0 +1,13 @@ +#![feature(c_variadic)] + +unsafe extern "C" fn helper(_: i32, _: ...) {} + +fn main() { + unsafe { + let f = helper as *const (); + let f = std::mem::transmute::<_, unsafe extern "C" fn(_: i32, _: i64)>(f); + + f(1i32, 1i64); + //~^ ERROR: Undefined Behavior: calling a function where the caller and callee disagree on whether the function is C-variadic + } +} diff --git a/src/tools/miri/tests/fail/c-variadic-mismatch.stderr b/src/tools/miri/tests/fail/c-variadic-mismatch.stderr new file mode 100644 index 0000000000000..a68b96f738d22 --- /dev/null +++ b/src/tools/miri/tests/fail/c-variadic-mismatch.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: calling a function where the caller and callee disagree on whether the function is C-variadic + --> tests/fail/c-variadic-mismatch.rs:LL:CC + | +LL | f(1i32, 1i64); + | ^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/c-variadic.rs b/src/tools/miri/tests/fail/c-variadic.rs new file mode 100644 index 0000000000000..38141c23d08de --- /dev/null +++ b/src/tools/miri/tests/fail/c-variadic.rs @@ -0,0 +1,15 @@ +#![feature(c_variadic)] + +//@error-in-other-file: Undefined Behavior: more C-variadic arguments read than were passed + +fn read_too_many() { + unsafe extern "C" fn variadic(mut ap: ...) { + ap.arg::(); + } + + unsafe { variadic() }; +} + +fn main() { + read_too_many(); +} diff --git a/src/tools/miri/tests/fail/c-variadic.stderr b/src/tools/miri/tests/fail/c-variadic.stderr new file mode 100644 index 0000000000000..31695f1cab49b --- /dev/null +++ b/src/tools/miri/tests/fail/c-variadic.stderr @@ -0,0 +1,22 @@ +error: Undefined Behavior: more C-variadic arguments read than were passed + --> RUSTLIB/core/src/ffi/va_list.rs:LL:CC + | +LL | unsafe { va_arg(self) } + | ^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: stack backtrace: + 0: std::ffi::VaList::<'_>::arg + at RUSTLIB/core/src/ffi/va_list.rs:LL:CC + 1: read_too_many::variadic + at tests/fail/c-variadic.rs:LL:CC + 2: read_too_many + at tests/fail/c-variadic.rs:LL:CC + 3: main + at tests/fail/c-variadic.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass/c-variadic.rs b/src/tools/miri/tests/pass/c-variadic.rs index a1f03db6872fa..8b353885699e6 100644 --- a/src/tools/miri/tests/pass/c-variadic.rs +++ b/src/tools/miri/tests/pass/c-variadic.rs @@ -1,18 +1,115 @@ #![feature(c_variadic)] -use core::ffi::VaList; +use std::ffi::{CStr, VaList, c_char, c_double, c_int, c_long}; -fn helper(ap: VaList) -> i32 { - // unsafe { ap.arg::() } - let _ = ap; - 0 +fn ignores_arguments() { + unsafe extern "C" fn variadic(_: ...) {} + + unsafe { variadic() }; + unsafe { variadic(1, 2, 3) }; +} + +fn echo() { + unsafe extern "C" fn variadic(mut ap: ...) -> i32 { + ap.arg() + } + + assert_eq!(unsafe { variadic(1) }, 1); + assert_eq!(unsafe { variadic(3, 2, 1) }, 3); +} + +fn forward_by_val() { + unsafe fn helper(mut ap: VaList) -> i32 { + ap.arg() + } + + unsafe extern "C" fn variadic(ap: ...) -> i32 { + helper(ap) + } + + assert_eq!(unsafe { variadic(1) }, 1); + assert_eq!(unsafe { variadic(3, 2, 1) }, 3); +} + +fn forward_by_ref() { + unsafe fn helper(ap: &mut VaList) -> i32 { + ap.arg() + } + + unsafe extern "C" fn variadic(mut ap: ...) -> i32 { + helper(&mut ap) + } + + assert_eq!(unsafe { variadic(1) }, 1); + assert_eq!(unsafe { variadic(3, 2, 1) }, 3); } -unsafe extern "C" fn variadic(a: i32, ap: ...) -> i32 { - assert_eq!(a, 42); - helper(ap) +#[allow(improper_ctypes_definitions)] +fn nested() { + unsafe fn helper(mut ap1: VaList, mut ap2: VaList) -> (i32, i32) { + (ap1.arg(), ap2.arg()) + } + + unsafe extern "C" fn variadic2(ap1: VaList, ap2: ...) -> (i32, i32) { + helper(ap1, ap2) + } + + unsafe extern "C" fn variadic1(ap1: ...) -> (i32, i32) { + variadic2(ap1, 2, 2) + } + + assert_eq!(unsafe { variadic1(1) }, (1, 2)); + + let (a, b) = unsafe { variadic1(1, 1) }; + + assert_eq!(a, 1); + assert_eq!(b, 2); +} + +fn various_types() { + unsafe extern "C" fn check_list_2(mut ap: ...) { + macro_rules! continue_if { + ($cond:expr) => { + if !($cond) { + panic!(); + } + }; + } + + unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool { + match CStr::from_ptr(ptr).to_str() { + Ok(cstr) => cstr == val, + Err(_) => panic!(), + } + } + + continue_if!(ap.arg::().floor() == 3.14f64.floor()); + continue_if!(ap.arg::() == 12); + continue_if!(ap.arg::() == 'a' as c_int); + continue_if!(ap.arg::().floor() == 6.18f64.floor()); + continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Hello")); + continue_if!(ap.arg::() == 42); + continue_if!(compare_c_str(ap.arg::<*const c_char>(), "World")); + } + + unsafe { + check_list_2( + 3.14 as c_double, + 12 as c_long, + b'a' as c_int, + 6.28 as c_double, + c"Hello".as_ptr(), + 42 as c_int, + c"World".as_ptr(), + ); + } } fn main() { - assert_eq!(unsafe { variadic(42, 1) }, 1); + ignores_arguments(); + echo(); + forward_by_val(); + forward_by_ref(); + nested(); + various_types(); } diff --git a/tests/ui/consts/const-eval/c-variadic-fail.rs b/tests/ui/consts/const-eval/c-variadic-fail.rs index 37810ddb356df..859dfed35719f 100644 --- a/tests/ui/consts/const-eval/c-variadic-fail.rs +++ b/tests/ui/consts/const-eval/c-variadic-fail.rs @@ -7,6 +7,7 @@ #![feature(const_clone)] use std::ffi::VaList; +use std::mem::MaybeUninit; const unsafe extern "C" fn read_n(mut ap: ...) { let mut i = N; @@ -77,23 +78,13 @@ fn use_after_free() { }; } -macro_rules! va_list_copy { - ($ap:expr) => {{ +fn manual_copy_drop() { + const unsafe extern "C" fn helper(ap: ...) { // A copy created using Clone is valid, and can be used to read arguments. - let mut copy = $ap.clone(); + let mut copy = ap.clone(); assert!(copy.arg::() == 1i32); - let mut u = core::mem::MaybeUninit::uninit(); - unsafe { core::ptr::copy_nonoverlapping(&$ap, u.as_mut_ptr(), 1) }; - - // Manually creating the copy is fine. - unsafe { u.assume_init() } - }}; -} - -fn manual_copy_drop() { - const unsafe extern "C" fn helper(ap: ...) { - let mut copy: VaList = va_list_copy!(ap); + let mut copy: VaList = unsafe { std::mem::transmute_copy(&ap) }; // Using the copy is actually fine. let _ = copy.arg::(); @@ -104,12 +95,12 @@ fn manual_copy_drop() { } const { unsafe { helper(1, 2, 3) } }; - //~^ ERROR va_end on unknown va_list allocation ALLOC0 [E0080] + //~^ ERROR using ALLOC0 as variable argument list pointer but it does not point to a variable argument list [E0080] } fn manual_copy_forget() { const unsafe extern "C" fn helper(ap: ...) { - let mut copy: VaList = va_list_copy!(ap); + let mut copy: VaList = unsafe { std::mem::transmute_copy(&ap) }; // Using the copy is actually fine. let _ = copy.arg::(); @@ -120,12 +111,12 @@ fn manual_copy_forget() { } const { unsafe { helper(1, 2, 3) } }; - //~^ ERROR va_end on unknown va_list allocation ALLOC0 [E0080] + //~^ ERROR using ALLOC0 as variable argument list pointer but it does not point to a variable argument list [E0080] } fn manual_copy_read() { const unsafe extern "C" fn helper(mut ap: ...) { - let mut copy: VaList = va_list_copy!(ap); + let mut copy: VaList = unsafe { std::mem::transmute_copy(&ap) }; // Reading from `ap` after reading from `copy` is UB. let _ = copy.arg::(); @@ -133,7 +124,15 @@ fn manual_copy_read() { } const { unsafe { helper(1, 2, 3) } }; - //~^ ERROR va_arg on unknown va_list allocation ALLOC0 + //~^ ERROR using ALLOC0 as variable argument list pointer but it does not point to a variable argument list [E0080] +} + +fn drop_of_invalid() { + const { + let mut invalid: MaybeUninit = MaybeUninit::zeroed(); + let ap = unsafe { invalid.assume_init() }; + } + //~^ ERROR pointer not dereferenceable: pointer must point to some allocation, but got null pointer [E0080] } fn main() { @@ -143,5 +142,6 @@ fn main() { manual_copy_read(); manual_copy_drop(); manual_copy_forget(); + drop_of_invalid(); } } diff --git a/tests/ui/consts/const-eval/c-variadic-fail.stderr b/tests/ui/consts/const-eval/c-variadic-fail.stderr index 4c569a4492d47..14da5500cb1b6 100644 --- a/tests/ui/consts/const-eval/c-variadic-fail.stderr +++ b/tests/ui/consts/const-eval/c-variadic-fail.stderr @@ -1,11 +1,11 @@ error[E0080]: more C-variadic arguments read than were passed - --> $DIR/c-variadic-fail.rs:27:13 + --> $DIR/c-variadic-fail.rs:28:13 | LL | const { read_n::<1>() } | ^^^^^^^^^^^^^ evaluation of `read_too_many::{constant#2}` failed inside this call | note: inside `read_n::<1>` - --> $DIR/c-variadic-fail.rs:15:17 + --> $DIR/c-variadic-fail.rs:16:17 | LL | let _ = ap.arg::(); | ^^^^^^^^^^^^^^^ @@ -13,13 +13,13 @@ note: inside `VaList::<'_>::arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:27:5 + --> $DIR/c-variadic-fail.rs:28:5 | LL | const { read_n::<1>() } | ^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:27:5 + --> $DIR/c-variadic-fail.rs:28:5 | LL | const { read_n::<1>() } | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -27,13 +27,13 @@ LL | const { read_n::<1>() } = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: more C-variadic arguments read than were passed - --> $DIR/c-variadic-fail.rs:31:13 + --> $DIR/c-variadic-fail.rs:32:13 | LL | const { read_n::<2>(1) } | ^^^^^^^^^^^^^^ evaluation of `read_too_many::{constant#3}` failed inside this call | note: inside `read_n::<2>` - --> $DIR/c-variadic-fail.rs:15:17 + --> $DIR/c-variadic-fail.rs:16:17 | LL | let _ = ap.arg::(); | ^^^^^^^^^^^^^^^ @@ -41,13 +41,13 @@ note: inside `VaList::<'_>::arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:31:5 + --> $DIR/c-variadic-fail.rs:32:5 | LL | const { read_n::<2>(1) } | ^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:31:5 + --> $DIR/c-variadic-fail.rs:32:5 | LL | const { read_n::<2>(1) } | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -55,13 +55,13 @@ LL | const { read_n::<2>(1) } = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: va_arg type mismatch: requested `u32`, but next argument is `i32` - --> $DIR/c-variadic-fail.rs:49:13 + --> $DIR/c-variadic-fail.rs:50:13 | LL | const { read_as::(1i32) }; | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#6}` failed inside this call | note: inside `read_as::` - --> $DIR/c-variadic-fail.rs:36:5 + --> $DIR/c-variadic-fail.rs:37:5 | LL | ap.arg::() | ^^^^^^^^^^^^^ @@ -69,13 +69,13 @@ note: inside `VaList::<'_>::arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:49:5 + --> $DIR/c-variadic-fail.rs:50:5 | LL | const { read_as::(1i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:49:5 + --> $DIR/c-variadic-fail.rs:50:5 | LL | const { read_as::(1i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,13 +83,13 @@ LL | const { read_as::(1i32) }; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: va_arg type mismatch: requested `i32`, but next argument is `u32` - --> $DIR/c-variadic-fail.rs:52:13 + --> $DIR/c-variadic-fail.rs:53:13 | LL | const { read_as::(1u32) }; | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#7}` failed inside this call | note: inside `read_as::` - --> $DIR/c-variadic-fail.rs:36:5 + --> $DIR/c-variadic-fail.rs:37:5 | LL | ap.arg::() | ^^^^^^^^^^^^^ @@ -97,13 +97,13 @@ note: inside `VaList::<'_>::arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:52:5 + --> $DIR/c-variadic-fail.rs:53:5 | LL | const { read_as::(1u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:52:5 + --> $DIR/c-variadic-fail.rs:53:5 | LL | const { read_as::(1u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -111,13 +111,13 @@ LL | const { read_as::(1u32) }; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: va_arg type mismatch: requested `i32`, but next argument is `u64` - --> $DIR/c-variadic-fail.rs:55:13 + --> $DIR/c-variadic-fail.rs:56:13 | LL | const { read_as::(1u64) }; | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#8}` failed inside this call | note: inside `read_as::` - --> $DIR/c-variadic-fail.rs:36:5 + --> $DIR/c-variadic-fail.rs:37:5 | LL | ap.arg::() | ^^^^^^^^^^^^^ @@ -125,13 +125,13 @@ note: inside `VaList::<'_>::arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:55:5 + --> $DIR/c-variadic-fail.rs:56:5 | LL | const { read_as::(1u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:55:5 + --> $DIR/c-variadic-fail.rs:56:5 | LL | const { read_as::(1u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -139,13 +139,13 @@ LL | const { read_as::(1u64) }; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: va_arg type mismatch: requested `f64`, but next argument is `i32` - --> $DIR/c-variadic-fail.rs:58:13 + --> $DIR/c-variadic-fail.rs:59:13 | LL | const { read_as::(1i32) }; | ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#9}` failed inside this call | note: inside `read_as::` - --> $DIR/c-variadic-fail.rs:36:5 + --> $DIR/c-variadic-fail.rs:37:5 | LL | ap.arg::() | ^^^^^^^^^^^^^ @@ -153,13 +153,13 @@ note: inside `VaList::<'_>::arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:58:5 + --> $DIR/c-variadic-fail.rs:59:5 | LL | const { read_as::(1i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:58:5 + --> $DIR/c-variadic-fail.rs:59:5 | LL | const { read_as::(1i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -167,13 +167,13 @@ LL | const { read_as::(1i32) }; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: va_arg type mismatch: requested `*const u8`, but next argument is `i32` - --> $DIR/c-variadic-fail.rs:61:13 + --> $DIR/c-variadic-fail.rs:62:13 | LL | const { read_as::<*const u8>(1i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#10}` failed inside this call | note: inside `read_as::<*const u8>` - --> $DIR/c-variadic-fail.rs:36:5 + --> $DIR/c-variadic-fail.rs:37:5 | LL | ap.arg::() | ^^^^^^^^^^^^^ @@ -181,13 +181,13 @@ note: inside `VaList::<'_>::arg::<*const u8>` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:61:5 + --> $DIR/c-variadic-fail.rs:62:5 | LL | const { read_as::<*const u8>(1i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:61:5 + --> $DIR/c-variadic-fail.rs:62:5 | LL | const { read_as::<*const u8>(1i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -195,7 +195,7 @@ LL | const { read_as::<*const u8>(1i32) }; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0080]: memory access failed: ALLOC0 has been freed, so this pointer is dangling - --> $DIR/c-variadic-fail.rs:74:13 + --> $DIR/c-variadic-fail.rs:75:13 | LL | ap.arg::(); | ^^^^^^^^^^^^^^^ evaluation of `use_after_free::{constant#0}` failed inside this call @@ -204,7 +204,7 @@ note: inside `VaList::<'_>::arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:70:5 + --> $DIR/c-variadic-fail.rs:71:5 | LL | / const { LL | | unsafe { @@ -215,7 +215,7 @@ LL | | }; | |_____^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:70:5 + --> $DIR/c-variadic-fail.rs:71:5 | LL | / const { LL | | unsafe { @@ -227,14 +227,14 @@ LL | | }; | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0080]: va_end on unknown va_list allocation ALLOC1 - --> $DIR/c-variadic-fail.rs:106:22 +error[E0080]: using ALLOC1 as variable argument list pointer but it does not point to a variable argument list + --> $DIR/c-variadic-fail.rs:97:22 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^ evaluation of `manual_copy_drop::{constant#0}` failed inside this call | note: inside `manual_copy_drop::helper` - --> $DIR/c-variadic-fail.rs:103:9 + --> $DIR/c-variadic-fail.rs:94:9 | LL | drop(ap); | ^^^^^^^^ @@ -246,27 +246,27 @@ note: inside ` as Drop>::drop` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:106:5 + --> $DIR/c-variadic-fail.rs:97:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:106:5 + --> $DIR/c-variadic-fail.rs:97:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0080]: va_end on unknown va_list allocation ALLOC2 - --> $DIR/c-variadic-fail.rs:122:22 +error[E0080]: using ALLOC2 as variable argument list pointer but it does not point to a variable argument list + --> $DIR/c-variadic-fail.rs:113:22 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^ evaluation of `manual_copy_forget::{constant#0}` failed inside this call | note: inside `manual_copy_forget::helper` - --> $DIR/c-variadic-fail.rs:119:9 + --> $DIR/c-variadic-fail.rs:110:9 | LL | drop(ap); | ^^^^^^^^ @@ -278,27 +278,27 @@ note: inside ` as Drop>::drop` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:122:5 + --> $DIR/c-variadic-fail.rs:113:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:122:5 + --> $DIR/c-variadic-fail.rs:113:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0080]: va_arg on unknown va_list allocation ALLOC3 - --> $DIR/c-variadic-fail.rs:135:22 +error[E0080]: using ALLOC3 as variable argument list pointer but it does not point to a variable argument list + --> $DIR/c-variadic-fail.rs:126:22 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^ evaluation of `manual_copy_read::{constant#0}` failed inside this call | note: inside `manual_copy_read::helper` - --> $DIR/c-variadic-fail.rs:132:17 + --> $DIR/c-variadic-fail.rs:123:17 | LL | let _ = ap.arg::(); | ^^^^^^^^^^^^^^^ @@ -306,19 +306,50 @@ note: inside `VaList::<'_>::arg::` --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:135:5 + --> $DIR/c-variadic-fail.rs:126:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/c-variadic-fail.rs:135:5 + --> $DIR/c-variadic-fail.rs:126:5 | LL | const { unsafe { helper(1, 2, 3) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 11 previous errors +error[E0080]: pointer not dereferenceable: pointer must point to some allocation, but got null pointer + --> $DIR/c-variadic-fail.rs:134:5 + | +LL | } + | ^ evaluation of `drop_of_invalid::{constant#0}` failed inside this call + | +note: inside `drop_in_place::> - shim(Some(VaList<'_>))` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL +note: inside ` as Drop>::drop` + --> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:131:5 + | +LL | / const { +LL | | let mut invalid: MaybeUninit = MaybeUninit::zeroed(); +LL | | let ap = unsafe { invalid.assume_init() }; +LL | | } + | |_____^ + +note: erroneous constant encountered + --> $DIR/c-variadic-fail.rs:131:5 + | +LL | / const { +LL | | let mut invalid: MaybeUninit = MaybeUninit::zeroed(); +LL | | let ap = unsafe { invalid.assume_init() }; +LL | | } + | |_____^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 41d9e73b69e8d..4e038875d78f8 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -32,14 +32,17 @@ extern "C" fn f3_3(_: ..., x: isize) {} const unsafe extern "C" fn f4_1(x: isize, _: ...) {} //~^ ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time +//~| ERROR c-variadic const function definitions are unstable const extern "C" fn f4_2(x: isize, _: ...) {} //~^ ERROR functions with a C variable argument list must be unsafe //~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time +//~| ERROR c-variadic const function definitions are unstable const extern "C" fn f4_3(_: ..., x: isize, _: ...) {} //~^ ERROR functions with a C variable argument list must be unsafe //~| ERROR `...` must be the last argument of a C-variadic function +//~| ERROR c-variadic const function definitions are unstable extern "C" { fn e_f2(..., x: isize); @@ -62,6 +65,7 @@ impl X { const fn i_f5(x: isize, _: ...) {} //~^ ERROR `...` is not supported for non-extern functions //~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time + //~| ERROR c-variadic const function definitions are unstable } trait T { diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 26d5cdaf995aa..ea9f9baa58ba2 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -80,8 +80,28 @@ error: `...` must be the last argument of a C-variadic function LL | extern "C" fn f3_3(_: ..., x: isize) {} | ^^^^^^ +error[E0658]: c-variadic const function definitions are unstable + --> $DIR/variadic-ffi-semantic-restrictions.rs:33:1 + | +LL | const unsafe extern "C" fn f4_1(x: isize, _: ...) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151787 for more information + = help: add `#![feature(const_c_variadic)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: c-variadic const function definitions are unstable + --> $DIR/variadic-ffi-semantic-restrictions.rs:37:1 + | +LL | const extern "C" fn f4_2(x: isize, _: ...) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151787 for more information + = help: add `#![feature(const_c_variadic)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:37:36 | LL | const extern "C" fn f4_2(x: isize, _: ...) {} | ^^^^^^ @@ -92,13 +112,23 @@ LL | const unsafe extern "C" fn f4_2(x: isize, _: ...) {} | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:40:26 + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:26 | LL | const extern "C" fn f4_3(_: ..., x: isize, _: ...) {} | ^^^^^^ +error[E0658]: c-variadic const function definitions are unstable + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:1 + | +LL | const extern "C" fn f4_3(_: ..., x: isize, _: ...) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151787 for more information + = help: add `#![feature(const_c_variadic)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:40:44 + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:44 | LL | const extern "C" fn f4_3(_: ..., x: isize, _: ...) {} | ^^^^^^ @@ -109,13 +139,13 @@ LL | const unsafe extern "C" fn f4_3(_: ..., x: isize, _: ...) {} | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:45:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:48:13 | LL | fn e_f2(..., x: isize); | ^^^ error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:52:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:55:23 | LL | fn i_f1(x: isize, _: ...) {} | ^^^^^^ @@ -123,7 +153,7 @@ LL | fn i_f1(x: isize, _: ...) {} = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:57:13 | LL | fn i_f2(_: ...) {} | ^^^^^^ @@ -131,13 +161,13 @@ LL | fn i_f2(_: ...) {} = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:56:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 | LL | fn i_f3(_: ..., x: isize, _: ...) {} | ^^^^^^ error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:56:31 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:31 | LL | fn i_f3(_: ..., x: isize, _: ...) {} | ^^^^^^ @@ -145,21 +175,31 @@ LL | fn i_f3(_: ..., x: isize, _: ...) {} = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:13 | LL | fn i_f4(_: ..., x: isize, _: ...) {} | ^^^^^^ error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:59:31 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:31 | LL | fn i_f4(_: ..., x: isize, _: ...) {} | ^^^^^^ | = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list +error[E0658]: c-variadic const function definitions are unstable + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:5 + | +LL | const fn i_f5(x: isize, _: ...) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151787 for more information + = help: add `#![feature(const_c_variadic)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29 | LL | const fn i_f5(x: isize, _: ...) {} | ^^^^^^ @@ -167,7 +207,7 @@ LL | const fn i_f5(x: isize, _: ...) {} = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:68:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:72:23 | LL | fn t_f1(x: isize, _: ...) {} | ^^^^^^ @@ -175,7 +215,7 @@ LL | fn t_f1(x: isize, _: ...) {} = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:70:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:74:23 | LL | fn t_f2(x: isize, _: ...); | ^^^^^^ @@ -183,7 +223,7 @@ LL | fn t_f2(x: isize, _: ...); = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:72:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:76:13 | LL | fn t_f3(_: ...) {} | ^^^^^^ @@ -191,7 +231,7 @@ LL | fn t_f3(_: ...) {} = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` is not supported for non-extern functions - --> $DIR/variadic-ffi-semantic-restrictions.rs:74:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:78:13 | LL | fn t_f4(_: ...); | ^^^^^^ @@ -199,13 +239,13 @@ LL | fn t_f4(_: ...); = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:76:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:80:13 | LL | fn t_f5(_: ..., x: isize) {} | ^^^^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:78:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13 | LL | fn t_f6(_: ..., x: isize); | ^^^^^^ @@ -223,7 +263,7 @@ LL | const unsafe extern "C" fn f4_1(x: isize, _: ...) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:37:36 | LL | const extern "C" fn f4_2(x: isize, _: ...) {} | ^ - value is dropped here @@ -235,7 +275,7 @@ LL | const extern "C" fn f4_2(x: isize, _: ...) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29 | LL | const fn i_f5(x: isize, _: ...) {} | ^ - value is dropped here @@ -246,6 +286,7 @@ LL | const fn i_f5(x: isize, _: ...) {} = help: add `#![feature(const_destruct)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 29 previous errors +error: aborting due to 33 previous errors -For more information about this error, try `rustc --explain E0493`. +Some errors have detailed explanations: E0493, E0658. +For more information about an error, try `rustc --explain E0493`. From dab350a3ed7691201f6752c4375e8e6ab40b58b9 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 15 Feb 2026 10:32:50 +0100 Subject: [PATCH 07/13] Remove timing assertion from `oneshot::send_before_recv_timeout` --- library/std/tests/sync/oneshot.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/tests/sync/oneshot.rs b/library/std/tests/sync/oneshot.rs index 8c47f35ebfea3..8e63a26fa3ac8 100644 --- a/library/std/tests/sync/oneshot.rs +++ b/library/std/tests/sync/oneshot.rs @@ -89,15 +89,15 @@ fn send_before_recv_timeout() { assert!(sender.send(22i128).is_ok()); - let start = Instant::now(); - let timeout = Duration::from_secs(1); match receiver.recv_timeout(timeout) { Ok(22) => {} _ => panic!("expected Ok(22)"), } - assert!(start.elapsed() < timeout); + // FIXME(#152648): There previously was a timing assertion here. + // This was removed, because under load there's no guarantee that the main thread is + // scheduled and run before `timeout` expires } #[test] From 2c1d605f21efee7ba9414e84a61498f34516dd5c Mon Sep 17 00:00:00 2001 From: xonx <119700621+xonx4l@users.noreply.github.com> Date: Tue, 28 Oct 2025 11:49:20 +0000 Subject: [PATCH 08/13] unify and deduplicate floats --- .../0029-sysroot_tests-disable-f16-math.patch | 133 ++++ library/coretests/tests/floats/mod.rs | 746 ++++++++++++++---- library/coretests/tests/lib.rs | 2 + library/std/Cargo.toml | 4 - library/std/tests/floats/f128.rs | 320 -------- library/std/tests/floats/f16.rs | 297 ------- library/std/tests/floats/f32.rs | 258 ------ library/std/tests/floats/f64.rs | 249 ------ library/std/tests/floats/lib.rs | 43 - 9 files changed, 707 insertions(+), 1345 deletions(-) create mode 100644 compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch delete mode 100644 library/std/tests/floats/f128.rs delete mode 100644 library/std/tests/floats/f16.rs delete mode 100644 library/std/tests/floats/f32.rs delete mode 100644 library/std/tests/floats/f64.rs delete mode 100644 library/std/tests/floats/lib.rs diff --git a/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch b/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch new file mode 100644 index 0000000000000..6a0244cfde3fb --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch @@ -0,0 +1,133 @@ +From 285d5716fcfa6d43a3516d899b73bc85da322c25 Mon Sep 17 00:00:00 2001 +From: xonx <119700621+xonx4l@users.noreply.github.com> +Date: Sun, 15 Feb 2026 14:06:49 +0000 +Subject: [PATCH] Disable f16 math tests for cranelift + +--- + coretests/tests/floats/mod.rs | 26 +++++++++++++------------- + 1 file changed, 13 insertions(+), 13 deletions(-) + +diff --git a/coretests/tests/floats/mod.rs b/coretests/tests/floats/mod.rs +index c61961f8584..d7b4fa20322 100644 +--- a/coretests/tests/floats/mod.rs ++++ b/coretests/tests/floats/mod.rs +@@ -1534,7 +1534,7 @@ fn s_nan() -> Float { + name: powf, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1557,7 +1557,7 @@ fn s_nan() -> Float { + name: exp, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1578,7 +1578,7 @@ fn s_nan() -> Float { + name: exp2, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1598,7 +1598,7 @@ fn s_nan() -> Float { + name: ln, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1620,7 +1620,7 @@ fn s_nan() -> Float { + name: log, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1645,7 +1645,7 @@ fn s_nan() -> Float { + name: log2, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1668,7 +1668,7 @@ fn s_nan() -> Float { + name: log10, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1692,7 +1692,7 @@ fn s_nan() -> Float { + name: asinh, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1725,7 +1725,7 @@ fn s_nan() -> Float { + name: acosh, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1753,7 +1753,7 @@ fn s_nan() -> Float { + name: atanh, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1779,7 +1779,7 @@ fn s_nan() -> Float { + name: gamma, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -1814,7 +1814,7 @@ fn s_nan() -> Float { + name: ln_gamma, + attrs: { + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +@@ -2027,7 +2027,7 @@ fn s_nan() -> Float { + attrs: { + // FIXME(f16_f128): add math tests when available + const: #[cfg(false)], +- f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], ++ f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { +-- +2.50.1 + diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 06fc3c96eafc8..c61961f8584e7 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -2,19 +2,35 @@ use std::num::FpCategory as Fp; use std::ops::{Add, Div, Mul, Rem, Sub}; trait TestableFloat: Sized { + const BITS: u32; /// Unsigned int with the same size, for converting to/from bits. type Int; /// Set the default tolerance for float comparison based on the type. const APPROX: Self; /// Allow looser tolerance for f32 on miri const POWI_APPROX: Self = Self::APPROX; + /// Tolerance for `powf` tests; some types need looser bounds + const POWF_APPROX: Self = Self::APPROX; /// Allow looser tolerance for f16 const _180_TO_RADIANS_APPROX: Self = Self::APPROX; /// Allow for looser tolerance for f16 const PI_TO_DEGREES_APPROX: Self = Self::APPROX; + /// Tolerance for math tests + const EXP_APPROX: Self = Self::APPROX; + const LN_APPROX: Self = Self::APPROX; + const LOG_APPROX: Self = Self::APPROX; + const LOG2_APPROX: Self = Self::APPROX; + const LOG10_APPROX: Self = Self::APPROX; + const ASINH_APPROX: Self = Self::APPROX; + const ACOSH_APPROX: Self = Self::APPROX; + const ATANH_APPROX: Self = Self::APPROX; + const GAMMA_APPROX: Self = Self::APPROX; + const GAMMA_APPROX_LOOSE: Self = Self::APPROX; + const LNGAMMA_APPROX: Self = Self::APPROX; + const LNGAMMA_APPROX_LOOSE: Self = Self::APPROX; const ZERO: Self; const ONE: Self; - const PI: Self; + const MIN_POSITIVE_NORMAL: Self; const MAX_SUBNORMAL: Self; /// Smallest number @@ -43,13 +59,26 @@ trait TestableFloat: Sized { } impl TestableFloat for f16 { + const BITS: u32 = 16; type Int = u16; const APPROX: Self = 1e-3; + const POWF_APPROX: Self = 5e-1; const _180_TO_RADIANS_APPROX: Self = 1e-2; const PI_TO_DEGREES_APPROX: Self = 0.125; + const EXP_APPROX: Self = 1e-2; + const LN_APPROX: Self = 1e-2; + const LOG_APPROX: Self = 1e-2; + const LOG2_APPROX: Self = 1e-2; + const LOG10_APPROX: Self = 1e-2; + const ASINH_APPROX: Self = 1e-2; + const ACOSH_APPROX: Self = 1e-2; + const ATANH_APPROX: Self = 1e-2; + const GAMMA_APPROX: Self = 1e-2; + const GAMMA_APPROX_LOOSE: Self = 1e-1; + const LNGAMMA_APPROX: Self = 1e-2; + const LNGAMMA_APPROX_LOOSE: Self = 1e-1; const ZERO: Self = 0.0; const ONE: Self = 1.0; - const PI: Self = std::f16::consts::PI; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); const TINY: Self = Self::from_bits(0x1); @@ -70,15 +99,28 @@ impl TestableFloat for f16 { } impl TestableFloat for f32 { + const BITS: u32 = 32; type Int = u32; const APPROX: Self = 1e-6; /// Miri adds some extra errors to float functions; make sure the tests still pass. /// These values are purely used as a canary to test against and are thus not a stable guarantee Rust provides. /// They serve as a way to get an idea of the real precision of floating point operations on different platforms. const POWI_APPROX: Self = if cfg!(miri) { 1e-4 } else { Self::APPROX }; + const POWF_APPROX: Self = if cfg!(miri) { 1e-3 } else { 1e-4 }; + const EXP_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const LN_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const LOG_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const LOG2_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const LOG10_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const ASINH_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const ACOSH_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const ATANH_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const GAMMA_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const GAMMA_APPROX_LOOSE: Self = if cfg!(miri) { 1e-2 } else { 1e-4 }; + const LNGAMMA_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const LNGAMMA_APPROX_LOOSE: Self = if cfg!(miri) { 1e-2 } else { 1e-4 }; const ZERO: Self = 0.0; const ONE: Self = 1.0; - const PI: Self = std::f32::consts::PI; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); const TINY: Self = Self::from_bits(0x1); @@ -99,11 +141,13 @@ impl TestableFloat for f32 { } impl TestableFloat for f64 { + const BITS: u32 = 64; type Int = u64; const APPROX: Self = 1e-6; + const GAMMA_APPROX_LOOSE: Self = 1e-4; + const LNGAMMA_APPROX_LOOSE: Self = 1e-4; const ZERO: Self = 0.0; const ONE: Self = 1.0; - const PI: Self = std::f64::consts::PI; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); const TINY: Self = Self::from_bits(0x1); @@ -124,11 +168,23 @@ impl TestableFloat for f64 { } impl TestableFloat for f128 { + const BITS: u32 = 128; type Int = u128; const APPROX: Self = 1e-9; + const EXP_APPROX: Self = 1e-12; + const LN_APPROX: Self = 1e-12; + const LOG_APPROX: Self = 1e-12; + const LOG2_APPROX: Self = 1e-12; + const LOG10_APPROX: Self = 1e-12; + const ASINH_APPROX: Self = 1e-10; + const ACOSH_APPROX: Self = 1e-10; + const ATANH_APPROX: Self = 1e-10; + const GAMMA_APPROX: Self = 1e-12; + const GAMMA_APPROX_LOOSE: Self = 1e-10; + const LNGAMMA_APPROX: Self = 1e-12; + const LNGAMMA_APPROX_LOOSE: Self = 1e-10; const ZERO: Self = 0.0; const ONE: Self = 1.0; - const PI: Self = std::f128::consts::PI; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); const TINY: Self = Self::from_bits(0x1); @@ -287,6 +343,8 @@ macro_rules! float_test { #[test] $( $( #[$f16_meta] )+ )? fn test_f16() { + #[allow(unused_imports)] + use core::f16::consts; type $fty = f16; #[allow(unused)] const fn flt (x: $fty) -> $fty { x } @@ -296,6 +354,8 @@ macro_rules! float_test { #[test] $( $( #[$f32_meta] )+ )? fn test_f32() { + #[allow(unused_imports)] + use core::f32::consts; type $fty = f32; #[allow(unused)] const fn flt (x: $fty) -> $fty { x } @@ -305,6 +365,8 @@ macro_rules! float_test { #[test] $( $( #[$f64_meta] )+ )? fn test_f64() { + #[allow(unused_imports)] + use core::f64::consts; type $fty = f64; #[allow(unused)] const fn flt (x: $fty) -> $fty { x } @@ -314,6 +376,8 @@ macro_rules! float_test { #[test] $( $( #[$f128_meta] )+ )? fn test_f128() { + #[allow(unused_imports)] + use core::f128::consts; type $fty = f128; #[allow(unused)] const fn flt (x: $fty) -> $fty { x } @@ -338,6 +402,8 @@ macro_rules! float_test { #[test] $( $( #[$f16_const_meta] )+ )? fn test_f16() { + #[allow(unused_imports)] + use core::f16::consts; type $fty = f16; #[allow(unused)] const fn flt (x: $fty) -> $fty { x } @@ -347,6 +413,8 @@ macro_rules! float_test { #[test] $( $( #[$f32_const_meta] )+ )? fn test_f32() { + #[allow(unused_imports)] + use core::f32::consts; type $fty = f32; #[allow(unused)] const fn flt (x: $fty) -> $fty { x } @@ -356,6 +424,8 @@ macro_rules! float_test { #[test] $( $( #[$f64_const_meta] )+ )? fn test_f64() { + #[allow(unused_imports)] + use core::f64::consts; type $fty = f64; #[allow(unused)] const fn flt (x: $fty) -> $fty { x } @@ -365,6 +435,8 @@ macro_rules! float_test { #[test] $( $( #[$f128_const_meta] )+ )? fn test_f128() { + #[allow(unused_imports)] + use core::f128::consts; type $fty = f128; #[allow(unused)] const fn flt (x: $fty) -> $fty { x } @@ -631,25 +703,25 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((0.0 as Float).min(0.0), 0.0); - assert_biteq!((-0.0 as Float).min(-0.0), -0.0); - assert_biteq!((9.0 as Float).min(9.0), 9.0); - assert_biteq!((-9.0 as Float).min(0.0), -9.0); - assert_biteq!((0.0 as Float).min(9.0), 0.0); - assert_biteq!((-0.0 as Float).min(9.0), -0.0); - assert_biteq!((-0.0 as Float).min(-9.0), -9.0); + assert_biteq!(flt(0.0).min(0.0), 0.0); + assert_biteq!(flt(-0.0).min(-0.0), -0.0); + assert_biteq!(flt(9.0).min(9.0), 9.0); + assert_biteq!(flt(-9.0).min(0.0), -9.0); + assert_biteq!(flt(0.0).min(9.0), 0.0); + assert_biteq!(flt(-0.0).min(9.0), -0.0); + assert_biteq!(flt(-0.0).min(-9.0), -9.0); assert_biteq!(Float::INFINITY.min(9.0), 9.0); - assert_biteq!((9.0 as Float).min(Float::INFINITY), 9.0); + assert_biteq!(flt(9.0).min(Float::INFINITY), 9.0); assert_biteq!(Float::INFINITY.min(-9.0), -9.0); - assert_biteq!((-9.0 as Float).min(Float::INFINITY), -9.0); + assert_biteq!(flt(-9.0).min(Float::INFINITY), -9.0); assert_biteq!(Float::NEG_INFINITY.min(9.0), Float::NEG_INFINITY); - assert_biteq!((9.0 as Float).min(Float::NEG_INFINITY), Float::NEG_INFINITY); + assert_biteq!(flt(9.0).min(Float::NEG_INFINITY), Float::NEG_INFINITY); assert_biteq!(Float::NEG_INFINITY.min(-9.0), Float::NEG_INFINITY); - assert_biteq!((-9.0 as Float).min(Float::NEG_INFINITY), Float::NEG_INFINITY); + assert_biteq!(flt(-9.0).min(Float::NEG_INFINITY), Float::NEG_INFINITY); assert_biteq!(Float::NAN.min(9.0), 9.0); assert_biteq!(Float::NAN.min(-9.0), -9.0); - assert_biteq!((9.0 as Float).min(Float::NAN), 9.0); - assert_biteq!((-9.0 as Float).min(Float::NAN), -9.0); + assert_biteq!(flt(9.0).min(Float::NAN), 9.0); + assert_biteq!(flt(-9.0).min(Float::NAN), -9.0); assert!(Float::NAN.min(Float::NAN).is_nan()); } } @@ -661,26 +733,26 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((0.0 as Float).max(0.0), 0.0); - assert_biteq!((-0.0 as Float).max(-0.0), -0.0); - assert_biteq!((9.0 as Float).max(9.0), 9.0); - assert_biteq!((-9.0 as Float).max(0.0), 0.0); - assert_biteq!((-9.0 as Float).max(-0.0), -0.0); - assert_biteq!((0.0 as Float).max(9.0), 9.0); - assert_biteq!((0.0 as Float).max(-9.0), 0.0); - assert_biteq!((-0.0 as Float).max(-9.0), -0.0); + assert_biteq!(flt(0.0).max(0.0), 0.0); + assert_biteq!(flt(-0.0).max(-0.0), -0.0); + assert_biteq!(flt(9.0).max(9.0), 9.0); + assert_biteq!(flt(-9.0).max(0.0), 0.0); + assert_biteq!(flt(-9.0).max(-0.0), -0.0); + assert_biteq!(flt(0.0).max(9.0), 9.0); + assert_biteq!(flt(0.0).max(-9.0), 0.0); + assert_biteq!(flt(-0.0).max(-9.0), -0.0); assert_biteq!(Float::INFINITY.max(9.0), Float::INFINITY); - assert_biteq!((9.0 as Float).max(Float::INFINITY), Float::INFINITY); + assert_biteq!(flt(9.0).max(Float::INFINITY), Float::INFINITY); assert_biteq!(Float::INFINITY.max(-9.0), Float::INFINITY); - assert_biteq!((-9.0 as Float).max(Float::INFINITY), Float::INFINITY); + assert_biteq!(flt(-9.0).max(Float::INFINITY), Float::INFINITY); assert_biteq!(Float::NEG_INFINITY.max(9.0), 9.0); - assert_biteq!((9.0 as Float).max(Float::NEG_INFINITY), 9.0); + assert_biteq!(flt(9.0).max(Float::NEG_INFINITY), 9.0); assert_biteq!(Float::NEG_INFINITY.max(-9.0), -9.0); - assert_biteq!((-9.0 as Float).max(Float::NEG_INFINITY), -9.0); + assert_biteq!(flt(-9.0).max(Float::NEG_INFINITY), -9.0); assert_biteq!(Float::NAN.max(9.0), 9.0); assert_biteq!(Float::NAN.max(-9.0), -9.0); - assert_biteq!((9.0 as Float).max(Float::NAN), 9.0); - assert_biteq!((-9.0 as Float).max(Float::NAN), -9.0); + assert_biteq!(flt(9.0).max(Float::NAN), 9.0); + assert_biteq!(flt(-9.0).max(Float::NAN), -9.0); assert!(Float::NAN.max(Float::NAN).is_nan()); } } @@ -692,26 +764,26 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((0.0 as Float).minimum(0.0), 0.0); - assert_biteq!((-0.0 as Float).minimum(0.0), -0.0); - assert_biteq!((-0.0 as Float).minimum(-0.0), -0.0); - assert_biteq!((9.0 as Float).minimum(9.0), 9.0); - assert_biteq!((-9.0 as Float).minimum(0.0), -9.0); - assert_biteq!((0.0 as Float).minimum(9.0), 0.0); - assert_biteq!((-0.0 as Float).minimum(9.0), -0.0); - assert_biteq!((-0.0 as Float).minimum(-9.0), -9.0); + assert_biteq!(flt(0.0).minimum(0.0), 0.0); + assert_biteq!(flt(-0.0).minimum(0.0), -0.0); + assert_biteq!(flt(-0.0).minimum(-0.0), -0.0); + assert_biteq!(flt(9.0).minimum(9.0), 9.0); + assert_biteq!(flt(-9.0).minimum(0.0), -9.0); + assert_biteq!(flt(0.0).minimum(9.0), 0.0); + assert_biteq!(flt(-0.0).minimum(9.0), -0.0); + assert_biteq!(flt(-0.0).minimum(-9.0), -9.0); assert_biteq!(Float::INFINITY.minimum(9.0), 9.0); - assert_biteq!((9.0 as Float).minimum(Float::INFINITY), 9.0); + assert_biteq!(flt(9.0).minimum(Float::INFINITY), 9.0); assert_biteq!(Float::INFINITY.minimum(-9.0), -9.0); - assert_biteq!((-9.0 as Float).minimum(Float::INFINITY), -9.0); + assert_biteq!(flt(-9.0).minimum(Float::INFINITY), -9.0); assert_biteq!(Float::NEG_INFINITY.minimum(9.0), Float::NEG_INFINITY); - assert_biteq!((9.0 as Float).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY); + assert_biteq!(flt(9.0).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY); assert_biteq!(Float::NEG_INFINITY.minimum(-9.0), Float::NEG_INFINITY); - assert_biteq!((-9.0 as Float).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY); + assert_biteq!(flt(-9.0).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY); assert!(Float::NAN.minimum(9.0).is_nan()); assert!(Float::NAN.minimum(-9.0).is_nan()); - assert!((9.0 as Float).minimum(Float::NAN).is_nan()); - assert!((-9.0 as Float).minimum(Float::NAN).is_nan()); + assert!(flt(9.0).minimum(Float::NAN).is_nan()); + assert!(flt(-9.0).minimum(Float::NAN).is_nan()); assert!(Float::NAN.minimum(Float::NAN).is_nan()); } } @@ -723,27 +795,27 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((0.0 as Float).maximum(0.0), 0.0); - assert_biteq!((-0.0 as Float).maximum(0.0), 0.0); - assert_biteq!((-0.0 as Float).maximum(-0.0), -0.0); - assert_biteq!((9.0 as Float).maximum(9.0), 9.0); - assert_biteq!((-9.0 as Float).maximum(0.0), 0.0); - assert_biteq!((-9.0 as Float).maximum(-0.0), -0.0); - assert_biteq!((0.0 as Float).maximum(9.0), 9.0); - assert_biteq!((0.0 as Float).maximum(-9.0), 0.0); - assert_biteq!((-0.0 as Float).maximum(-9.0), -0.0); + assert_biteq!(flt(0.0).maximum(0.0), 0.0); + assert_biteq!(flt(-0.0).maximum(0.0), 0.0); + assert_biteq!(flt(-0.0).maximum(-0.0), -0.0); + assert_biteq!(flt(9.0).maximum(9.0), 9.0); + assert_biteq!(flt(-9.0).maximum(0.0), 0.0); + assert_biteq!(flt(-9.0).maximum(-0.0), -0.0); + assert_biteq!(flt(0.0).maximum(9.0), 9.0); + assert_biteq!(flt(0.0).maximum(-9.0), 0.0); + assert_biteq!(flt(-0.0).maximum(-9.0), -0.0); assert_biteq!(Float::INFINITY.maximum(9.0), Float::INFINITY); - assert_biteq!((9.0 as Float).maximum(Float::INFINITY), Float::INFINITY); + assert_biteq!(flt(9.0).maximum(Float::INFINITY), Float::INFINITY); assert_biteq!(Float::INFINITY.maximum(-9.0), Float::INFINITY); - assert_biteq!((-9.0 as Float).maximum(Float::INFINITY), Float::INFINITY); + assert_biteq!(flt(-9.0).maximum(Float::INFINITY), Float::INFINITY); assert_biteq!(Float::NEG_INFINITY.maximum(9.0), 9.0); - assert_biteq!((9.0 as Float).maximum(Float::NEG_INFINITY), 9.0); + assert_biteq!(flt(9.0).maximum(Float::NEG_INFINITY), 9.0); assert_biteq!(Float::NEG_INFINITY.maximum(-9.0), -9.0); - assert_biteq!((-9.0 as Float).maximum(Float::NEG_INFINITY), -9.0); + assert_biteq!(flt(-9.0).maximum(Float::NEG_INFINITY), -9.0); assert!(Float::NAN.maximum(9.0).is_nan()); assert!(Float::NAN.maximum(-9.0).is_nan()); - assert!((9.0 as Float).maximum(Float::NAN).is_nan()); - assert!((-9.0 as Float).maximum(Float::NAN).is_nan()); + assert!(flt(9.0).maximum(Float::NAN).is_nan()); + assert!(flt(-9.0).maximum(Float::NAN).is_nan()); assert!(Float::NAN.maximum(Float::NAN).is_nan()); } } @@ -755,15 +827,15 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((0.5 as Float).midpoint(0.5), 0.5); - assert_biteq!((0.5 as Float).midpoint(2.5), 1.5); - assert_biteq!((3.0 as Float).midpoint(4.0), 3.5); - assert_biteq!((-3.0 as Float).midpoint(4.0), 0.5); - assert_biteq!((3.0 as Float).midpoint(-4.0), -0.5); - assert_biteq!((-3.0 as Float).midpoint(-4.0), -3.5); - assert_biteq!((0.0 as Float).midpoint(0.0), 0.0); - assert_biteq!((-0.0 as Float).midpoint(-0.0), -0.0); - assert_biteq!((-5.0 as Float).midpoint(5.0), 0.0); + assert_biteq!(flt(0.5).midpoint(0.5), 0.5); + assert_biteq!(flt(0.5).midpoint(2.5), 1.5); + assert_biteq!(flt(3.0).midpoint(4.0), 3.5); + assert_biteq!(flt(-3.0).midpoint(4.0), 0.5); + assert_biteq!(flt(3.0).midpoint(-4.0), -0.5); + assert_biteq!(flt(-3.0).midpoint(-4.0), -3.5); + assert_biteq!(flt(0.0).midpoint(0.0), 0.0); + assert_biteq!(flt(-0.0).midpoint(-0.0), -0.0); + assert_biteq!(flt(-5.0).midpoint(5.0), 0.0); assert_biteq!(Float::MAX.midpoint(Float::MIN), 0.0); assert_biteq!(Float::MIN.midpoint(Float::MAX), 0.0); assert_biteq!(Float::MAX.midpoint(Float::MIN_POSITIVE), Float::MAX / 2.); @@ -793,7 +865,7 @@ float_test! { assert!(Float::NEG_INFINITY.midpoint(Float::INFINITY).is_nan()); assert!(Float::INFINITY.midpoint(Float::NEG_INFINITY).is_nan()); assert!(Float::NAN.midpoint(1.0).is_nan()); - assert!((1.0 as Float).midpoint(Float::NAN).is_nan()); + assert!(flt(1.0).midpoint(Float::NAN).is_nan()); assert!(Float::NAN.midpoint(Float::NAN).is_nan()); } } @@ -815,10 +887,10 @@ float_test! { // be safely doubled, while j is significantly smaller. for i in Float::MAX_EXP.saturating_sub(64)..Float::MAX_EXP { for j in 0..64u8 { - let large = (2.0 as Float).powi(i); + let large = flt(2.0).powi(i); // a much smaller number, such that there is no chance of overflow to test // potential double rounding in midpoint's implementation. - let small = (2.0 as Float).powi(Float::MAX_EXP - 1) + let small = flt(2.0).powi(Float::MAX_EXP - 1) * Float::EPSILON * Float::from(j); @@ -856,8 +928,8 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((1.0 as Float).copysign(-2.0), -1.0); - assert_biteq!((-1.0 as Float).copysign(2.0), 1.0); + assert_biteq!(flt(1.0).copysign(-2.0), -1.0); + assert_biteq!(flt(-1.0).copysign(2.0), 1.0); assert_biteq!(Float::INFINITY.copysign(-0.0), Float::NEG_INFINITY); assert_biteq!(Float::NEG_INFINITY.copysign(0.0), Float::INFINITY); } @@ -871,9 +943,9 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert!(Float::INFINITY.rem_euclid(42.0 as Float).is_nan()); - assert_biteq!((42.0 as Float).rem_euclid(Float::INFINITY), 42.0 as Float); - assert!((42.0 as Float).rem_euclid(Float::NAN).is_nan()); + assert!(Float::INFINITY.rem_euclid(42.0).is_nan()); + assert_biteq!(flt(42.0).rem_euclid(Float::INFINITY), 42.0); + assert!(flt(42.0).rem_euclid(Float::NAN).is_nan()); assert!(Float::INFINITY.rem_euclid(Float::INFINITY).is_nan()); assert!(Float::INFINITY.rem_euclid(Float::NAN).is_nan()); assert!(Float::NAN.rem_euclid(Float::INFINITY).is_nan()); @@ -888,8 +960,8 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((42.0 as Float).div_euclid(Float::INFINITY), 0.0); - assert!((42.0 as Float).div_euclid(Float::NAN).is_nan()); + assert_biteq!(flt(42.0).div_euclid(Float::INFINITY), 0.0); + assert!(flt(42.0).div_euclid(Float::NAN).is_nan()); assert!(Float::INFINITY.div_euclid(Float::INFINITY).is_nan()); assert!(Float::INFINITY.div_euclid(Float::NAN).is_nan()); assert!(Float::NAN.div_euclid(Float::INFINITY).is_nan()); @@ -903,18 +975,18 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((1.0 as Float).floor(), 1.0); - assert_biteq!((1.3 as Float).floor(), 1.0); - assert_biteq!((1.5 as Float).floor(), 1.0); - assert_biteq!((1.7 as Float).floor(), 1.0); - assert_biteq!((0.5 as Float).floor(), 0.0); - assert_biteq!((0.0 as Float).floor(), 0.0); - assert_biteq!((-0.0 as Float).floor(), -0.0); - assert_biteq!((-0.5 as Float).floor(), -1.0); - assert_biteq!((-1.0 as Float).floor(), -1.0); - assert_biteq!((-1.3 as Float).floor(), -2.0); - assert_biteq!((-1.5 as Float).floor(), -2.0); - assert_biteq!((-1.7 as Float).floor(), -2.0); + assert_biteq!(flt(1.0).floor(), 1.0); + assert_biteq!(flt(1.3).floor(), 1.0); + assert_biteq!(flt(1.5).floor(), 1.0); + assert_biteq!(flt(1.7).floor(), 1.0); + assert_biteq!(flt(0.5).floor(), 0.0); + assert_biteq!(flt(0.0).floor(), 0.0); + assert_biteq!(flt(-0.0).floor(), -0.0); + assert_biteq!(flt(-0.5).floor(), -1.0); + assert_biteq!(flt(-1.0).floor(), -1.0); + assert_biteq!(flt(-1.3).floor(), -2.0); + assert_biteq!(flt(-1.5).floor(), -2.0); + assert_biteq!(flt(-1.7).floor(), -2.0); assert_biteq!(Float::MAX.floor(), Float::MAX); assert_biteq!(Float::MIN.floor(), Float::MIN); assert_biteq!(Float::MIN_POSITIVE.floor(), 0.0); @@ -932,18 +1004,18 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((1.0 as Float).ceil(), 1.0); - assert_biteq!((1.3 as Float).ceil(), 2.0); - assert_biteq!((1.5 as Float).ceil(), 2.0); - assert_biteq!((1.7 as Float).ceil(), 2.0); - assert_biteq!((0.5 as Float).ceil(), 1.0); - assert_biteq!((0.0 as Float).ceil(), 0.0); - assert_biteq!((-0.0 as Float).ceil(), -0.0); - assert_biteq!((-0.5 as Float).ceil(), -0.0); - assert_biteq!((-1.0 as Float).ceil(), -1.0); - assert_biteq!((-1.3 as Float).ceil(), -1.0); - assert_biteq!((-1.5 as Float).ceil(), -1.0); - assert_biteq!((-1.7 as Float).ceil(), -1.0); + assert_biteq!(flt(1.0).ceil(), 1.0); + assert_biteq!(flt(1.3).ceil(), 2.0); + assert_biteq!(flt(1.5).ceil(), 2.0); + assert_biteq!(flt(1.7).ceil(), 2.0); + assert_biteq!(flt(0.5).ceil(), 1.0); + assert_biteq!(flt(0.0).ceil(), 0.0); + assert_biteq!(flt(-0.0).ceil(), -0.0); + assert_biteq!(flt(-0.5).ceil(), -0.0); + assert_biteq!(flt(-1.0).ceil(), -1.0); + assert_biteq!(flt(-1.3).ceil(), -1.0); + assert_biteq!(flt(-1.5).ceil(), -1.0); + assert_biteq!(flt(-1.7).ceil(), -1.0); assert_biteq!(Float::MAX.ceil(), Float::MAX); assert_biteq!(Float::MIN.ceil(), Float::MIN); assert_biteq!(Float::MIN_POSITIVE.ceil(), 1.0); @@ -961,19 +1033,19 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((2.5 as Float).round(), 3.0); - assert_biteq!((1.0 as Float).round(), 1.0); - assert_biteq!((1.3 as Float).round(), 1.0); - assert_biteq!((1.5 as Float).round(), 2.0); - assert_biteq!((1.7 as Float).round(), 2.0); - assert_biteq!((0.5 as Float).round(), 1.0); - assert_biteq!((0.0 as Float).round(), 0.0); - assert_biteq!((-0.0 as Float).round(), -0.0); - assert_biteq!((-0.5 as Float).round(), -1.0); - assert_biteq!((-1.0 as Float).round(), -1.0); - assert_biteq!((-1.3 as Float).round(), -1.0); - assert_biteq!((-1.5 as Float).round(), -2.0); - assert_biteq!((-1.7 as Float).round(), -2.0); + assert_biteq!(flt(2.5).round(), 3.0); + assert_biteq!(flt(1.0).round(), 1.0); + assert_biteq!(flt(1.3).round(), 1.0); + assert_biteq!(flt(1.5).round(), 2.0); + assert_biteq!(flt(1.7).round(), 2.0); + assert_biteq!(flt(0.5).round(), 1.0); + assert_biteq!(flt(0.0).round(), 0.0); + assert_biteq!(flt(-0.0).round(), -0.0); + assert_biteq!(flt(-0.5).round(), -1.0); + assert_biteq!(flt(-1.0).round(), -1.0); + assert_biteq!(flt(-1.3).round(), -1.0); + assert_biteq!(flt(-1.5).round(), -2.0); + assert_biteq!(flt(-1.7).round(), -2.0); assert_biteq!(Float::MAX.round(), Float::MAX); assert_biteq!(Float::MIN.round(), Float::MIN); assert_biteq!(Float::MIN_POSITIVE.round(), 0.0); @@ -991,19 +1063,19 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((2.5 as Float).round_ties_even(), 2.0); - assert_biteq!((1.0 as Float).round_ties_even(), 1.0); - assert_biteq!((1.3 as Float).round_ties_even(), 1.0); - assert_biteq!((1.5 as Float).round_ties_even(), 2.0); - assert_biteq!((1.7 as Float).round_ties_even(), 2.0); - assert_biteq!((0.5 as Float).round_ties_even(), 0.0); - assert_biteq!((0.0 as Float).round_ties_even(), 0.0); - assert_biteq!((-0.0 as Float).round_ties_even(), -0.0); - assert_biteq!((-0.5 as Float).round_ties_even(), -0.0); - assert_biteq!((-1.0 as Float).round_ties_even(), -1.0); - assert_biteq!((-1.3 as Float).round_ties_even(), -1.0); - assert_biteq!((-1.5 as Float).round_ties_even(), -2.0); - assert_biteq!((-1.7 as Float).round_ties_even(), -2.0); + assert_biteq!(flt(2.5).round_ties_even(), 2.0); + assert_biteq!(flt(1.0).round_ties_even(), 1.0); + assert_biteq!(flt(1.3).round_ties_even(), 1.0); + assert_biteq!(flt(1.5).round_ties_even(), 2.0); + assert_biteq!(flt(1.7).round_ties_even(), 2.0); + assert_biteq!(flt(0.5).round_ties_even(), 0.0); + assert_biteq!(flt(0.0).round_ties_even(), 0.0); + assert_biteq!(flt(-0.0).round_ties_even(), -0.0); + assert_biteq!(flt(-0.5).round_ties_even(), -0.0); + assert_biteq!(flt(-1.0).round_ties_even(), -1.0); + assert_biteq!(flt(-1.3).round_ties_even(), -1.0); + assert_biteq!(flt(-1.5).round_ties_even(), -2.0); + assert_biteq!(flt(-1.7).round_ties_even(), -2.0); assert_biteq!(Float::MAX.round_ties_even(), Float::MAX); assert_biteq!(Float::MIN.round_ties_even(), Float::MIN); assert_biteq!(Float::MIN_POSITIVE.round_ties_even(), 0.0); @@ -1021,18 +1093,18 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((1.0 as Float).trunc(), 1.0); - assert_biteq!((1.3 as Float).trunc(), 1.0); - assert_biteq!((1.5 as Float).trunc(), 1.0); - assert_biteq!((1.7 as Float).trunc(), 1.0); - assert_biteq!((0.5 as Float).trunc(), 0.0); - assert_biteq!((0.0 as Float).trunc(), 0.0); - assert_biteq!((-0.0 as Float).trunc(), -0.0); - assert_biteq!((-0.5 as Float).trunc(), -0.0); - assert_biteq!((-1.0 as Float).trunc(), -1.0); - assert_biteq!((-1.3 as Float).trunc(), -1.0); - assert_biteq!((-1.5 as Float).trunc(), -1.0); - assert_biteq!((-1.7 as Float).trunc(), -1.0); + assert_biteq!(flt(1.0).trunc(), 1.0); + assert_biteq!(flt(1.3).trunc(), 1.0); + assert_biteq!(flt(1.5).trunc(), 1.0); + assert_biteq!(flt(1.7).trunc(), 1.0); + assert_biteq!(flt(0.5).trunc(), 0.0); + assert_biteq!(flt(0.0).trunc(), 0.0); + assert_biteq!(flt(-0.0).trunc(), -0.0); + assert_biteq!(flt(-0.5).trunc(), -0.0); + assert_biteq!(flt(-1.0).trunc(), -1.0); + assert_biteq!(flt(-1.3).trunc(), -1.0); + assert_biteq!(flt(-1.5).trunc(), -1.0); + assert_biteq!(flt(-1.7).trunc(), -1.0); assert_biteq!(Float::MAX.trunc(), Float::MAX); assert_biteq!(Float::MIN.trunc(), Float::MIN); assert_biteq!(Float::MIN_POSITIVE.trunc(), 0.0); @@ -1050,18 +1122,18 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((1.0 as Float).fract(), 0.0); - assert_approx_eq!((1.3 as Float).fract(), 0.3); // rounding differs between float types - assert_biteq!((1.5 as Float).fract(), 0.5); - assert_approx_eq!((1.7 as Float).fract(), 0.7); - assert_biteq!((0.5 as Float).fract(), 0.5); - assert_biteq!((0.0 as Float).fract(), 0.0); - assert_biteq!((-0.0 as Float).fract(), 0.0); - assert_biteq!((-0.5 as Float).fract(), -0.5); - assert_biteq!((-1.0 as Float).fract(), 0.0); - assert_approx_eq!((-1.3 as Float).fract(), -0.3); // rounding differs between float types - assert_biteq!((-1.5 as Float).fract(), -0.5); - assert_approx_eq!((-1.7 as Float).fract(), -0.7); + assert_biteq!(flt(1.0).fract(), 0.0); + assert_approx_eq!(flt(1.3).fract(), 0.3); // rounding differs between float types + assert_biteq!(flt(1.5).fract(), 0.5); + assert_approx_eq!(flt(1.7).fract(), 0.7); + assert_biteq!(flt(0.5).fract(), 0.5); + assert_biteq!(flt(0.0).fract(), 0.0); + assert_biteq!(flt(-0.0).fract(), 0.0); + assert_biteq!(flt(-0.5).fract(), -0.5); + assert_biteq!(flt(-1.0).fract(), 0.0); + assert_approx_eq!(flt(-1.3).fract(), -0.3); // rounding differs between float types + assert_biteq!(flt(-1.5).fract(), -0.5); + assert_approx_eq!(flt(-1.7).fract(), -0.7); assert_biteq!(Float::MAX.fract(), 0.0); assert_biteq!(Float::MIN.fract(), 0.0); assert_biteq!(Float::MIN_POSITIVE.fract(), Float::MIN_POSITIVE); @@ -1425,10 +1497,10 @@ float_test! { let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; let max: Float = Float::MAX; - assert_biteq!((1.0 as Float).recip(), 1.0); - assert_biteq!((2.0 as Float).recip(), 0.5); - assert_biteq!((-0.4 as Float).recip(), -2.5); - assert_biteq!((0.0 as Float).recip(), inf); + assert_biteq!(flt(1.0).recip(), 1.0); + assert_biteq!(flt(2.0).recip(), 0.5); + assert_biteq!(flt(-0.4).recip(), -2.5); + assert_biteq!(flt(0.0).recip(), inf); assert!(nan.recip().is_nan()); assert_biteq!(inf.recip(), 0.0); assert_biteq!(neg_inf.recip(), -0.0); @@ -1449,15 +1521,314 @@ float_test! { let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; assert_approx_eq!(Float::ONE.powi(1), Float::ONE); - assert_approx_eq!((-3.1 as Float).powi(2), 9.6100000000000005506706202140776519387, Float::POWI_APPROX); - assert_approx_eq!((5.9 as Float).powi(-2), 0.028727377190462507313100483690639638451); - assert_biteq!((8.3 as Float).powi(0), Float::ONE); + assert_approx_eq!(flt(-3.1).powi(2), 9.6100000000000005506706202140776519387, Float::POWI_APPROX); + assert_approx_eq!(flt(5.9).powi(-2), 0.028727377190462507313100483690639638451); + assert_biteq!(flt(8.3).powi(0), Float::ONE); assert!(nan.powi(2).is_nan()); assert_biteq!(inf.powi(3), inf); assert_biteq!(neg_inf.powi(2), inf); } } +float_test! { + name: powf, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let nan: Float = Float::NAN; + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_biteq!(flt(1.0).powf(1.0), 1.0); + assert_approx_eq!(flt(3.4).powf(4.5), 246.40818323761892815995637964326426756, Float::POWF_APPROX); + assert_approx_eq!(flt(2.7).powf(-3.2), 0.041652009108526178281070304373500889273, Float::POWF_APPROX); + assert_approx_eq!(flt(-3.1).powf(2.0), 9.6100000000000005506706202140776519387, Float::POWF_APPROX); + assert_approx_eq!(flt(5.9).powf(-2.0), 0.028727377190462507313100483690639638451, Float::POWF_APPROX); + assert_biteq!(flt(8.3).powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_biteq!(inf.powf(2.0), inf); + assert_biteq!(neg_inf.powf(3.0), neg_inf); + } +} + +float_test! { + name: exp, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_biteq!(1.0, flt(0.0).exp()); + assert_approx_eq!(consts::E, flt(1.0).exp(), Float::EXP_APPROX); + assert_approx_eq!(148.41315910257660342111558004055227962348775, flt(5.0).exp(), Float::EXP_APPROX); + + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + let nan: Float = Float::NAN; + assert_biteq!(inf, inf.exp()); + assert_biteq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); + } +} + +float_test! { + name: exp2, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_approx_eq!(32.0, flt(5.0).exp2(), Float::EXP_APPROX); + assert_biteq!(1.0, flt(0.0).exp2()); + + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + let nan: Float = Float::NAN; + assert_biteq!(inf, inf.exp2()); + assert_biteq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); + } +} + +float_test! { + name: ln, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let nan: Float = Float::NAN; + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_approx_eq!(flt(1.0).exp().ln(), 1.0, Float::LN_APPROX); + assert!(nan.ln().is_nan()); + assert_biteq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!(flt(-2.3).ln().is_nan()); + assert_biteq!(flt(-0.0).ln(), neg_inf); + assert_biteq!(flt(0.0).ln(), neg_inf); + assert_approx_eq!(flt(4.0).ln(), 1.3862943611198906188344642429163531366, Float::LN_APPROX); + } +} + +float_test! { + name: log, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let nan: Float = Float::NAN; + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_approx_eq!(flt(10.0).log(10.0), 1.0, Float::LOG_APPROX); + assert_approx_eq!(flt(2.3).log(3.5), 0.66485771361478710036766645911922010272, Float::LOG_APPROX); + assert_approx_eq!(flt(1.0).exp().log(flt(1.0).exp()), 1.0, Float::LOG_APPROX); + assert!(flt(1.0).log(1.0).is_nan()); + assert!(flt(1.0).log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_biteq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!(flt(-2.3).log(0.1).is_nan()); + assert_biteq!(flt(-0.0).log(2.0), neg_inf); + assert_biteq!(flt(0.0).log(7.0), neg_inf); + } +} + +float_test! { + name: log2, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let nan: Float = Float::NAN; + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_approx_eq!(flt(10.0).log2(), 3.32192809488736234787031942948939017, Float::LOG2_APPROX); + assert_approx_eq!(flt(2.3).log2(), 1.2016338611696504130002982471978765921, Float::LOG2_APPROX); + assert_approx_eq!(flt(1.0).exp().log2(), 1.4426950408889634073599246810018921381, Float::LOG2_APPROX); + assert!(nan.log2().is_nan()); + assert_biteq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!(flt(-2.3).log2().is_nan()); + assert_biteq!(flt(-0.0).log2(), neg_inf); + assert_biteq!(flt(0.0).log2(), neg_inf); + } +} + +float_test! { + name: log10, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let nan: Float = Float::NAN; + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_approx_eq!(flt(10.0).log10(), 1.0, Float::LOG10_APPROX); + assert_approx_eq!(flt(2.3).log10(), 0.36172783601759284532595218865859309898, Float::LOG10_APPROX); + assert_approx_eq!(flt(1.0).exp().log10(), 0.43429448190325182765112891891660508222, Float::LOG10_APPROX); + assert_biteq!(flt(1.0).log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_biteq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!(flt(-2.3).log10().is_nan()); + assert_biteq!(flt(-0.0).log10(), neg_inf); + assert_biteq!(flt(0.0).log10(), neg_inf); + } +} + +float_test! { + name: asinh, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_biteq!(flt(0.0).asinh(), 0.0); + assert_biteq!(flt(-0.0).asinh(), -0.0); + + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + let nan: Float = Float::NAN; + assert_biteq!(inf.asinh(), inf); + assert_biteq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!(flt(-0.0).asinh().is_sign_negative()); + + // issue 63271 + assert_approx_eq!(flt(2.0).asinh(), 1.443635475178810342493276740273105, Float::ASINH_APPROX); + assert_approx_eq!(flt(-2.0).asinh(), -1.443635475178810342493276740273105, Float::ASINH_APPROX); + + assert_approx_eq!(flt(-200.0).asinh(), -5.991470797049389, Float::ASINH_APPROX); + + #[allow(overflowing_literals)] + if Float::MAX > flt(66000.0) { + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!(flt(-67452098.07139316).asinh(), -18.720075426274544393985484294000831757220, Float::ASINH_APPROX); + } + } +} + +float_test! { + name: acosh, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_biteq!(flt(1.0).acosh(), 0.0); + assert!(flt(0.999).acosh().is_nan()); + + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + let nan: Float = Float::NAN; + assert_biteq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(flt(2.0).acosh(), 1.31695789692481670862504634730796844, Float::ACOSH_APPROX); + assert_approx_eq!(flt(3.0).acosh(), 1.76274717403908605046521864995958461, Float::ACOSH_APPROX); + + #[allow(overflowing_literals)] + if Float::MAX > flt(66000.0) { + // test for low accuracy from issue 104548 + assert_approx_eq!(flt(60.0), flt(60.0).cosh().acosh(), Float::ACOSH_APPROX); + } + } +} + +float_test! { + name: atanh, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_biteq!(flt(0.0).atanh(), 0.0); + assert_biteq!(flt(-0.0).atanh(), -0.0); + + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + assert_biteq!(flt(1.0).atanh(), inf); + assert_biteq!(flt(-1.0).atanh(), neg_inf); + + let nan: Float = Float::NAN; + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + + assert_approx_eq!(flt(0.5).atanh(), 0.54930614433405484569762261846126285, Float::ATANH_APPROX); + assert_approx_eq!(flt(-0.5).atanh(), -0.54930614433405484569762261846126285, Float::ATANH_APPROX); + } +} + +float_test! { + name: gamma, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_approx_eq!(flt(1.0).gamma(), 1.0, Float::GAMMA_APPROX); + assert_approx_eq!(flt(2.0).gamma(), 1.0, Float::GAMMA_APPROX); + assert_approx_eq!(flt(3.0).gamma(), 2.0, Float::GAMMA_APPROX); + assert_approx_eq!(flt(4.0).gamma(), 6.0, Float::GAMMA_APPROX); + assert_approx_eq!(flt(5.0).gamma(), 24.0, Float::GAMMA_APPROX_LOOSE); + assert_approx_eq!(flt(0.5).gamma(), consts::PI.sqrt(), Float::GAMMA_APPROX); + assert_approx_eq!(flt(-0.5).gamma(), flt(-2.0) * consts::PI.sqrt(), Float::GAMMA_APPROX_LOOSE); + assert_biteq!(flt(0.0).gamma(), Float::INFINITY); + assert_biteq!(flt(-0.0).gamma(), Float::NEG_INFINITY); + assert!(flt(-1.0).gamma().is_nan()); + assert!(flt(-2.0).gamma().is_nan()); + assert!(Float::NAN.gamma().is_nan()); + assert!(Float::NEG_INFINITY.gamma().is_nan()); + assert_biteq!(Float::INFINITY.gamma(), Float::INFINITY); + + // FIXME: there is a bug in the MinGW gamma implementation that causes this to + // return NaN. https://sourceforge.net/p/mingw-w64/bugs/517/ + if !cfg!(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm"))) { + assert_biteq!(flt(1760.9).gamma(), Float::INFINITY); + } + + if ::BITS <= 64 { + assert_biteq!(flt(171.71).gamma(), Float::INFINITY); + } + } +} + +float_test! { + name: ln_gamma, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + assert_approx_eq!(flt(1.0).ln_gamma().0, 0.0, Float::LNGAMMA_APPROX); + assert_eq!(flt(1.0).ln_gamma().1, 1); + assert_approx_eq!(flt(2.0).ln_gamma().0, 0.0, Float::LNGAMMA_APPROX); + assert_eq!(flt(2.0).ln_gamma().1, 1); + assert_approx_eq!(flt(3.0).ln_gamma().0, flt(2.0).ln(), Float::LNGAMMA_APPROX); + assert_eq!(flt(3.0).ln_gamma().1, 1); + assert_approx_eq!(flt(-0.5).ln_gamma().0, (flt(2.0) * consts::PI.sqrt()).ln(), Float::LNGAMMA_APPROX_LOOSE); + assert_eq!(flt(-0.5).ln_gamma().1, -1); + } +} + float_test! { name: to_degrees, attrs: { @@ -1465,17 +1836,17 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { - let pi: Float = Float::PI; + let pi: Float = consts::PI; let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; - assert_biteq!((0.0 as Float).to_degrees(), 0.0); - assert_approx_eq!((-5.8 as Float).to_degrees(), -332.31552117587745090765431723855668471); + assert_biteq!(flt(0.0).to_degrees(), 0.0); + assert_approx_eq!(flt(-5.8).to_degrees(), -332.31552117587745090765431723855668471); assert_approx_eq!(pi.to_degrees(), 180.0, Float::PI_TO_DEGREES_APPROX); assert!(nan.to_degrees().is_nan()); assert_biteq!(inf.to_degrees(), inf); assert_biteq!(neg_inf.to_degrees(), neg_inf); - assert_biteq!((1.0 as Float).to_degrees(), 57.2957795130823208767981548141051703); + assert_biteq!(flt(1.0).to_degrees(), 57.2957795130823208767981548141051703); } } @@ -1486,14 +1857,14 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { - let pi: Float = Float::PI; + let pi: Float = consts::PI; let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; - assert_biteq!((0.0 as Float).to_radians(), 0.0); - assert_approx_eq!((154.6 as Float).to_radians(), 2.6982790235832334267135442069489767804); - assert_approx_eq!((-332.31 as Float).to_radians(), -5.7999036373023566567593094812182763013); - assert_approx_eq!((180.0 as Float).to_radians(), pi, Float::_180_TO_RADIANS_APPROX); + assert_biteq!(flt(0.0).to_radians(), 0.0); + assert_approx_eq!(flt(154.6).to_radians(), 2.6982790235832334267135442069489767804); + assert_approx_eq!(flt(-332.31).to_radians(), -5.7999036373023566567593094812182763013); + assert_approx_eq!(flt(180.0).to_radians(), pi, Float::_180_TO_RADIANS_APPROX); assert!(nan.to_radians().is_nan()); assert_biteq!(inf.to_radians(), inf); assert_biteq!(neg_inf.to_radians(), neg_inf); @@ -1507,8 +1878,8 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { - let a: Float = 123.0; - let b: Float = 456.0; + let a: Float = flt(123.0); + let b: Float = flt(456.0); // Check that individual operations match their primitive counterparts. // @@ -1564,8 +1935,8 @@ float_test! { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; - assert_biteq!(flt(12.3).mul_add(4.5, 6.7), Float::MUL_ADD_RESULT); - assert_biteq!((flt(-12.3)).mul_add(-4.5, -6.7), Float::NEG_MUL_ADD_RESULT); + assert_biteq!(flt(12.3).mul_add(flt(4.5), flt(6.7)), Float::MUL_ADD_RESULT); + assert_biteq!((flt(-12.3)).mul_add(flt(-4.5), flt(-6.7)), Float::NEG_MUL_ADD_RESULT); assert_biteq!(flt(0.0).mul_add(8.9, 1.2), 1.2); assert_biteq!(flt(3.4).mul_add(-0.0, 5.6), 5.6); assert!(nan.mul_add(7.8, 9.0).is_nan()); @@ -1650,3 +2021,30 @@ float_test! { // assert_biteq!(Float::from(i64::MAX), 9223372036854775807.0); // } // } + +float_test! { + name: real_consts, + attrs: { + // FIXME(f16_f128): add math tests when available + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let pi: Float = consts::PI; + assert_approx_eq!(consts::FRAC_PI_2, pi / 2.0); + assert_approx_eq!(consts::FRAC_PI_3, pi / 3.0, Float::APPROX); + assert_approx_eq!(consts::FRAC_PI_4, pi / 4.0); + assert_approx_eq!(consts::FRAC_PI_6, pi / 6.0); + assert_approx_eq!(consts::FRAC_PI_8, pi / 8.0); + assert_approx_eq!(consts::FRAC_1_PI, 1.0 / pi); + assert_approx_eq!(consts::FRAC_2_PI, 2.0 / pi); + assert_approx_eq!(consts::FRAC_2_SQRT_PI, 2.0 / pi.sqrt()); + assert_approx_eq!(consts::SQRT_2, flt(2.0).sqrt()); + assert_approx_eq!(consts::FRAC_1_SQRT_2, 1.0 / flt(2.0).sqrt()); + assert_approx_eq!(consts::LOG2_E, consts::E.log2()); + assert_approx_eq!(consts::LOG10_E, consts::E.log10()); + assert_approx_eq!(consts::LN_2, flt(2.0).ln()); + assert_approx_eq!(consts::LN_10, flt(10.0).ln(), Float::APPROX); + } +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index b8702ee20cbb1..34732741a21c0 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -52,6 +52,8 @@ #![feature(f16)] #![feature(f128)] #![feature(float_algebraic)] +#![feature(float_bits_const)] +#![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(flt2dec)] #![feature(fmt_internals)] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index f4cc8edc0c1da..1b7a41d697367 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -146,10 +146,6 @@ harness = false name = "sync" path = "tests/sync/lib.rs" -[[test]] -name = "floats" -path = "tests/floats/lib.rs" - [[test]] name = "thread_local" path = "tests/thread_local/lib.rs" diff --git a/library/std/tests/floats/f128.rs b/library/std/tests/floats/f128.rs deleted file mode 100644 index d20762023caf1..0000000000000 --- a/library/std/tests/floats/f128.rs +++ /dev/null @@ -1,320 +0,0 @@ -#![cfg(target_has_reliable_f128)] - -use std::f128::consts; -use std::ops::{Add, Div, Mul, Sub}; - -// Note these tolerances make sense around zero, but not for more extreme exponents. - -/// Default tolerances. Works for values that should be near precise but not exact. Roughly -/// the precision carried by `100 * 100`. -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -const TOL: f128 = 1e-12; - -/// For operations that are near exact, usually not involving math of different -/// signs. -const TOL_PRECISE: f128 = 1e-28; - -/// Tolerances for math that is allowed to be imprecise, usually due to multiple chained -/// operations. -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -const TOL_IMPR: f128 = 1e-10; - -/// Compare by representation -#[allow(unused_macros)] -macro_rules! assert_f128_biteq { - ($a:expr, $b:expr) => { - let (l, r): (&f128, &f128) = (&$a, &$b); - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}"); - }; -} - -#[test] -fn test_num_f128() { - // FIXME(f128): replace with a `test_num` call once the required `fmodl`/`fmodf128` - // function is available on all platforms. - let ten = 10f128; - let two = 2f128; - assert_eq!(ten.add(two), ten + two); - assert_eq!(ten.sub(two), ten - two); - assert_eq!(ten.mul(two), ten * two); - assert_eq!(ten.div(two), ten / two); -} - -// Many math functions allow for less accurate results, so the next tolerance up is used - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_powf() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.powf(1.0), 1.0); - assert_approx_eq!(3.4f128.powf(4.5), 246.40818323761892815995637964326426756, TOL_IMPR); - assert_approx_eq!(2.7f128.powf(-3.2), 0.041652009108526178281070304373500889273, TOL_IMPR); - assert_approx_eq!((-3.1f128).powf(2.0), 9.6100000000000005506706202140776519387, TOL_IMPR); - assert_approx_eq!(5.9f128.powf(-2.0), 0.028727377190462507313100483690639638451, TOL_IMPR); - assert_eq!(8.3f128.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_exp() { - assert_eq!(1.0, 0.0f128.exp()); - assert_approx_eq!(consts::E, 1.0f128.exp(), TOL); - assert_approx_eq!(148.41315910257660342111558004055227962348775, 5.0f128.exp(), TOL); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_exp2() { - assert_eq!(32.0, 5.0f128.exp2()); - assert_eq!(1.0, 0.0f128.exp2()); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_ln() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_approx_eq!(1.0f128.exp().ln(), 1.0, TOL); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f128).ln().is_nan()); - assert_eq!((-0.0f128).ln(), neg_inf); - assert_eq!(0.0f128.ln(), neg_inf); - assert_approx_eq!(4.0f128.ln(), 1.3862943611198906188344642429163531366, TOL); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_log() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(10.0f128.log(10.0), 1.0); - assert_approx_eq!(2.3f128.log(3.5), 0.66485771361478710036766645911922010272, TOL); - assert_eq!(1.0f128.exp().log(1.0f128.exp()), 1.0); - assert!(1.0f128.log(1.0).is_nan()); - assert!(1.0f128.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f128).log(0.1).is_nan()); - assert_eq!((-0.0f128).log(2.0), neg_inf); - assert_eq!(0.0f128.log(7.0), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_log2() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_approx_eq!(10.0f128.log2(), 3.32192809488736234787031942948939017, TOL); - assert_approx_eq!(2.3f128.log2(), 1.2016338611696504130002982471978765921, TOL); - assert_approx_eq!(1.0f128.exp().log2(), 1.4426950408889634073599246810018921381, TOL); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f128).log2().is_nan()); - assert_eq!((-0.0f128).log2(), neg_inf); - assert_eq!(0.0f128.log2(), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_log10() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(10.0f128.log10(), 1.0); - assert_approx_eq!(2.3f128.log10(), 0.36172783601759284532595218865859309898, TOL); - assert_approx_eq!(1.0f128.exp().log10(), 0.43429448190325182765112891891660508222, TOL); - assert_eq!(1.0f128.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f128).log10().is_nan()); - assert_eq!((-0.0f128).log10(), neg_inf); - assert_eq!(0.0f128.log10(), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_asinh() { - // Lower accuracy results are allowed, use increased tolerances - assert_eq!(0.0f128.asinh(), 0.0f128); - assert_eq!((-0.0f128).asinh(), -0.0f128); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f128).asinh().is_sign_negative()); - - // issue 63271 - assert_approx_eq!(2.0f128.asinh(), 1.443635475178810342493276740273105f128, TOL_IMPR); - assert_approx_eq!((-2.0f128).asinh(), -1.443635475178810342493276740273105f128, TOL_IMPR); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!( - (-67452098.07139316f128).asinh(), - -18.720075426274544393985484294000831757220, - TOL_IMPR - ); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f128, 60.0f128.sinh().asinh(), TOL_IMPR); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f128, 1e-15f128.sinh().asinh() * 1e15f128, TOL_IMPR); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_acosh() { - assert_eq!(1.0f128.acosh(), 0.0f128); - assert!(0.999f128.acosh().is_nan()); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f128.acosh(), 1.31695789692481670862504634730796844f128, TOL_IMPR); - assert_approx_eq!(3.0f128.acosh(), 1.76274717403908605046521864995958461f128, TOL_IMPR); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f128, 60.0f128.cosh().acosh(), TOL_IMPR); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_atanh() { - assert_eq!(0.0f128.atanh(), 0.0f128); - assert_eq!((-0.0f128).atanh(), -0.0f128); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(1.0f128.atanh(), inf); - assert_eq!((-1.0f128).atanh(), neg_inf); - assert!(2f128.atanh().atanh().is_nan()); - assert!((-2f128).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f128.atanh(), 0.54930614433405484569762261846126285f128, TOL_IMPR); - assert_approx_eq!((-0.5f128).atanh(), -0.54930614433405484569762261846126285f128, TOL_IMPR); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_gamma() { - // precision can differ among platforms - assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR); - assert_approx_eq!(2.0f128.gamma(), 1.0f128, TOL_IMPR); - assert_approx_eq!(3.0f128.gamma(), 2.0f128, TOL_IMPR); - assert_approx_eq!(4.0f128.gamma(), 6.0f128, TOL_IMPR); - assert_approx_eq!(5.0f128.gamma(), 24.0f128, TOL_IMPR); - assert_approx_eq!(0.5f128.gamma(), consts::PI.sqrt(), TOL_IMPR); - assert_approx_eq!((-0.5f128).gamma(), -2.0 * consts::PI.sqrt(), TOL_IMPR); - assert_eq!(0.0f128.gamma(), f128::INFINITY); - assert_eq!((-0.0f128).gamma(), f128::NEG_INFINITY); - assert!((-1.0f128).gamma().is_nan()); - assert!((-2.0f128).gamma().is_nan()); - assert!(f128::NAN.gamma().is_nan()); - assert!(f128::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f128::INFINITY.gamma(), f128::INFINITY); - assert_eq!(1760.9f128.gamma(), f128::INFINITY); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_ln_gamma() { - assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); - assert_eq!(1.0f128.ln_gamma().1, 1); - assert_approx_eq!(2.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); - assert_eq!(2.0f128.ln_gamma().1, 1); - assert_approx_eq!(3.0f128.ln_gamma().0, 2.0f128.ln(), TOL_IMPR); - assert_eq!(3.0f128.ln_gamma().1, 1); - assert_approx_eq!((-0.5f128).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_IMPR); - assert_eq!((-0.5f128).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - 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; - - assert_approx_eq!(frac_pi_2, pi / 2f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_3, pi / 3f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_4, pi / 4f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_6, pi / 6f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_8, pi / 8f128, TOL_PRECISE); - assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE); - assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE); - - #[cfg(not(miri))] - #[cfg(target_has_reliable_f128_math)] - { - let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; - let sqrt2: f128 = consts::SQRT_2; - let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; - let e: f128 = consts::E; - let log2_e: f128 = consts::LOG2_E; - let log10_e: f128 = consts::LOG10_E; - let ln_2: f128 = consts::LN_2; - let ln_10: f128 = consts::LN_10; - - assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt(), TOL_PRECISE); - assert_approx_eq!(sqrt2, 2f128.sqrt(), TOL_PRECISE); - assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt(), TOL_PRECISE); - assert_approx_eq!(log2_e, e.log2(), TOL_PRECISE); - assert_approx_eq!(log10_e, e.log10(), TOL_PRECISE); - assert_approx_eq!(ln_2, 2f128.ln(), TOL_PRECISE); - assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); - } -} diff --git a/library/std/tests/floats/f16.rs b/library/std/tests/floats/f16.rs deleted file mode 100644 index cc0960765f411..0000000000000 --- a/library/std/tests/floats/f16.rs +++ /dev/null @@ -1,297 +0,0 @@ -#![cfg(target_has_reliable_f16)] - -use std::f16::consts; - -/// Tolerance for results on the order of 10.0e-2 -#[allow(unused)] -const TOL_N2: f16 = 0.0001; - -/// Tolerance for results on the order of 10.0e+0 -#[allow(unused)] -const TOL_0: f16 = 0.01; - -/// Tolerance for results on the order of 10.0e+2 -#[allow(unused)] -const TOL_P2: f16 = 0.5; - -/// Tolerance for results on the order of 10.0e+4 -#[allow(unused)] -const TOL_P4: f16 = 10.0; - -/// Compare by representation -#[allow(unused_macros)] -macro_rules! assert_f16_biteq { - ($a:expr, $b:expr) => { - let (l, r): (&f16, &f16) = (&$a, &$b); - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})"); - }; -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_powf() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(1.0f16.powf(1.0), 1.0); - assert_approx_eq!(3.4f16.powf(4.5), 246.408183, TOL_P2); - assert_approx_eq!(2.7f16.powf(-3.2), 0.041652, TOL_N2); - assert_approx_eq!((-3.1f16).powf(2.0), 9.61, TOL_P2); - assert_approx_eq!(5.9f16.powf(-2.0), 0.028727, TOL_N2); - assert_eq!(8.3f16.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_exp() { - assert_eq!(1.0, 0.0f16.exp()); - assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0); - assert_approx_eq!(148.413159, 5.0f16.exp(), TOL_0); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_exp2() { - assert_eq!(32.0, 5.0f16.exp2()); - assert_eq!(1.0, 0.0f16.exp2()); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_ln() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_approx_eq!(1.0f16.exp().ln(), 1.0, TOL_0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f16).ln().is_nan()); - assert_eq!((-0.0f16).ln(), neg_inf); - assert_eq!(0.0f16.ln(), neg_inf); - assert_approx_eq!(4.0f16.ln(), 1.386294, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_log() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(10.0f16.log(10.0), 1.0); - assert_approx_eq!(2.3f16.log(3.5), 0.664858, TOL_0); - assert_eq!(1.0f16.exp().log(1.0f16.exp()), 1.0); - assert!(1.0f16.log(1.0).is_nan()); - assert!(1.0f16.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f16).log(0.1).is_nan()); - assert_eq!((-0.0f16).log(2.0), neg_inf); - assert_eq!(0.0f16.log(7.0), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_log2() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_approx_eq!(10.0f16.log2(), 3.321928, TOL_0); - assert_approx_eq!(2.3f16.log2(), 1.201634, TOL_0); - assert_approx_eq!(1.0f16.exp().log2(), 1.442695, TOL_0); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f16).log2().is_nan()); - assert_eq!((-0.0f16).log2(), neg_inf); - assert_eq!(0.0f16.log2(), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_log10() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(10.0f16.log10(), 1.0); - assert_approx_eq!(2.3f16.log10(), 0.361728, TOL_0); - assert_approx_eq!(1.0f16.exp().log10(), 0.434294, TOL_0); - assert_eq!(1.0f16.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f16).log10().is_nan()); - assert_eq!((-0.0f16).log10(), neg_inf); - assert_eq!(0.0f16.log10(), neg_inf); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_asinh() { - assert_eq!(0.0f16.asinh(), 0.0f16); - assert_eq!((-0.0f16).asinh(), -0.0f16); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f16).asinh().is_sign_negative()); - // issue 63271 - assert_approx_eq!(2.0f16.asinh(), 1.443635475178810342493276740273105f16, TOL_0); - assert_approx_eq!((-2.0f16).asinh(), -1.443635475178810342493276740273105f16, TOL_0); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-200.0f16).asinh(), -5.991470797049389, TOL_0); - - // test for low accuracy from issue 104548 - assert_approx_eq!(10.0f16, 10.0f16.sinh().asinh(), TOL_0); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f16, 1e-3f16.sinh().asinh() * 1e3f16, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_acosh() { - assert_eq!(1.0f16.acosh(), 0.0f16); - assert!(0.999f16.acosh().is_nan()); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f16.acosh(), 1.31695789692481670862504634730796844f16, TOL_0); - assert_approx_eq!(3.0f16.acosh(), 1.76274717403908605046521864995958461f16, TOL_0); - - // test for low accuracy from issue 104548 - assert_approx_eq!(10.0f16, 10.0f16.cosh().acosh(), TOL_P2); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_atanh() { - assert_eq!(0.0f16.atanh(), 0.0f16); - assert_eq!((-0.0f16).atanh(), -0.0f16); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(1.0f16.atanh(), inf); - assert_eq!((-1.0f16).atanh(), neg_inf); - assert!(2f16.atanh().atanh().is_nan()); - assert!((-2f16).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f16.atanh(), 0.54930614433405484569762261846126285f16, TOL_0); - assert_approx_eq!((-0.5f16).atanh(), -0.54930614433405484569762261846126285f16, TOL_0); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_gamma() { - // precision can differ among platforms - assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0); - assert_approx_eq!(2.0f16.gamma(), 1.0f16, TOL_0); - assert_approx_eq!(3.0f16.gamma(), 2.0f16, TOL_0); - assert_approx_eq!(4.0f16.gamma(), 6.0f16, TOL_0); - assert_approx_eq!(5.0f16.gamma(), 24.0f16, TOL_0); - assert_approx_eq!(0.5f16.gamma(), consts::PI.sqrt(), TOL_0); - assert_approx_eq!((-0.5f16).gamma(), -2.0 * consts::PI.sqrt(), TOL_0); - assert_eq!(0.0f16.gamma(), f16::INFINITY); - assert_eq!((-0.0f16).gamma(), f16::NEG_INFINITY); - assert!((-1.0f16).gamma().is_nan()); - assert!((-2.0f16).gamma().is_nan()); - assert!(f16::NAN.gamma().is_nan()); - assert!(f16::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f16::INFINITY.gamma(), f16::INFINITY); - assert_eq!(171.71f16.gamma(), f16::INFINITY); -} - -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_ln_gamma() { - assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0); - assert_eq!(1.0f16.ln_gamma().1, 1); - assert_approx_eq!(2.0f16.ln_gamma().0, 0.0f16, TOL_0); - assert_eq!(2.0f16.ln_gamma().1, 1); - assert_approx_eq!(3.0f16.ln_gamma().0, 2.0f16.ln(), TOL_0); - assert_eq!(3.0f16.ln_gamma().1, 1); - assert_approx_eq!((-0.5f16).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_0); - assert_eq!((-0.5f16).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - 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; - - assert_approx_eq!(frac_pi_2, pi / 2f16, TOL_0); - assert_approx_eq!(frac_pi_3, pi / 3f16, TOL_0); - assert_approx_eq!(frac_pi_4, pi / 4f16, TOL_0); - assert_approx_eq!(frac_pi_6, pi / 6f16, TOL_0); - assert_approx_eq!(frac_pi_8, pi / 8f16, TOL_0); - assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0); - assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0); - - #[cfg(not(miri))] - #[cfg(target_has_reliable_f16_math)] - { - let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; - let sqrt2: f16 = consts::SQRT_2; - let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; - let e: f16 = consts::E; - let log2_e: f16 = consts::LOG2_E; - let log10_e: f16 = consts::LOG10_E; - let ln_2: f16 = consts::LN_2; - let ln_10: f16 = consts::LN_10; - - assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt(), TOL_0); - assert_approx_eq!(sqrt2, 2f16.sqrt(), TOL_0); - assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt(), TOL_0); - assert_approx_eq!(log2_e, e.log2(), TOL_0); - assert_approx_eq!(log10_e, e.log10(), TOL_0); - assert_approx_eq!(ln_2, 2f16.ln(), TOL_0); - assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); - } -} diff --git a/library/std/tests/floats/f32.rs b/library/std/tests/floats/f32.rs deleted file mode 100644 index 3acd067091415..0000000000000 --- a/library/std/tests/floats/f32.rs +++ /dev/null @@ -1,258 +0,0 @@ -use std::f32::consts; - -/// Miri adds some extra errors to float functions; make sure the tests still pass. -/// These values are purely used as a canary to test against and are thus not a stable guarantee Rust provides. -/// They serve as a way to get an idea of the real precision of floating point operations on different platforms. -const APPROX_DELTA: f32 = if cfg!(miri) { 1e-3 } else { 1e-6 }; - -#[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_powf() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.powf(1.0), 1.0); - assert_approx_eq!(3.4f32.powf(4.5), 246.408218, APPROX_DELTA); - assert_approx_eq!(2.7f32.powf(-3.2), 0.041652); - assert_approx_eq!((-3.1f32).powf(2.0), 9.61, APPROX_DELTA); - assert_approx_eq!(5.9f32.powf(-2.0), 0.028727); - assert_eq!(8.3f32.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - -#[test] -fn test_exp() { - assert_eq!(1.0, 0.0f32.exp()); - assert_approx_eq!(2.718282, 1.0f32.exp(), APPROX_DELTA); - assert_approx_eq!(148.413162, 5.0f32.exp(), APPROX_DELTA); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -fn test_exp2() { - assert_approx_eq!(32.0, 5.0f32.exp2(), APPROX_DELTA); - assert_eq!(1.0, 0.0f32.exp2()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -fn test_ln() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(1.0f32.exp().ln(), 1.0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f32).ln().is_nan()); - assert_eq!((-0.0f32).ln(), neg_inf); - assert_eq!(0.0f32.ln(), neg_inf); - assert_approx_eq!(4.0f32.ln(), 1.386294, APPROX_DELTA); -} - -#[test] -fn test_log() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(10.0f32.log(10.0), 1.0); - assert_approx_eq!(2.3f32.log(3.5), 0.664858); - assert_approx_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0, APPROX_DELTA); - assert!(1.0f32.log(1.0).is_nan()); - assert!(1.0f32.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f32).log(0.1).is_nan()); - assert_eq!((-0.0f32).log(2.0), neg_inf); - assert_eq!(0.0f32.log(7.0), neg_inf); -} - -#[test] -fn test_log2() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(10.0f32.log2(), 3.321928, APPROX_DELTA); - assert_approx_eq!(2.3f32.log2(), 1.201634); - assert_approx_eq!(1.0f32.exp().log2(), 1.442695, APPROX_DELTA); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f32).log2().is_nan()); - assert_eq!((-0.0f32).log2(), neg_inf); - assert_eq!(0.0f32.log2(), neg_inf); -} - -#[test] -fn test_log10() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(10.0f32.log10(), 1.0); - assert_approx_eq!(2.3f32.log10(), 0.361728); - assert_approx_eq!(1.0f32.exp().log10(), 0.434294); - assert_eq!(1.0f32.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f32).log10().is_nan()); - assert_eq!((-0.0f32).log10(), neg_inf); - assert_eq!(0.0f32.log10(), neg_inf); -} - -#[test] -fn test_asinh() { - assert_eq!(0.0f32.asinh(), 0.0f32); - assert_eq!((-0.0f32).asinh(), -0.0f32); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271 - assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); - assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32, APPROX_DELTA); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f32, 60.0f32.sinh().asinh(), APPROX_DELTA); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f32, 1e-15f32.sinh().asinh() * 1e15f32); -} - -#[test] -fn test_acosh() { - assert_eq!(1.0f32.acosh(), 0.0f32); - assert!(0.999f32.acosh().is_nan()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32); - assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f32, 60.0f32.cosh().acosh(), APPROX_DELTA); -} - -#[test] -fn test_atanh() { - assert_eq!(0.0f32.atanh(), 0.0f32); - assert_eq!((-0.0f32).atanh(), -0.0f32); - - let inf32: f32 = f32::INFINITY; - let neg_inf32: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.atanh(), inf32); - assert_eq!((-1.0f32).atanh(), neg_inf32); - - assert!(2f64.atanh().atanh().is_nan()); - assert!((-2f64).atanh().atanh().is_nan()); - - let inf64: f32 = f32::INFINITY; - let neg_inf64: f32 = f32::NEG_INFINITY; - let nan32: f32 = f32::NAN; - assert!(inf64.atanh().is_nan()); - assert!(neg_inf64.atanh().is_nan()); - assert!(nan32.atanh().is_nan()); - - assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32); - assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32); -} - -#[test] -fn test_gamma() { - // precision can differ between platforms - assert_approx_eq!(1.0f32.gamma(), 1.0f32, APPROX_DELTA); - assert_approx_eq!(2.0f32.gamma(), 1.0f32, APPROX_DELTA); - assert_approx_eq!(3.0f32.gamma(), 2.0f32, APPROX_DELTA); - assert_approx_eq!(4.0f32.gamma(), 6.0f32, APPROX_DELTA); - assert_approx_eq!(5.0f32.gamma(), 24.0f32, APPROX_DELTA); - assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt(), APPROX_DELTA); - assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt(), APPROX_DELTA); - assert_eq!(0.0f32.gamma(), f32::INFINITY); - assert_eq!((-0.0f32).gamma(), f32::NEG_INFINITY); - assert!((-1.0f32).gamma().is_nan()); - assert!((-2.0f32).gamma().is_nan()); - assert!(f32::NAN.gamma().is_nan()); - assert!(f32::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f32::INFINITY.gamma(), f32::INFINITY); - assert_eq!(171.71f32.gamma(), f32::INFINITY); -} - -#[test] -fn test_ln_gamma() { - assert_approx_eq!(1.0f32.ln_gamma().0, 0.0f32); - assert_eq!(1.0f32.ln_gamma().1, 1); - assert_approx_eq!(2.0f32.ln_gamma().0, 0.0f32); - assert_eq!(2.0f32.ln_gamma().1, 1); - assert_approx_eq!(3.0f32.ln_gamma().0, 2.0f32.ln()); - assert_eq!(3.0f32.ln_gamma().1, 1); - assert_approx_eq!((-0.5f32).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), APPROX_DELTA); - assert_eq!((-0.5f32).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - let pi: f32 = consts::PI; - let frac_pi_2: f32 = consts::FRAC_PI_2; - let frac_pi_3: f32 = consts::FRAC_PI_3; - let frac_pi_4: f32 = consts::FRAC_PI_4; - let frac_pi_6: f32 = consts::FRAC_PI_6; - let frac_pi_8: f32 = consts::FRAC_PI_8; - let frac_1_pi: f32 = consts::FRAC_1_PI; - let frac_2_pi: f32 = consts::FRAC_2_PI; - let frac_2_sqrtpi: f32 = consts::FRAC_2_SQRT_PI; - let sqrt2: f32 = consts::SQRT_2; - let frac_1_sqrt2: f32 = consts::FRAC_1_SQRT_2; - let e: f32 = consts::E; - let log2_e: f32 = consts::LOG2_E; - let log10_e: f32 = consts::LOG10_E; - let ln_2: f32 = consts::LN_2; - let ln_10: f32 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f32); - assert_approx_eq!(frac_pi_3, pi / 3f32, APPROX_DELTA); - assert_approx_eq!(frac_pi_4, pi / 4f32); - assert_approx_eq!(frac_pi_6, pi / 6f32); - assert_approx_eq!(frac_pi_8, pi / 8f32); - assert_approx_eq!(frac_1_pi, 1f32 / pi); - assert_approx_eq!(frac_2_pi, 2f32 / pi); - assert_approx_eq!(frac_2_sqrtpi, 2f32 / pi.sqrt()); - assert_approx_eq!(sqrt2, 2f32.sqrt()); - assert_approx_eq!(frac_1_sqrt2, 1f32 / 2f32.sqrt()); - assert_approx_eq!(log2_e, e.log2()); - assert_approx_eq!(log10_e, e.log10()); - assert_approx_eq!(ln_2, 2f32.ln()); - assert_approx_eq!(ln_10, 10f32.ln(), APPROX_DELTA); -} diff --git a/library/std/tests/floats/f64.rs b/library/std/tests/floats/f64.rs deleted file mode 100644 index fccf20097278b..0000000000000 --- a/library/std/tests/floats/f64.rs +++ /dev/null @@ -1,249 +0,0 @@ -use std::f64::consts; - -#[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_powf() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.powf(1.0), 1.0); - assert_approx_eq!(3.4f64.powf(4.5), 246.408183); - assert_approx_eq!(2.7f64.powf(-3.2), 0.041652); - assert_approx_eq!((-3.1f64).powf(2.0), 9.61); - assert_approx_eq!(5.9f64.powf(-2.0), 0.028727); - assert_eq!(8.3f64.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - -#[test] -fn test_exp() { - assert_eq!(1.0, 0.0f64.exp()); - assert_approx_eq!(2.718282, 1.0f64.exp()); - assert_approx_eq!(148.413159, 5.0f64.exp()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -fn test_exp2() { - assert_approx_eq!(32.0, 5.0f64.exp2()); - assert_eq!(1.0, 0.0f64.exp2()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -fn test_ln() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(1.0f64.exp().ln(), 1.0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f64).ln().is_nan()); - assert_eq!((-0.0f64).ln(), neg_inf); - assert_eq!(0.0f64.ln(), neg_inf); - assert_approx_eq!(4.0f64.ln(), 1.386294); -} - -#[test] -fn test_log() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(10.0f64.log(10.0), 1.0); - assert_approx_eq!(2.3f64.log(3.5), 0.664858); - assert_approx_eq!(1.0f64.exp().log(1.0f64.exp()), 1.0); - assert!(1.0f64.log(1.0).is_nan()); - assert!(1.0f64.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f64).log(0.1).is_nan()); - assert_eq!((-0.0f64).log(2.0), neg_inf); - assert_eq!(0.0f64.log(7.0), neg_inf); -} - -#[test] -fn test_log2() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(10.0f64.log2(), 3.321928); - assert_approx_eq!(2.3f64.log2(), 1.201634); - assert_approx_eq!(1.0f64.exp().log2(), 1.442695); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f64).log2().is_nan()); - assert_eq!((-0.0f64).log2(), neg_inf); - assert_eq!(0.0f64.log2(), neg_inf); -} - -#[test] -fn test_log10() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(10.0f64.log10(), 1.0); - assert_approx_eq!(2.3f64.log10(), 0.361728); - assert_approx_eq!(1.0f64.exp().log10(), 0.434294); - assert_eq!(1.0f64.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f64).log10().is_nan()); - assert_eq!((-0.0f64).log10(), neg_inf); - assert_eq!(0.0f64.log10(), neg_inf); -} - -#[test] -fn test_asinh() { - assert_eq!(0.0f64.asinh(), 0.0f64); - assert_eq!((-0.0f64).asinh(), -0.0f64); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f64).asinh().is_sign_negative()); - // issue 63271 - assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); - assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f64, 60.0f64.sinh().asinh()); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f64, 1e-15f64.sinh().asinh() * 1e15f64); -} - -#[test] -fn test_acosh() { - assert_eq!(1.0f64.acosh(), 0.0f64); - assert!(0.999f64.acosh().is_nan()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64); - assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f64, 60.0f64.cosh().acosh()); -} - -#[test] -fn test_atanh() { - assert_eq!(0.0f64.atanh(), 0.0f64); - assert_eq!((-0.0f64).atanh(), -0.0f64); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(1.0f64.atanh(), inf); - assert_eq!((-1.0f64).atanh(), neg_inf); - assert!(2f64.atanh().atanh().is_nan()); - assert!((-2f64).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64); - assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64); -} - -#[test] -fn test_gamma() { - // precision can differ between platforms - assert_approx_eq!(1.0f64.gamma(), 1.0f64); - assert_approx_eq!(2.0f64.gamma(), 1.0f64); - assert_approx_eq!(3.0f64.gamma(), 2.0f64); - assert_approx_eq!(4.0f64.gamma(), 6.0f64); - assert_approx_eq!(5.0f64.gamma(), 24.0f64); - assert_approx_eq!(0.5f64.gamma(), consts::PI.sqrt()); - assert_approx_eq!((-0.5f64).gamma(), -2.0 * consts::PI.sqrt()); - assert_eq!(0.0f64.gamma(), f64::INFINITY); - assert_eq!((-0.0f64).gamma(), f64::NEG_INFINITY); - assert!((-1.0f64).gamma().is_nan()); - assert!((-2.0f64).gamma().is_nan()); - assert!(f64::NAN.gamma().is_nan()); - assert!(f64::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f64::INFINITY.gamma(), f64::INFINITY); - assert_eq!(171.71f64.gamma(), f64::INFINITY); -} - -#[test] -fn test_ln_gamma() { - assert_approx_eq!(1.0f64.ln_gamma().0, 0.0f64); - assert_eq!(1.0f64.ln_gamma().1, 1); - assert_approx_eq!(2.0f64.ln_gamma().0, 0.0f64); - assert_eq!(2.0f64.ln_gamma().1, 1); - assert_approx_eq!(3.0f64.ln_gamma().0, 2.0f64.ln()); - assert_eq!(3.0f64.ln_gamma().1, 1); - assert_approx_eq!((-0.5f64).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); - assert_eq!((-0.5f64).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - let pi: f64 = consts::PI; - let frac_pi_2: f64 = consts::FRAC_PI_2; - let frac_pi_3: f64 = consts::FRAC_PI_3; - let frac_pi_4: f64 = consts::FRAC_PI_4; - let frac_pi_6: f64 = consts::FRAC_PI_6; - let frac_pi_8: f64 = consts::FRAC_PI_8; - let frac_1_pi: f64 = consts::FRAC_1_PI; - let frac_2_pi: f64 = consts::FRAC_2_PI; - let frac_2_sqrtpi: f64 = consts::FRAC_2_SQRT_PI; - let sqrt2: f64 = consts::SQRT_2; - let frac_1_sqrt2: f64 = consts::FRAC_1_SQRT_2; - let e: f64 = consts::E; - let log2_e: f64 = consts::LOG2_E; - let log10_e: f64 = consts::LOG10_E; - let ln_2: f64 = consts::LN_2; - let ln_10: f64 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f64); - assert_approx_eq!(frac_pi_3, pi / 3f64); - assert_approx_eq!(frac_pi_4, pi / 4f64); - assert_approx_eq!(frac_pi_6, pi / 6f64); - assert_approx_eq!(frac_pi_8, pi / 8f64); - assert_approx_eq!(frac_1_pi, 1f64 / pi); - assert_approx_eq!(frac_2_pi, 2f64 / pi); - assert_approx_eq!(frac_2_sqrtpi, 2f64 / pi.sqrt()); - assert_approx_eq!(sqrt2, 2f64.sqrt()); - assert_approx_eq!(frac_1_sqrt2, 1f64 / 2f64.sqrt()); - assert_approx_eq!(log2_e, e.log2()); - assert_approx_eq!(log10_e, e.log10()); - assert_approx_eq!(ln_2, 2f64.ln()); - assert_approx_eq!(ln_10, 10f64.ln()); -} diff --git a/library/std/tests/floats/lib.rs b/library/std/tests/floats/lib.rs deleted file mode 100644 index 012349350b0b8..0000000000000 --- a/library/std/tests/floats/lib.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![feature(f16, f128, float_gamma, cfg_target_has_reliable_f16_f128)] -#![expect(internal_features)] // for reliable_f16_f128 - -use std::fmt; -use std::ops::{Add, Div, Mul, Rem, Sub}; - -/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. -macro_rules! assert_approx_eq { - ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; - ($a:expr, $b:expr, $lim:expr) => {{ - let (a, b) = (&$a, &$b); - let diff = (*a - *b).abs(); - assert!( - diff <= $lim, - "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", - lim = $lim - ); - }}; -} - -/// Helper function for testing numeric operations -pub fn test_num(ten: T, two: T) -where - T: PartialEq - + Add - + Sub - + Mul - + Div - + Rem - + fmt::Debug - + Copy, -{ - assert_eq!(ten.add(two), ten + two); - assert_eq!(ten.sub(two), ten - two); - assert_eq!(ten.mul(two), ten * two); - assert_eq!(ten.div(two), ten / two); - assert_eq!(ten.rem(two), ten % two); -} - -mod f128; -mod f16; -mod f32; -mod f64; From 83794755b7ecce9ebcf96321b98db4eca50ea572 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 30 Jan 2026 21:35:07 -0600 Subject: [PATCH 09/13] clif: Only set has_reliable_f128_math with glibc New float tests in core are failing on clif with issues like the following: Undefined symbols for architecture arm64: "_coshf128", referenced from: __RNvMNtCshY0fR2o0hOA_3std4f128C4f1284coshCs5TKtJxXQNGL_9coretests in coretests-e38519c0cc90db54.coretests.44b6247a565e10d1-cgu.10.rcgu.o "_exp2f128", referenced from: __RNvMNtCshY0fR2o0hOA_3std4f128C4f1284exp2Cs5TKtJxXQNGL_9coretests in coretests-e38519c0cc90db54.coretests.44b6247a565e10d1-cgu.10.rcgu.o ... Disable f128 math unless the symbols are known to be available, which for now is only glibc targets. This matches the LLVM backend. --- compiler/rustc_codegen_cranelift/src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 656e7b0aec5b2..7bab07def63d1 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -180,6 +180,10 @@ impl CodegenBackend for CraneliftCodegenBackend { && sess.target.env == Env::Gnu && sess.target.abi != Abi::Llvm); + // FIXME(f128): f128 math operations need f128 math symbols, which currently aren't always + // filled in by compiler-builtins. The only libc that provides these currently is glibc. + let has_reliable_f128_math = has_reliable_f16_f128 && sess.target.env == Env::Gnu; + TargetConfig { target_features, unstable_target_features, @@ -188,7 +192,7 @@ impl CodegenBackend for CraneliftCodegenBackend { has_reliable_f16: has_reliable_f16_f128, has_reliable_f16_math: has_reliable_f16_f128, has_reliable_f128: has_reliable_f16_f128, - has_reliable_f128_math: has_reliable_f16_f128, + has_reliable_f128_math, } } From 981dacc34fe799c4c1643d662819f8fcba76fd0e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 28 Jan 2026 21:03:41 +0100 Subject: [PATCH 10/13] feature-gate c-variadic definitions and calls in const contexts --- .../rustc_ast_passes/src/ast_validation.rs | 7 ++++++ .../src/check_consts/check.rs | 4 ++++ .../rustc_const_eval/src/check_consts/ops.rs | 21 ++++++++++++++++ compiler/rustc_const_eval/src/errors.rs | 13 ++++++++++ compiler/rustc_feature/src/unstable.rs | 2 ++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/ffi/va_list.rs | 6 ++--- tests/ui/consts/const-eval/c-variadic-fail.rs | 2 +- tests/ui/consts/const-eval/c-variadic.rs | 2 +- .../feature-gate-const-c-variadic.rs | 11 +++++++++ .../feature-gate-const-c-variadic.stderr | 24 +++++++++++++++++++ 11 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-const-c-variadic.rs create mode 100644 tests/ui/feature-gates/feature-gate-const-c-variadic.stderr diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index f9a64ffe09c42..ac2ba6b2b92a7 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -698,6 +698,13 @@ impl<'a> AstValidator<'a> { unreachable!("C variable argument list cannot be used in closures") }; + if let Const::Yes(_) = sig.header.constness + && !self.features.enabled(sym::const_c_variadic) + { + let msg = format!("c-variadic const function definitions are unstable"); + feature_err(&self.sess, sym::const_c_variadic, sig.span, msg).emit(); + } + if let Some(coroutine_kind) = sig.header.coroutine_kind { self.dcx().emit_err(errors::CoroutineAndCVariadic { spans: vec![coroutine_kind.span(), variadic_param.span], diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 57396b657a3fa..4ce0345f2e61c 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -815,6 +815,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { }); } + if self.tcx.fn_sig(callee).skip_binder().c_variadic() { + self.check_op(ops::FnCallCVariadic) + } + // At this point, we are calling a function, `callee`, whose `DefId` is known... // `begin_panic` and `panic_display` functions accept generic diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 5b62ba8c1605b..be8f0f430e13f 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -75,6 +75,27 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect { } } +/// A c-variadic function call. +#[derive(Debug)] +pub(crate) struct FnCallCVariadic; +impl<'tcx> NonConstOp<'tcx> for FnCallCVariadic { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { + Status::Unstable { + gate: sym::const_c_variadic, + gate_already_checked: false, + safe_to_expose_on_stable: false, + is_function_call: true, + } + } + + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { + ccx.tcx.sess.create_feature_err( + errors::NonConstCVariadicCall { span, kind: ccx.const_kind() }, + sym::const_c_variadic, + ) + } +} + /// A call to a function that is in a trait, or has trait bounds that make it conditionally-const. #[derive(Debug)] pub(crate) struct ConditionallyConstCall<'tcx> { diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 061ed38c9df1d..886995aa3a407 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -531,6 +531,19 @@ pub struct NonConstClosure { pub non_or_conditionally: &'static str, } +#[derive(Diagnostic)] +#[diag(r#"calling const c-variadic functions is unstable in {$kind -> + [const] constant + [static] static + [const_fn] constant function + *[other] {""} +}s"#, code = E0015)] +pub struct NonConstCVariadicCall { + #[primary_span] + pub span: Span, + pub kind: ConstContext, +} + #[derive(Subdiagnostic)] pub enum NonConstClosureNote { #[note("function defined here, but it is not `const`")] diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 082558cf9a93f..36be4a0378060 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -420,6 +420,8 @@ declare_features! ( (unstable, const_async_blocks, "1.53.0", Some(85368)), /// Allows `const { ... }` as a shorthand for `const _: () = const { ... };` for module items. (unstable, const_block_items, "CURRENT_RUSTC_VERSION", Some(149226)), + /// Allows defining and calling c-variadic functions in const contexts. + (unstable, const_c_variadic, "CURRENT_RUSTC_VERSION", Some(151787)), /// Allows `const || {}` closures in const contexts. (incomplete, const_closures, "1.68.0", Some(106003)), /// Allows using `[const] Destruct` bounds and calling drop impls in const contexts. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b9a9d8029d286..a39f01c8e70bd 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -740,6 +740,7 @@ symbols! { const_allocate, const_async_blocks, const_block_items, + const_c_variadic, const_closures, const_compare_raw_pointers, const_constructor, diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 45a9b7ba5293e..4ed93f54d43c3 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -205,7 +205,7 @@ impl VaList<'_> { } } -#[rustc_const_unstable(feature = "c_variadic_const", issue = "none")] +#[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")] impl<'f> const Clone for VaList<'f> { #[inline] fn clone(&self) -> Self { @@ -217,7 +217,7 @@ impl<'f> const Clone for VaList<'f> { } } -#[rustc_const_unstable(feature = "c_variadic_const", issue = "none")] +#[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")] impl<'f> const Drop for VaList<'f> { fn drop(&mut self) { // SAFETY: this variable argument list is being dropped, so won't be read from again. @@ -293,7 +293,7 @@ impl<'f> VaList<'f> { /// /// [valid]: https://doc.rust-lang.org/nightly/nomicon/what-unsafe-does.html #[inline] - #[rustc_const_unstable(feature = "c_variadic_const", issue = "none")] + #[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")] pub const unsafe fn arg(&mut self) -> T { // SAFETY: the caller must uphold the safety contract for `va_arg`. unsafe { va_arg(self) } diff --git a/tests/ui/consts/const-eval/c-variadic-fail.rs b/tests/ui/consts/const-eval/c-variadic-fail.rs index 859dfed35719f..b5a400a8bf255 100644 --- a/tests/ui/consts/const-eval/c-variadic-fail.rs +++ b/tests/ui/consts/const-eval/c-variadic-fail.rs @@ -1,7 +1,7 @@ //@ build-fail #![feature(c_variadic)] -#![feature(c_variadic_const)] +#![feature(const_c_variadic)] #![feature(const_trait_impl)] #![feature(const_destruct)] #![feature(const_clone)] diff --git a/tests/ui/consts/const-eval/c-variadic.rs b/tests/ui/consts/const-eval/c-variadic.rs index ec49b5cc5831d..2f8d043fb5db6 100644 --- a/tests/ui/consts/const-eval/c-variadic.rs +++ b/tests/ui/consts/const-eval/c-variadic.rs @@ -3,8 +3,8 @@ //@ ignore-backends: gcc #![feature(c_variadic)] +#![feature(const_c_variadic)] #![feature(const_destruct)] -#![feature(c_variadic_const)] #![feature(const_cmp)] #![feature(const_trait_impl)] diff --git a/tests/ui/feature-gates/feature-gate-const-c-variadic.rs b/tests/ui/feature-gates/feature-gate-const-c-variadic.rs new file mode 100644 index 0000000000000..4e8b3c54b1fd1 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-const-c-variadic.rs @@ -0,0 +1,11 @@ +#![feature(c_variadic)] + +fn main() { + const unsafe extern "C" fn foo(ap: ...) { + //~^ ERROR c-variadic const function definitions are unstable + core::mem::forget(ap); + } + + const { unsafe { foo() } } + //~^ ERROR calling const c-variadic functions is unstable in constants +} diff --git a/tests/ui/feature-gates/feature-gate-const-c-variadic.stderr b/tests/ui/feature-gates/feature-gate-const-c-variadic.stderr new file mode 100644 index 0000000000000..8fd85be08fca4 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-const-c-variadic.stderr @@ -0,0 +1,24 @@ +error[E0658]: c-variadic const function definitions are unstable + --> $DIR/feature-gate-const-c-variadic.rs:4:5 + | +LL | const unsafe extern "C" fn foo(ap: ...) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151787 for more information + = help: add `#![feature(const_c_variadic)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0015]: calling const c-variadic functions is unstable in constants + --> $DIR/feature-gate-const-c-variadic.rs:9:22 + | +LL | const { unsafe { foo() } } + | ^^^^^ + | + = note: see issue #151787 for more information + = help: add `#![feature(const_c_variadic)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0015, E0658. +For more information about an error, try `rustc --explain E0015`. From f1664acfbfe7335bfb59cdebf940342a6157ea9d Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 15 Feb 2026 21:16:35 +1100 Subject: [PATCH 11/13] Inline the `is_tool` check for setting `-Zforce-unstable-if-unmarked` --- src/bootstrap/src/core/builder/cargo.rs | 14 ++++++++++++-- src/bootstrap/src/lib.rs | 7 ------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index e3fa826c45af3..7f4e23881e455 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -999,8 +999,18 @@ impl Builder<'_> { cargo.env("UPDATE_EXPECT", "1"); } - if !mode.is_tool() { - cargo.env("RUSTC_FORCE_UNSTABLE", "1"); + // Set an environment variable that tells the rustc/rustdoc wrapper + // binary to pass `-Zforce-unstable-if-unmarked` to the real compiler. + match mode { + // Any library crate that's part of the sysroot should be marked unstable + // (including third-party dependencies), unless it uses a staged_api + // `#![stable(..)]` attribute to explicitly mark itself stable. + Mode::Std | Mode::Codegen | Mode::Rustc => { + cargo.env("RUSTC_FORCE_UNSTABLE", "1"); + } + + // For everything else, crate stability shouldn't matter, so don't set a flag. + Mode::ToolBootstrap | Mode::ToolRustcPrivate | Mode::ToolStd | Mode::ToolTarget => {} } if let Some(x) = self.crt_static(target) { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index dfa29b5aa3cd3..ec5c6fbe1d01d 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -340,13 +340,6 @@ pub enum Mode { } impl Mode { - pub fn is_tool(&self) -> bool { - match self { - Mode::ToolBootstrap | Mode::ToolRustcPrivate | Mode::ToolStd | Mode::ToolTarget => true, - Mode::Std | Mode::Codegen | Mode::Rustc => false, - } - } - pub fn must_support_dlopen(&self) -> bool { match self { Mode::Std | Mode::Codegen => true, From 47ca78040a6e37e4a2688f8c5a72c631e8e89c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sat, 7 Feb 2026 15:21:35 +0100 Subject: [PATCH 12/13] Port rustc_do_not_const_check to the new attribute parser --- .../src/attributes/rustc_internal.rs | 15 +++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 1 + .../rustc_const_eval/src/check_consts/check.rs | 5 +++-- .../src/check_consts/post_drop_elaboration.rs | 4 +++- .../rustc_const_eval/src/const_eval/machine.rs | 7 +++++-- compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir_typeck/src/callee.rs | 4 +++- compiler/rustc_passes/src/check_attr.rs | 2 +- 9 files changed, 35 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 2c94a708a90c8..40ab0e6167523 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1178,6 +1178,21 @@ impl SingleAttributeParser for RustcDiagnosticItemParser { } } +pub(crate) struct RustcDoNotConstCheckParser; + +impl NoArgsAttributeParser for RustcDoNotConstCheckParser { + const PATH: &[Symbol] = &[sym::rustc_do_not_const_check]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDoNotConstCheck; +} + pub(crate) struct RustcSymbolName; impl SingleAttributeParser for RustcSymbolName { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 17a92641f10f4..7ad74761cf707 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -274,6 +274,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 57396b657a3fa..41c4274ce4f93 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -7,9 +7,10 @@ use std::ops::Deref; use rustc_data_structures::assert_matches; use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::visit::Visitor; @@ -215,7 +216,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { return; } - if !tcx.has_attr(def_id, sym::rustc_do_not_const_check) { + if !find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcDoNotConstCheck) { self.visit_body(body); } diff --git a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs index 16e142a85ee6f..6189d9321dc09 100644 --- a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs @@ -1,3 +1,5 @@ +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, BasicBlock, Location}; use rustc_middle::ty::TyCtxt; @@ -34,7 +36,7 @@ pub fn check_live_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>) { return; } - if tcx.has_attr(body.source.def_id(), sym::rustc_do_not_const_check) { + if find_attr!(tcx.get_all_attrs(body.source.def_id()), AttributeKind::RustcDoNotConstCheck) { return; } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index e017362b8c4b5..1c58745e02eb4 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -6,8 +6,9 @@ use rustc_abi::{Align, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_errors::msg; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem}; +use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem, find_attr}; use rustc_middle::mir::AssertMessage; use rustc_middle::mir::interpret::{Pointer, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; @@ -440,7 +441,9 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // sensitive check here. But we can at least rule out functions that are not const at // all. That said, we have to allow calling functions inside a `const trait`. These // *are* const-checked! - if !ecx.tcx.is_const_fn(def) || ecx.tcx.has_attr(def, sym::rustc_do_not_const_check) { + if !ecx.tcx.is_const_fn(def) + || find_attr!(ecx.tcx.get_all_attrs(def), AttributeKind::RustcDoNotConstCheck) + { // We certainly do *not* want to actually call the fn // though, so be sure we return here. throw_unsup_format!("calling non-const function `{}`", instance) diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 3798a586c0517..2a11518aaa790 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1163,6 +1163,9 @@ pub enum AttributeKind { /// Represents `#[rustc_diagnostic_item]` RustcDiagnosticItem(Symbol), + /// Represents `#[rustc_do_not_const_check]` + RustcDoNotConstCheck, + /// Represents `#[rustc_dummy]`. RustcDummy, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index d8b8ca90660a0..8fe70efc23d67 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -115,6 +115,7 @@ impl AttributeKind { RustcDenyExplicitImpl(..) => No, RustcDeprecatedSafe2024 { .. } => Yes, RustcDiagnosticItem(..) => Yes, + RustcDoNotConstCheck => Yes, RustcDummy => No, RustcDumpDefParents => No, RustcDumpItemBounds => No, diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 7ad0b85ee53f9..77ef8a83bce18 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -914,7 +914,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // If we have `rustc_do_not_const_check`, do not check `[const]` bounds. - if self.has_rustc_attrs && self.tcx.has_attr(self.body_id, sym::rustc_do_not_const_check) { + if self.has_rustc_attrs + && find_attr!(self.tcx.get_all_attrs(self.body_id), AttributeKind::RustcDoNotConstCheck) + { return; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 33d45b648cb81..5059c7c1c0be1 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -310,6 +310,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcDenyExplicitImpl(..) | AttributeKind::RustcDeprecatedSafe2024 {..} | AttributeKind::RustcDiagnosticItem(..) + | AttributeKind::RustcDoNotConstCheck | AttributeKind::RustcDummy | AttributeKind::RustcDumpDefParents | AttributeKind::RustcDumpItemBounds @@ -401,7 +402,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_nonnull_optimization_guaranteed | sym::rustc_inherit_overflow_checks | sym::rustc_on_unimplemented - | sym::rustc_do_not_const_check | sym::rustc_doc_primitive | sym::rustc_layout | sym::rustc_autodiff From 12e66289778bd712d9fe6268b130b45861d7a58f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sat, 7 Feb 2026 16:01:11 +0100 Subject: [PATCH 13/13] Port rustc_nonnull_optimization_guaranteed to the new attribute parser --- .../rustc_attr_parsing/src/attributes/rustc_internal.rs | 9 +++++++++ compiler/rustc_attr_parsing/src/context.rs | 1 + compiler/rustc_const_eval/src/interpret/call.rs | 8 ++++++-- compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_lint/src/types.rs | 5 +++-- compiler/rustc_passes/src/check_attr.rs | 2 +- tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs | 4 +++- 8 files changed, 27 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 40ab0e6167523..fa0611bd8358f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1193,6 +1193,15 @@ impl NoArgsAttributeParser for RustcDoNotConstCheckParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDoNotConstCheck; } +pub(crate) struct RustcNonnullOptimizationGuaranteedParser; + +impl NoArgsAttributeParser for RustcNonnullOptimizationGuaranteedParser { + const PATH: &[Symbol] = &[sym::rustc_nonnull_optimization_guaranteed]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNonnullOptimizationGuaranteed; +} + pub(crate) struct RustcSymbolName; impl SingleAttributeParser for RustcSymbolName { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 7ad74761cf707..1bb6c8aa1866c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -296,6 +296,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index b17740f57f786..7076aa94fc57d 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -7,11 +7,12 @@ use either::{Left, Right}; use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx}; use rustc_data_structures::assert_matches; use rustc_errors::msg; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; +use rustc_hir::find_attr; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef}; use rustc_middle::{bug, mir, span_bug}; -use rustc_span::sym; use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; use tracing::field::Empty; use tracing::{info, instrument, trace}; @@ -145,7 +146,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Check if the inner type is one of the NPO-guaranteed ones. // For that we first unpeel transparent *structs* (but not unions). let is_npo = |def: AdtDef<'tcx>| { - self.tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed) + find_attr!( + self.tcx.get_all_attrs(def.did()), + AttributeKind::RustcNonnullOptimizationGuaranteed + ) }; let inner = self.unfold_transparent(inner, /* may_unfold */ |def| { // Stop at NPO types so that we don't miss that attribute in the check below! diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 2a11518aaa790..41970fb7d35a1 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1268,6 +1268,9 @@ pub enum AttributeKind { /// Represents `#[rustc_non_const_trait_method]`. RustcNonConstTraitMethod, + /// Represents `#[rustc_nonnull_optimization_guaranteed]`. + RustcNonnullOptimizationGuaranteed, + /// Represents `#[rustc_nounwind]` RustcNounwind, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 8fe70efc23d67..c50a5c649ee4b 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -149,6 +149,7 @@ impl AttributeKind { RustcNoImplicitBounds => No, RustcNoMirInline => Yes, RustcNonConstTraitMethod => No, // should be reported via other queries like `constness` + RustcNonnullOptimizationGuaranteed => Yes, RustcNounwind => No, RustcObjcClass { .. } => No, RustcObjcSelector { .. } => No, diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 47e1fef8b82ea..b5126fffc1c5d 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,7 +1,8 @@ use std::iter; use rustc_abi::{BackendRepr, TagEncoding, Variants, WrappingRange}; -use rustc_hir::{Expr, ExprKind, HirId, LangItem}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, find_attr}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; @@ -686,7 +687,7 @@ pub(crate) fn nonnull_optimization_guaranteed<'tcx>( tcx: TyCtxt<'tcx>, def: ty::AdtDef<'tcx>, ) -> bool { - tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed) + find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::RustcNonnullOptimizationGuaranteed) } /// `repr(transparent)` structs can have a single non-1-ZST field, this function returns that diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 5059c7c1c0be1..ec0306371205b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -342,6 +342,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcNoImplicitBounds | AttributeKind::RustcNoMirInline | AttributeKind::RustcNonConstTraitMethod + | AttributeKind::RustcNonnullOptimizationGuaranteed | AttributeKind::RustcNounwind | AttributeKind::RustcObjcClass { .. } | AttributeKind::RustcObjcSelector { .. } @@ -399,7 +400,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::forbid // internal | sym::default_lib_allocator - | sym::rustc_nonnull_optimization_guaranteed | sym::rustc_inherit_overflow_checks | sym::rustc_on_unimplemented | sym::rustc_doc_primitive diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs index f3d5bcbd8d781..beddfd87a5e7e 100644 --- a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs +++ b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs @@ -5,10 +5,12 @@ //~| NOTE the `#[rustc_nonnull_optimization_guaranteed]` attribute is an internal implementation detail that will never be stable //~| NOTE the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in the standard library //~| NOTE the compiler does not even check whether the type indeed is being non-null-optimized; it is your responsibility to ensure that the attribute is only used on types that are optimized -fn main() {} +struct Foo {} #[rustc_variance] //~^ ERROR use of an internal attribute [E0658] //~| NOTE the `#[rustc_variance]` attribute is an internal implementation detail that will never be stable //~| NOTE the `#[rustc_variance]` attribute is used for rustc unit tests enum E {} + +fn main() {}