Auto merge of #152701 - jhpratt:rollup-clRaY9x, r=jhpratt
Rollup of 6 pull requests Successful merges: - rust-lang/rust#148206 (Deduplicated float tests and unified in floats/mod.rs) - rust-lang/rust#150601 (support c-variadic functions in `rustc_const_eval`) - rust-lang/rust#152103 (Consider captures to be used by closures that unwind) - rust-lang/rust#152296 (Port `rust_nonnull_optimization_guaranteed` and `rustc_do_not_const_check` to the new attribute parser) - rust-lang/rust#152648 (Remove timing assertion from `oneshot::send_before_recv_timeout`) - rust-lang/rust#152686 (bootstrap: Inline the `is_tool` check for setting `-Zforce-unstable-if-unmarked`) Failed merges: - rust-lang/rust#152512 (core: Implement feature `float_exact_integer_constants`)
This commit is contained in:
commit
71e00273c0
57 changed files with 2117 additions and 1443 deletions
|
|
@ -698,13 +698,11 @@ 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 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 {
|
||||
|
|
|
|||
|
|
@ -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<Span>,
|
||||
#[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 {
|
||||
|
|
|
|||
|
|
@ -1178,6 +1178,30 @@ impl<S: Stage> SingleAttributeParser<S> for RustcDiagnosticItemParser {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcDoNotConstCheckParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcDoNotConstCheckParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_do_not_const_check];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = 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 RustcNonnullOptimizationGuaranteedParser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for RustcNonnullOptimizationGuaranteedParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_nonnull_optimization_guaranteed];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
|
||||
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNonnullOptimizationGuaranteed;
|
||||
}
|
||||
|
||||
pub(crate) struct RustcSymbolName;
|
||||
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcSymbolName {
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ attribute_parsers!(
|
|||
Single<WithoutArgs<RustcConversionSuggestionParser>>,
|
||||
Single<WithoutArgs<RustcDeallocatorParser>>,
|
||||
Single<WithoutArgs<RustcDelayedBugFromInsideQueryParser>>,
|
||||
Single<WithoutArgs<RustcDoNotConstCheckParser>>,
|
||||
Single<WithoutArgs<RustcDumpDefParentsParser>>,
|
||||
Single<WithoutArgs<RustcDumpItemBoundsParser>>,
|
||||
Single<WithoutArgs<RustcDumpPredicatesParser>>,
|
||||
|
|
@ -295,6 +296,7 @@ attribute_parsers!(
|
|||
Single<WithoutArgs<RustcNoImplicitBoundsParser>>,
|
||||
Single<WithoutArgs<RustcNoMirInlineParser>>,
|
||||
Single<WithoutArgs<RustcNonConstTraitMethodParser>>,
|
||||
Single<WithoutArgs<RustcNonnullOptimizationGuaranteedParser>>,
|
||||
Single<WithoutArgs<RustcNounwindParser>>,
|
||||
Single<WithoutArgs<RustcObjectLifetimeDefaultParser>>,
|
||||
Single<WithoutArgs<RustcOffloadKernelParser>>,
|
||||
|
|
|
|||
|
|
@ -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<Float> {
|
||||
@@ -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<Float> {
|
||||
@@ -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<Float> {
|
||||
@@ -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<Float> {
|
||||
@@ -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<Float> {
|
||||
@@ -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<Float> {
|
||||
@@ -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<Float> {
|
||||
@@ -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<Float> {
|
||||
@@ -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<Float> {
|
||||
@@ -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<Float> {
|
||||
@@ -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<Float> {
|
||||
@@ -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<Float> {
|
||||
@@ -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<Float> {
|
||||
--
|
||||
2.50.1
|
||||
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -815,6 +816,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
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ 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::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 +23,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,
|
||||
};
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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`")]
|
||||
|
|
@ -757,11 +770,13 @@ 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}"),
|
||||
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}"),
|
||||
|
|
@ -776,6 +791,9 @@ 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}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -800,6 +818,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
| InvalidMeta(InvalidMetaKind::TooBig)
|
||||
| InvalidUninitBytes(None)
|
||||
| DeadLocal
|
||||
| VaArgOutOfBounds
|
||||
| UninhabitedEnumVariantWritten(_)
|
||||
| UninhabitedEnumVariantRead(_) => {}
|
||||
|
||||
|
|
@ -820,7 +839,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 } => {
|
||||
|
|
@ -874,6 +896,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
WriteToReadOnly(alloc)
|
||||
| DerefFunctionPointer(alloc)
|
||||
| DerefVTablePointer(alloc)
|
||||
| DerefVaListPointer(alloc)
|
||||
| DerefTypeIdPointer(alloc) => {
|
||||
diag.arg("allocation", alloc);
|
||||
}
|
||||
|
|
@ -910,6 +933,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
@ -19,7 +20,7 @@ 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,
|
||||
throw_ub, throw_ub_custom,
|
||||
};
|
||||
use crate::enter_trace_span;
|
||||
use crate::interpret::EnteredTraceSpan;
|
||||
|
|
@ -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!
|
||||
|
|
@ -354,13 +358,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);
|
||||
|
||||
// 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())?;
|
||||
|
||||
if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic {
|
||||
throw_unsup_format!("calling a c-variadic function is not supported");
|
||||
}
|
||||
// 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 {
|
||||
ty::List::empty()
|
||||
};
|
||||
let callee_fn_abi = self.fn_abi_of_instance(instance, extra_tys)?;
|
||||
|
||||
if caller_fn_abi.conv != callee_fn_abi.conv {
|
||||
throw_ub_custom!(
|
||||
|
|
@ -372,6 +381,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.
|
||||
|
|
@ -444,6 +466,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// `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();
|
||||
// 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`.
|
||||
|
|
@ -452,7 +478,31 @@ 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 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 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());
|
||||
|
||||
// Zero the VaList, so it is fully initialized.
|
||||
self.write_bytes_ptr(
|
||||
mplace.ptr(),
|
||||
(0..mplace.layout.size.bytes()).map(|_| 0u8),
|
||||
)?;
|
||||
|
||||
// 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)?;
|
||||
// Must be a tuple
|
||||
|
|
@ -491,7 +541,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"
|
||||
|
|
|
|||
|
|
@ -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_custom, throw_ub_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;
|
||||
|
||||
|
|
@ -750,6 +750,57 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.float_muladd_intrinsic::<Quad>(args, dest, MulAddType::Nondeterministic)?
|
||||
}
|
||||
|
||||
sym::va_copy => {
|
||||
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 varargs = self.get_ptr_va_list(key)?;
|
||||
let copy_key = self.va_list_ptr(varargs.clone());
|
||||
|
||||
let copy_key_mplace = self.va_list_key_field(dest)?;
|
||||
self.write_pointer(copy_key, ©_key_mplace)?;
|
||||
}
|
||||
|
||||
sym::va_end => {
|
||||
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)?;
|
||||
|
||||
self.deallocate_va_list(key)?;
|
||||
}
|
||||
|
||||
sym::va_arg => {
|
||||
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)?;
|
||||
|
||||
// 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(arg_mplace) = varargs.pop_front() else {
|
||||
throw_ub!(VaArgOutOfBounds);
|
||||
};
|
||||
|
||||
// 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 arg_mplace.layout.ty != dest.layout.ty {
|
||||
throw_unsup_format!(
|
||||
"va_arg type mismatch: requested `{}`, but next argument is `{}`",
|
||||
dest.layout.ty,
|
||||
arg_mplace.layout.ty
|
||||
);
|
||||
}
|
||||
// Copy the argument.
|
||||
self.copy_op(&arg_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.
|
||||
_ => return interp_ok(false),
|
||||
}
|
||||
|
|
@ -1230,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<P: Projectable<'tcx, M::Provenance>>(
|
||||
&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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<AllocId, M::ExtraFnVal>,
|
||||
|
||||
/// Map storing variable argument lists.
|
||||
va_list_map: FxIndexMap<AllocId, VecDeque<MPlaceTy<'tcx, M::Provenance>>>,
|
||||
|
||||
/// 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,19 @@ 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: VecDeque<MPlaceTy<'tcx, M::Provenance>>,
|
||||
) -> Pointer<M::Provenance> {
|
||||
let id = self.tcx.reserve_alloc_id();
|
||||
let old = self.memory.va_list_map.insert(id, varargs);
|
||||
assert!(old.is_none());
|
||||
// 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(Pointer::from(id)).unwrap()
|
||||
}
|
||||
|
||||
pub fn allocate_ptr(
|
||||
&mut self,
|
||||
size: Size,
|
||||
|
|
@ -956,6 +977,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 +1017,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
return AllocInfo::new(Size::ZERO, align, AllocKind::Function, Mutability::Not);
|
||||
}
|
||||
|
||||
// # Variable argument lists
|
||||
if self.memory.va_list_map.contains_key(&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.
|
||||
|
|
@ -1069,6 +1096,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
.into()
|
||||
}
|
||||
|
||||
pub fn get_ptr_va_list(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
) -> InterpResult<'tcx, &VecDeque<MPlaceTy<'tcx, M::Provenance>>> {
|
||||
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<Option<M::Provenance>>,
|
||||
) -> InterpResult<'tcx, VecDeque<MPlaceTy<'tcx, M::Provenance>>> {
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -12,13 +12,14 @@ 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, Machine, MemPlace, MemPlaceMeta,
|
||||
MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout,
|
||||
interp_ok, throw_ub, throw_unsup,
|
||||
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,
|
||||
};
|
||||
use crate::{enter_trace_span, errors};
|
||||
|
||||
|
|
@ -91,6 +92,10 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> {
|
|||
/// Do *not* access this directly; always go through the machine hook!
|
||||
pub locals: IndexVec<mir::Local, LocalState<'tcx, Prov>>,
|
||||
|
||||
/// The complete variable argument list of this frame. Its elements must be dropped when the
|
||||
/// frame is popped.
|
||||
pub(super) va_list: Vec<MPlaceTy<'tcx, Prov>>,
|
||||
|
||||
/// 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 +264,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 +383,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: (),
|
||||
|
|
@ -449,11 +456,13 @@ 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.
|
||||
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)?;
|
||||
assert_ne!(return_action, ReturnAction::NoCleanup);
|
||||
|
|
@ -626,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<I, J>(
|
||||
&mut self,
|
||||
caller_args: &mut I,
|
||||
callee_abis: &mut J,
|
||||
) -> InterpResult<'tcx, Vec<MPlaceTy<'tcx, M::Provenance>>>
|
||||
where
|
||||
I: Iterator<Item = (&'a FnArg<'tcx, M::Provenance>, &'a ArgAbi<'tcx, Ty<'tcx>>)>,
|
||||
J: Iterator<Item = (usize, &'a ArgAbi<'tcx, Ty<'tcx>>)>,
|
||||
{
|
||||
// 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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -1163,6 +1163,9 @@ pub enum AttributeKind {
|
|||
/// Represents `#[rustc_diagnostic_item]`
|
||||
RustcDiagnosticItem(Symbol),
|
||||
|
||||
/// Represents `#[rustc_do_not_const_check]`
|
||||
RustcDoNotConstCheck,
|
||||
|
||||
/// Represents `#[rustc_dummy]`.
|
||||
RustcDummy,
|
||||
|
||||
|
|
@ -1265,6 +1268,9 @@ pub enum AttributeKind {
|
|||
/// Represents `#[rustc_non_const_trait_method]`.
|
||||
RustcNonConstTraitMethod,
|
||||
|
||||
/// Represents `#[rustc_nonnull_optimization_guaranteed]`.
|
||||
RustcNonnullOptimizationGuaranteed,
|
||||
|
||||
/// Represents `#[rustc_nounwind]`
|
||||
RustcNounwind,
|
||||
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ impl AttributeKind {
|
|||
RustcDenyExplicitImpl(..) => No,
|
||||
RustcDeprecatedSafe2024 { .. } => Yes,
|
||||
RustcDiagnosticItem(..) => Yes,
|
||||
RustcDoNotConstCheck => Yes,
|
||||
RustcDummy => No,
|
||||
RustcDumpDefParents => No,
|
||||
RustcDumpItemBounds => No,
|
||||
|
|
@ -148,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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -402,6 +404,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
|||
InvalidTag(Scalar<AllocId>),
|
||||
/// Using a pointer-not-to-a-function as function pointer.
|
||||
InvalidFunctionPointer(Pointer<AllocId>),
|
||||
/// Using a pointer-not-to-a-va-list as variable argument list pointer.
|
||||
InvalidVaListPointer(Pointer<AllocId>),
|
||||
/// Using a pointer-not-to-a-vtable as vtable pointer.
|
||||
InvalidVTablePointer(Pointer<AllocId>),
|
||||
/// Using a vtable for the wrong trait.
|
||||
|
|
@ -434,6 +438,12 @@ 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,
|
||||
/// 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)]
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -310,6 +310,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
| AttributeKind::RustcDenyExplicitImpl(..)
|
||||
| AttributeKind::RustcDeprecatedSafe2024 {..}
|
||||
| AttributeKind::RustcDiagnosticItem(..)
|
||||
| AttributeKind::RustcDoNotConstCheck
|
||||
| AttributeKind::RustcDummy
|
||||
| AttributeKind::RustcDumpDefParents
|
||||
| AttributeKind::RustcDumpItemBounds
|
||||
|
|
@ -341,6 +342,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
| AttributeKind::RustcNoImplicitBounds
|
||||
| AttributeKind::RustcNoMirInline
|
||||
| AttributeKind::RustcNonConstTraitMethod
|
||||
| AttributeKind::RustcNonnullOptimizationGuaranteed
|
||||
| AttributeKind::RustcNounwind
|
||||
| AttributeKind::RustcObjcClass { .. }
|
||||
| AttributeKind::RustcObjcSelector { .. }
|
||||
|
|
@ -398,10 +400,8 @@ 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_do_not_const_check
|
||||
| sym::rustc_doc_primitive
|
||||
| sym::rustc_layout
|
||||
| sym::rustc_autodiff
|
||||
|
|
|
|||
|
|
@ -742,6 +742,7 @@ symbols! {
|
|||
const_allocate,
|
||||
const_async_blocks,
|
||||
const_block_items,
|
||||
const_c_variadic,
|
||||
const_closures,
|
||||
const_compare_raw_pointers,
|
||||
const_constructor,
|
||||
|
|
|
|||
|
|
@ -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 = "const_c_variadic", issue = "151787")]
|
||||
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
|
||||
|
|
@ -216,7 +217,8 @@ impl Clone for VaList<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'f> Drop for VaList<'f> {
|
||||
#[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.
|
||||
unsafe { va_end(self) }
|
||||
|
|
@ -291,7 +293,8 @@ impl<'f> VaList<'f> {
|
|||
///
|
||||
/// [valid]: https://doc.rust-lang.org/nightly/nomicon/what-unsafe-does.html
|
||||
#[inline]
|
||||
pub unsafe fn arg<T: VaArgSafe>(&mut self) -> T {
|
||||
#[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")]
|
||||
pub const unsafe fn arg<T: VaArgSafe>(&mut self) -> T {
|
||||
// SAFETY: the caller must uphold the safety contract for `va_arg`.
|
||||
unsafe { va_arg(self) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3485,7 +3485,7 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
|
|||
///
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_nounwind]
|
||||
pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaList<'_>) -> T;
|
||||
pub const unsafe fn va_arg<T: VaArgSafe>(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.
|
||||
|
|
@ -3497,7 +3497,7 @@ pub unsafe fn va_arg<T: VaArgSafe>(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()
|
||||
}
|
||||
|
||||
|
|
@ -3516,6 +3516,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 */
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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<T>(ten: T, two: T)
|
||||
where
|
||||
T: PartialEq
|
||||
+ Add<Output = T>
|
||||
+ Sub<Output = T>
|
||||
+ Mul<Output = T>
|
||||
+ Div<Output = T>
|
||||
+ Rem<Output = T>
|
||||
+ 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;
|
||||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -184,7 +184,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::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`.
|
||||
|
|
|
|||
|
|
@ -655,7 +655,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.
|
||||
}
|
||||
}
|
||||
|
|
@ -1014,7 +1014,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.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -577,7 +577,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.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
13
src/tools/miri/tests/fail/c-variadic-mismatch-count.rs
Normal file
13
src/tools/miri/tests/fail/c-variadic-mismatch-count.rs
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
13
src/tools/miri/tests/fail/c-variadic-mismatch-count.stderr
Normal file
13
src/tools/miri/tests/fail/c-variadic-mismatch-count.stderr
Normal file
|
|
@ -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
|
||||
|
||||
13
src/tools/miri/tests/fail/c-variadic-mismatch.rs
Normal file
13
src/tools/miri/tests/fail/c-variadic-mismatch.rs
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
13
src/tools/miri/tests/fail/c-variadic-mismatch.stderr
Normal file
13
src/tools/miri/tests/fail/c-variadic-mismatch.stderr
Normal file
|
|
@ -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
|
||||
|
||||
15
src/tools/miri/tests/fail/c-variadic.rs
Normal file
15
src/tools/miri/tests/fail/c-variadic.rs
Normal file
|
|
@ -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::<i32>();
|
||||
}
|
||||
|
||||
unsafe { variadic() };
|
||||
}
|
||||
|
||||
fn main() {
|
||||
read_too_many();
|
||||
}
|
||||
22
src/tools/miri/tests/fail/c-variadic.stderr
Normal file
22
src/tools/miri/tests/fail/c-variadic.stderr
Normal file
|
|
@ -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
|
||||
|
||||
115
src/tools/miri/tests/pass/c-variadic.rs
Normal file
115
src/tools/miri/tests/pass/c-variadic.rs
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#![feature(c_variadic)]
|
||||
|
||||
use std::ffi::{CStr, VaList, c_char, c_double, c_int, c_long};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#[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::<c_double>().floor() == 3.14f64.floor());
|
||||
continue_if!(ap.arg::<c_long>() == 12);
|
||||
continue_if!(ap.arg::<c_int>() == 'a' as c_int);
|
||||
continue_if!(ap.arg::<c_double>().floor() == 6.18f64.floor());
|
||||
continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Hello"));
|
||||
continue_if!(ap.arg::<c_int>() == 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() {
|
||||
ignores_arguments();
|
||||
echo();
|
||||
forward_by_val();
|
||||
forward_by_ref();
|
||||
nested();
|
||||
various_types();
|
||||
}
|
||||
147
tests/ui/consts/const-eval/c-variadic-fail.rs
Normal file
147
tests/ui/consts/const-eval/c-variadic-fail.rs
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
//@ build-fail
|
||||
|
||||
#![feature(c_variadic)]
|
||||
#![feature(const_c_variadic)]
|
||||
#![feature(const_trait_impl)]
|
||||
#![feature(const_destruct)]
|
||||
#![feature(const_clone)]
|
||||
|
||||
use std::ffi::VaList;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
const unsafe extern "C" fn read_n<const N: usize>(mut ap: ...) {
|
||||
let mut i = N;
|
||||
while i > 0 {
|
||||
i -= 1;
|
||||
let _ = ap.arg::<i32>();
|
||||
}
|
||||
}
|
||||
|
||||
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<T: core::ffi::VaArgSafe>(mut ap: ...) -> T {
|
||||
ap.arg::<T>()
|
||||
}
|
||||
|
||||
unsafe fn read_cast() {
|
||||
const { read_as::<i32>(1i32) };
|
||||
const { read_as::<u32>(1u32) };
|
||||
|
||||
const { read_as::<i32>(1i32, 2u64, 3.0f64) };
|
||||
const { read_as::<u32>(1u32, 2u64, 3.0f64) };
|
||||
|
||||
const { read_as::<i64>(1i64) };
|
||||
const { read_as::<u64>(1u64) };
|
||||
|
||||
const { read_as::<u32>(1i32) };
|
||||
//~^ ERROR va_arg type mismatch: requested `u32`, but next argument is `i32`
|
||||
|
||||
const { read_as::<i32>(1u32) };
|
||||
//~^ ERROR va_arg type mismatch: requested `i32`, but next argument is `u32`
|
||||
|
||||
const { read_as::<i32>(1u64) };
|
||||
//~^ ERROR va_arg type mismatch: requested `i32`, but next argument is `u64`
|
||||
|
||||
const { read_as::<f64>(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::<VaList>()] {
|
||||
unsafe { std::mem::transmute(ap) }
|
||||
}
|
||||
|
||||
const {
|
||||
unsafe {
|
||||
let ap = helper(1, 2, 3);
|
||||
let mut ap = std::mem::transmute::<_, VaList>(ap);
|
||||
ap.arg::<i32>();
|
||||
//~^ ERROR memory access failed: ALLOC0 has been freed, so this pointer is dangling [E0080]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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();
|
||||
assert!(copy.arg::<i32>() == 1i32);
|
||||
|
||||
let mut copy: VaList = unsafe { std::mem::transmute_copy(&ap) };
|
||||
|
||||
// Using the copy is actually fine.
|
||||
let _ = copy.arg::<i32>();
|
||||
drop(copy);
|
||||
|
||||
// But then using the original is UB.
|
||||
drop(ap);
|
||||
}
|
||||
|
||||
const { unsafe { helper(1, 2, 3) } };
|
||||
//~^ 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 = unsafe { std::mem::transmute_copy(&ap) };
|
||||
|
||||
// Using the copy is actually fine.
|
||||
let _ = copy.arg::<i32>();
|
||||
std::mem::forget(copy);
|
||||
|
||||
// The read (via `copy`) deallocated the original allocation.
|
||||
drop(ap);
|
||||
}
|
||||
|
||||
const { unsafe { helper(1, 2, 3) } };
|
||||
//~^ 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 = unsafe { std::mem::transmute_copy(&ap) };
|
||||
|
||||
// Reading from `ap` after reading from `copy` is UB.
|
||||
let _ = copy.arg::<i32>();
|
||||
let _ = ap.arg::<i32>();
|
||||
}
|
||||
|
||||
const { unsafe { helper(1, 2, 3) } };
|
||||
//~^ 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<VaList> = 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() {
|
||||
unsafe {
|
||||
read_too_many();
|
||||
read_cast();
|
||||
manual_copy_read();
|
||||
manual_copy_drop();
|
||||
manual_copy_forget();
|
||||
drop_of_invalid();
|
||||
}
|
||||
}
|
||||
355
tests/ui/consts/const-eval/c-variadic-fail.stderr
Normal file
355
tests/ui/consts/const-eval/c-variadic-fail.stderr
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
error[E0080]: more C-variadic arguments read than were passed
|
||||
--> $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:16:17
|
||||
|
|
||||
LL | let _ = ap.arg::<i32>();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<i32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:28:5
|
||||
|
|
||||
LL | const { read_n::<1>() }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:28: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: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:16:17
|
||||
|
|
||||
LL | let _ = ap.arg::<i32>();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<i32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:32:5
|
||||
|
|
||||
LL | const { read_n::<2>(1) }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:32: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:50:13
|
||||
|
|
||||
LL | const { read_as::<u32>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#6}` failed inside this call
|
||||
|
|
||||
note: inside `read_as::<u32>`
|
||||
--> $DIR/c-variadic-fail.rs:37:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<u32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:50:5
|
||||
|
|
||||
LL | const { read_as::<u32>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:50:5
|
||||
|
|
||||
LL | const { read_as::<u32>(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:53:13
|
||||
|
|
||||
LL | const { read_as::<i32>(1u32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#7}` failed inside this call
|
||||
|
|
||||
note: inside `read_as::<i32>`
|
||||
--> $DIR/c-variadic-fail.rs:37:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<i32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:53:5
|
||||
|
|
||||
LL | const { read_as::<i32>(1u32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:53:5
|
||||
|
|
||||
LL | const { read_as::<i32>(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:56:13
|
||||
|
|
||||
LL | const { read_as::<i32>(1u64) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#8}` failed inside this call
|
||||
|
|
||||
note: inside `read_as::<i32>`
|
||||
--> $DIR/c-variadic-fail.rs:37:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<i32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:56:5
|
||||
|
|
||||
LL | const { read_as::<i32>(1u64) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:56:5
|
||||
|
|
||||
LL | const { read_as::<i32>(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:59:13
|
||||
|
|
||||
LL | const { read_as::<f64>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^ evaluation of `read_cast::{constant#9}` failed inside this call
|
||||
|
|
||||
note: inside `read_as::<f64>`
|
||||
--> $DIR/c-variadic-fail.rs:37:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<f64>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:59:5
|
||||
|
|
||||
LL | const { read_as::<f64>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:59:5
|
||||
|
|
||||
LL | const { read_as::<f64>(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: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:37:5
|
||||
|
|
||||
LL | ap.arg::<T>()
|
||||
| ^^^^^^^^^^^^^
|
||||
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:62:5
|
||||
|
|
||||
LL | const { read_as::<*const u8>(1i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:62: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:75:13
|
||||
|
|
||||
LL | ap.arg::<i32>();
|
||||
| ^^^^^^^^^^^^^^^ evaluation of `use_after_free::{constant#0}` failed inside this call
|
||||
|
|
||||
note: inside `VaList::<'_>::arg::<i32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:71: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:71: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]: 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:94:9
|
||||
|
|
||||
LL | drop(ap);
|
||||
| ^^^^^^^^
|
||||
note: inside `std::mem::drop::<VaList<'_>>`
|
||||
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||
note: inside `drop_in_place::<VaList<'_>> - shim(Some(VaList<'_>))`
|
||||
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
||||
note: inside `<VaList<'_> as Drop>::drop`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:97:5
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $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]: 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:110:9
|
||||
|
|
||||
LL | drop(ap);
|
||||
| ^^^^^^^^
|
||||
note: inside `std::mem::drop::<VaList<'_>>`
|
||||
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||
note: inside `drop_in_place::<VaList<'_>> - shim(Some(VaList<'_>))`
|
||||
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
||||
note: inside `<VaList<'_> as Drop>::drop`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:113:5
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $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]: 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:123:17
|
||||
|
|
||||
LL | let _ = ap.arg::<i32>();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
note: inside `VaList::<'_>::arg::<i32>`
|
||||
--> $SRC_DIR/core/src/ffi/va_list.rs:LL:COL
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/c-variadic-fail.rs:126:5
|
||||
|
|
||||
LL | const { unsafe { helper(1, 2, 3) } };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $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[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::<VaList<'_>> - shim(Some(VaList<'_>))`
|
||||
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
||||
note: inside `<VaList<'_> 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<VaList> = 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<VaList> = 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`.
|
||||
155
tests/ui/consts/const-eval/c-variadic.rs
Normal file
155
tests/ui/consts/const-eval/c-variadic.rs
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
//@ edition: 2021
|
||||
//@ run-pass
|
||||
//@ ignore-backends: gcc
|
||||
|
||||
#![feature(c_variadic)]
|
||||
#![feature(const_c_variadic)]
|
||||
#![feature(const_destruct)]
|
||||
#![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::<c_double>().floor() == 3.14f64.floor());
|
||||
continue_if!(ap.arg::<c_long>() == 12);
|
||||
continue_if!(ap.arg::<c_int>() == 'a' as c_int);
|
||||
continue_if!(ap.arg::<c_double>().floor() == 6.18f64.floor());
|
||||
continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Hello"));
|
||||
continue_if!(ap.arg::<c_int>() == 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();
|
||||
}
|
||||
11
tests/ui/feature-gates/feature-gate-const-c-variadic.rs
Normal file
11
tests/ui/feature-gates/feature-gate-const-c-variadic.rs
Normal file
|
|
@ -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
|
||||
}
|
||||
24
tests/ui/feature-gates/feature-gate-const-c-variadic.stderr
Normal file
24
tests/ui/feature-gates/feature-gate-const-c-variadic.stderr
Normal file
|
|
@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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`.
|
||||
|
|
@ -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() {}
|
||||
|
|
|
|||
21
tests/ui/lint/unused/diverging-path.rs
Normal file
21
tests/ui/lint/unused/diverging-path.rs
Normal file
|
|
@ -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: FnOnce()>(f: F) {
|
||||
if let Ok(true) = std::fs::exists("may_or_may_not_call_f") {
|
||||
_ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
|
||||
}
|
||||
}
|
||||
|
|
@ -31,18 +31,18 @@ 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
|
||||
//~| ERROR c-variadic const function definitions are unstable
|
||||
|
||||
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
|
||||
//~| ERROR c-variadic const function definitions are unstable
|
||||
|
||||
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
|
||||
//~| ERROR c-variadic const function definitions are unstable
|
||||
|
||||
extern "C" {
|
||||
fn e_f2(..., x: isize);
|
||||
|
|
@ -64,8 +64,8 @@ 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
|
||||
//~| ERROR c-variadic const function definitions are unstable
|
||||
}
|
||||
|
||||
trait T {
|
||||
|
|
|
|||
|
|
@ -80,17 +80,25 @@ 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
|
||||
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, _: ...) {}
|
||||
| ^^^^^ `const` because of this ^^^^^^ C-variadic because of this
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #151787 <https://github.com/rust-lang/rust/issues/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 cannot be both `const` and C-variadic
|
||||
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, _: ...) {}
|
||||
| ^^^^^ `const` because of this ^^^^^^ C-variadic because of this
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #151787 <https://github.com/rust-lang/rust/issues/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:37:36
|
||||
|
|
@ -109,11 +117,15 @@ error: `...` must be the last argument of a C-variadic function
|
|||
LL | const extern "C" fn f4_3(_: ..., x: isize, _: ...) {}
|
||||
| ^^^^^^
|
||||
|
||||
error: functions cannot be both `const` and C-variadic
|
||||
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, _: ...) {}
|
||||
| ^^^^^ `const` because of this ^^^^^^ C-variadic because of this
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #151787 <https://github.com/rust-lang/rust/issues/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:42:44
|
||||
|
|
@ -176,13 +188,15 @@ 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
|
||||
error[E0658]: c-variadic const function definitions are unstable
|
||||
--> $DIR/variadic-ffi-semantic-restrictions.rs:65:5
|
||||
|
|
||||
LL | const fn i_f5(x: isize, _: ...) {}
|
||||
| ^^^^^ ^^^^^^ C-variadic because of this
|
||||
| |
|
||||
| `const` because of this
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #151787 <https://github.com/rust-lang/rust/issues/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:65:29
|
||||
|
|
@ -243,6 +257,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 <https://github.com/rust-lang/rust/issues/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:37:36
|
||||
|
|
@ -251,6 +269,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 <https://github.com/rust-lang/rust/issues/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:65:29
|
||||
|
|
@ -259,7 +281,12 @@ 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 <https://github.com/rust-lang/rust/issues/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 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`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue