Auto merge of #143233 - dianqk:rollup-lcx3278, r=dianqk
Rollup of 14 pull requests Successful merges: - rust-lang/rust#142429 (`tests/ui`: A New Order [13/N]) - rust-lang/rust#142514 (Miri: handling of SNaN inputs in `f*::pow` operations) - rust-lang/rust#143066 (Use let chains in the new solver) - rust-lang/rust#143090 (Workaround for memory unsafety in third party DLLs) - rust-lang/rust#143118 (`tests/ui`: A New Order [15/N]) - rust-lang/rust#143159 (Do not freshen `ReError`) - rust-lang/rust#143168 (`tests/ui`: A New Order [16/N]) - rust-lang/rust#143176 (fix typos and improve clarity in documentation) - rust-lang/rust#143187 (Add my work email to mailmap) - rust-lang/rust#143190 (Use the `new` method for `BasicBlockData` and `Statement`) - rust-lang/rust#143195 (`tests/ui`: A New Order [17/N]) - rust-lang/rust#143196 (Port #[link_section] to the new attribute parsing infrastructure) - rust-lang/rust#143199 (Re-disable `tests/run-make/short-ice` on Windows MSVC again) - rust-lang/rust#143219 (Show auto trait and blanket impls for `!`) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
c65dccabac
125 changed files with 1360 additions and 1065 deletions
|
|
@ -499,7 +499,7 @@ This directive takes comma-separated issue numbers as arguments, or `"unknown"`:
|
|||
- `//@ known-bug: rust-lang/chalk#123456`
|
||||
(allows arbitrary text before the `#`, which is useful when the issue is on another repo)
|
||||
- `//@ known-bug: unknown`
|
||||
(when there is no known issue yet; preferrably open one if it does not already exist)
|
||||
(when there is no known issue yet; preferably open one if it does not already exist)
|
||||
|
||||
Do not include [error annotations](#error-annotations) in a test with
|
||||
`known-bug`. The test should still include other normal directives and
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ struct HaveAlias {
|
|||
|
||||
In this example, the concrete type referred to by `Alias` is guaranteed to be the same wherever `Alias` occurs.
|
||||
|
||||
> Orginally this feature included type aliases as an associated type of a trait. In [#110237] this was split off to [`impl_trait_in_assoc_type`].
|
||||
> Originally this feature included type aliases as an associated type of a trait. In [#110237] this was split off to [`impl_trait_in_assoc_type`].
|
||||
|
||||
### `type_alias_impl_trait` in argument position.
|
||||
|
||||
|
|
|
|||
|
|
@ -753,9 +753,12 @@ impl Item {
|
|||
.other_attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
if let hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) = attr {
|
||||
Some(format!("#[link_section = \"{name}\"]"))
|
||||
}
|
||||
// NoMangle is special cased, as it appears in HTML output, and we want to show it in source form, not HIR printing.
|
||||
// It is also used by cargo-semver-checks.
|
||||
if let hir::Attribute::Parsed(AttributeKind::NoMangle(..)) = attr {
|
||||
else if let hir::Attribute::Parsed(AttributeKind::NoMangle(..)) = attr {
|
||||
Some("#[no_mangle]".to_string())
|
||||
} else if let hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) = attr
|
||||
{
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let [f] = check_intrinsic_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f32()?;
|
||||
|
||||
let res = fixed_float_value(intrinsic_name, &[f]).unwrap_or_else(||{
|
||||
let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
|
||||
// Using host floats (but it's fine, these operations do not have
|
||||
// guaranteed precision).
|
||||
let host = f.to_host();
|
||||
|
|
@ -235,7 +235,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let [f] = check_intrinsic_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f64()?;
|
||||
|
||||
let res = fixed_float_value(intrinsic_name, &[f]).unwrap_or_else(||{
|
||||
let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
|
||||
// Using host floats (but it's fine, these operations do not have
|
||||
// guaranteed precision).
|
||||
let host = f.to_host();
|
||||
|
|
@ -312,7 +312,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let f1 = this.read_scalar(f1)?.to_f32()?;
|
||||
let f2 = this.read_scalar(f2)?.to_f32()?;
|
||||
|
||||
let res = fixed_float_value(intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
|
||||
let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
let res = f1.to_host().powf(f2.to_host()).to_soft();
|
||||
|
||||
|
|
@ -330,7 +330,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let f1 = this.read_scalar(f1)?.to_f64()?;
|
||||
let f2 = this.read_scalar(f2)?.to_f64()?;
|
||||
|
||||
let res = fixed_float_value(intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
|
||||
let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
let res = f1.to_host().powf(f2.to_host()).to_soft();
|
||||
|
||||
|
|
@ -349,7 +349,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let f = this.read_scalar(f)?.to_f32()?;
|
||||
let i = this.read_scalar(i)?.to_i32()?;
|
||||
|
||||
let res = fixed_powi_float_value(f, i).unwrap_or_else(|| {
|
||||
let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| {
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
let res = f.to_host().powi(i).to_soft();
|
||||
|
||||
|
|
@ -367,7 +367,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let f = this.read_scalar(f)?.to_f64()?;
|
||||
let i = this.read_scalar(i)?.to_i32()?;
|
||||
|
||||
let res = fixed_powi_float_value(f, i).unwrap_or_else(|| {
|
||||
let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| {
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
let res = f.to_host().powi(i).to_soft();
|
||||
|
||||
|
|
@ -496,52 +496,88 @@ fn apply_random_float_error_to_imm<'tcx>(
|
|||
/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
|
||||
/// - powf32, powf64
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
|
||||
/// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error
|
||||
/// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
|
||||
/// implementation. Returns `None` if no specific value is guaranteed.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// For `powf*` operations of the form:
|
||||
///
|
||||
/// - `(SNaN)^(±0)`
|
||||
/// - `1^(SNaN)`
|
||||
///
|
||||
/// The result is implementation-defined:
|
||||
/// - musl returns for both `1.0`
|
||||
/// - glibc returns for both `NaN`
|
||||
///
|
||||
/// This discrepancy exists because SNaN handling is not consistently defined across platforms,
|
||||
/// and the C standard leaves behavior for SNaNs unspecified.
|
||||
///
|
||||
/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
|
||||
fn fixed_float_value<S: Semantics>(
|
||||
ecx: &mut MiriInterpCx<'_>,
|
||||
intrinsic_name: &str,
|
||||
args: &[IeeeFloat<S>],
|
||||
) -> Option<IeeeFloat<S>> {
|
||||
let one = IeeeFloat::<S>::one();
|
||||
match (intrinsic_name, args) {
|
||||
Some(match (intrinsic_name, args) {
|
||||
// cos(+- 0) = 1
|
||||
("cosf32" | "cosf64", [input]) if input.is_zero() => Some(one),
|
||||
("cosf32" | "cosf64", [input]) if input.is_zero() => one,
|
||||
|
||||
// e^0 = 1
|
||||
("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => Some(one),
|
||||
|
||||
// 1^y = 1 for any y, even a NaN.
|
||||
("powf32" | "powf64", [base, _]) if *base == one => Some(one),
|
||||
("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one,
|
||||
|
||||
// (-1)^(±INF) = 1
|
||||
("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => Some(one),
|
||||
("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one,
|
||||
|
||||
// 1^y = 1 for any y, even a NaN
|
||||
("powf32" | "powf64", [base, exp]) if *base == one => {
|
||||
let rng = ecx.machine.rng.get_mut();
|
||||
// SNaN exponents get special treatment: they might return 1, or a NaN.
|
||||
let return_nan = exp.is_signaling() && ecx.machine.float_nondet && rng.random();
|
||||
// Handle both the musl and glibc cases non-deterministically.
|
||||
if return_nan { ecx.generate_nan(args) } else { one }
|
||||
}
|
||||
|
||||
// FIXME(#4286): The C ecosystem is inconsistent with handling sNaN's, some return 1 others propogate
|
||||
// the NaN. We should return either 1 or the NaN non-deterministically here.
|
||||
// But for now, just handle them all the same.
|
||||
// x^(±0) = 1 for any x, even a NaN
|
||||
("powf32" | "powf64", [_, exp]) if exp.is_zero() => Some(one),
|
||||
("powf32" | "powf64", [base, exp]) if exp.is_zero() => {
|
||||
let rng = ecx.machine.rng.get_mut();
|
||||
// SNaN bases get special treatment: they might return 1, or a NaN.
|
||||
let return_nan = base.is_signaling() && ecx.machine.float_nondet && rng.random();
|
||||
// Handle both the musl and glibc cases non-deterministically.
|
||||
if return_nan { ecx.generate_nan(args) } else { one }
|
||||
}
|
||||
|
||||
// There are a lot of cases for fixed outputs according to the C Standard, but these are mainly INF or zero
|
||||
// which are not affected by the applied error.
|
||||
_ => None,
|
||||
}
|
||||
// There are a lot of cases for fixed outputs according to the C Standard, but these are
|
||||
// mainly INF or zero which are not affected by the applied error.
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the C standard
|
||||
/// (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
|
||||
fn fixed_powi_float_value<S: Semantics>(base: IeeeFloat<S>, exp: i32) -> Option<IeeeFloat<S>> {
|
||||
match (base.category(), exp) {
|
||||
// x^0 = 1, if x is not a Signaling NaN
|
||||
// FIXME(#4286): The C ecosystem is inconsistent with handling sNaN's, some return 1 others propogate
|
||||
// the NaN. We should return either 1 or the NaN non-deterministically here.
|
||||
// But for now, just handle them all the same.
|
||||
(_, 0) => Some(IeeeFloat::<S>::one()),
|
||||
/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the
|
||||
/// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
|
||||
fn fixed_powi_float_value<S: Semantics>(
|
||||
ecx: &mut MiriInterpCx<'_>,
|
||||
base: IeeeFloat<S>,
|
||||
exp: i32,
|
||||
) -> Option<IeeeFloat<S>> {
|
||||
Some(match exp {
|
||||
0 => {
|
||||
let one = IeeeFloat::<S>::one();
|
||||
let rng = ecx.machine.rng.get_mut();
|
||||
let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling();
|
||||
// For SNaN treatment, we are consistent with `powf`above.
|
||||
// (We wouldn't have two, unlike powf all implementations seem to agree for powi,
|
||||
// but for now we are maximally conservative.)
|
||||
if return_nan { ecx.generate_nan(&[base]) } else { one }
|
||||
}
|
||||
|
||||
_ => None,
|
||||
}
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Given an floating-point operation and a floating-point value, clamps the result to the output
|
||||
|
|
|
|||
|
|
@ -1066,17 +1066,6 @@ pub fn libm() {
|
|||
assert_eq!((-1f32).powf(f32::NEG_INFINITY), 1.0);
|
||||
assert_eq!((-1f64).powf(f64::NEG_INFINITY), 1.0);
|
||||
|
||||
// For pow (powf in rust) the C standard says:
|
||||
// x^0 = 1 for all x even a sNaN
|
||||
// FIXME(#4286): this does not match the behavior of all implementations.
|
||||
assert_eq!(SNAN_F32.powf(0.0), 1.0);
|
||||
assert_eq!(SNAN_F64.powf(0.0), 1.0);
|
||||
|
||||
// For pown (powi in rust) the C standard says:
|
||||
// x^0 = 1 for all x even a sNaN
|
||||
// FIXME(#4286): this does not match the behavior of all implementations.
|
||||
assert_eq!(SNAN_F32.powi(0), 1.0);
|
||||
assert_eq!(SNAN_F64.powi(0), 1.0);
|
||||
|
||||
assert_eq!(0f32.powi(10), 0.0);
|
||||
assert_eq!(0f64.powi(100), 0.0);
|
||||
|
|
@ -1500,4 +1489,18 @@ fn test_non_determinism() {
|
|||
test_operations_f32(12., 5.);
|
||||
test_operations_f64(19., 11.);
|
||||
test_operations_f128(25., 18.);
|
||||
|
||||
|
||||
// SNaN^0 = (1 | NaN)
|
||||
ensure_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan());
|
||||
ensure_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan());
|
||||
|
||||
// 1^SNaN = (1 | NaN)
|
||||
ensure_nondet(|| f32::powf(1.0, SNAN_F32).is_nan());
|
||||
ensure_nondet(|| f64::powf(1.0, SNAN_F64).is_nan());
|
||||
|
||||
// same as powf (keep it consistent):
|
||||
// x^SNaN = (1 | NaN)
|
||||
ensure_nondet(|| f32::powi(SNAN_F32, 0).is_nan());
|
||||
ensure_nondet(|| f64::powi(SNAN_F64, 0).is_nan());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,6 +260,7 @@ fn test_f32() {
|
|||
|
||||
// Intrinsics
|
||||
let nan = F32::nan(Neg, Quiet, 0).as_f32();
|
||||
let snan = F32::nan(Neg, Signaling, 1).as_f32();
|
||||
check_all_outcomes(
|
||||
HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
|
||||
|| F32::from(f32::min(nan, nan)),
|
||||
|
|
@ -313,6 +314,18 @@ fn test_f32() {
|
|||
HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
|
||||
|| F32::from(nan.ln_gamma().0),
|
||||
);
|
||||
check_all_outcomes(
|
||||
HashSet::from_iter([
|
||||
F32::from(1.0),
|
||||
F32::nan(Pos, Quiet, 0),
|
||||
F32::nan(Neg, Quiet, 0),
|
||||
F32::nan(Pos, Quiet, 1),
|
||||
F32::nan(Neg, Quiet, 1),
|
||||
F32::nan(Pos, Signaling, 1),
|
||||
F32::nan(Neg, Signaling, 1),
|
||||
]),
|
||||
|| F32::from(snan.powf(0.0)),
|
||||
);
|
||||
}
|
||||
|
||||
fn test_f64() {
|
||||
|
|
@ -376,6 +389,7 @@ fn test_f64() {
|
|||
|
||||
// Intrinsics
|
||||
let nan = F64::nan(Neg, Quiet, 0).as_f64();
|
||||
let snan = F64::nan(Neg, Signaling, 1).as_f64();
|
||||
check_all_outcomes(
|
||||
HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
|
||||
|| F64::from(f64::min(nan, nan)),
|
||||
|
|
@ -433,6 +447,18 @@ fn test_f64() {
|
|||
HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
|
||||
|| F64::from(nan.ln_gamma().0),
|
||||
);
|
||||
check_all_outcomes(
|
||||
HashSet::from_iter([
|
||||
F64::from(1.0),
|
||||
F64::nan(Pos, Quiet, 0),
|
||||
F64::nan(Neg, Quiet, 0),
|
||||
F64::nan(Pos, Quiet, 1),
|
||||
F64::nan(Neg, Quiet, 1),
|
||||
F64::nan(Pos, Signaling, 1),
|
||||
F64::nan(Neg, Signaling, 1),
|
||||
]),
|
||||
|| F64::from(snan.powf(0.0)),
|
||||
);
|
||||
}
|
||||
|
||||
fn test_casts() {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ To combine installers.
|
|||
|
||||
* Make install.sh not have to be customized, pull it's data from a
|
||||
config file.
|
||||
* Be more resiliant to installation failures, particularly if the disk
|
||||
* Be more resilient to installation failures, particularly if the disk
|
||||
is full.
|
||||
* Pre-install and post-uninstall scripts.
|
||||
* Allow components to depend on or contradict other components.
|
||||
|
|
|
|||
|
|
@ -1368,7 +1368,6 @@ ui/infinite/issue-41731-infinite-macro-println.rs
|
|||
ui/intrinsics/issue-28575.rs
|
||||
ui/intrinsics/issue-84297-reifying-copy.rs
|
||||
ui/invalid/issue-114435-layout-type-err.rs
|
||||
ui/issue-11881.rs
|
||||
ui/issue-15924.rs
|
||||
ui/issue-16822.rs
|
||||
ui/issues-71798.rs
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue