Merge from rustc
This commit is contained in:
commit
e5bede18f6
95 changed files with 1019 additions and 8397 deletions
|
|
@ -38,11 +38,6 @@ Files: compiler/*
|
|||
Copyright: The Rust Project Developers (see https://thanks.rust-lang.org)
|
||||
License: MIT or Apache-2.0
|
||||
|
||||
Files: compiler/rustc_apfloat/*
|
||||
Copyright: LLVM APFloat authors
|
||||
The Rust Project Developers (see https://thanks.rust-lang.org)
|
||||
License: NCSA AND (MIT OR Apache-2.0)
|
||||
|
||||
Files: compiler/rustc_codegen_cranelift/src/cranelift_native.rs
|
||||
Copyright: The Cranelift Project Developers
|
||||
The Rust Project Developers (see https://thanks.rust-lang.org)
|
||||
|
|
|
|||
|
|
@ -3135,7 +3135,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc_apfloat"
|
||||
version = "0.0.0"
|
||||
version = "0.2.0+llvm-462a31f5a5ab"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "465187772033a5ee566f69fe008df03628fce549a0899aae76f0a0c2e34696be"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"smallvec",
|
||||
|
|
@ -4725,9 +4727,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||
|
||||
[[package]]
|
||||
name = "snap"
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
University of Illinois/NCSA Open Source License
|
||||
|
||||
Copyright (c) <Year> <Owner Organization Name>. All rights reserved.
|
||||
|
||||
Developed by: <Name of Development Group> <Name of Institution> <URL for Development Group/Institution>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of <Name of Development Group, Name of Institution>, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
[package]
|
||||
name = "rustc_apfloat"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.2.1"
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,695 +0,0 @@
|
|||
//! Port of LLVM's APFloat software floating-point implementation from the
|
||||
//! following C++ sources (please update commit hash when backporting):
|
||||
//! <https://github.com/llvm-mirror/llvm/tree/23efab2bbd424ed13495a420ad8641cb2c6c28f9>
|
||||
//!
|
||||
//! * `include/llvm/ADT/APFloat.h` -> `Float` and `FloatConvert` traits
|
||||
//! * `lib/Support/APFloat.cpp` -> `ieee` and `ppc` modules
|
||||
//! * `unittests/ADT/APFloatTest.cpp` -> `tests` directory
|
||||
//!
|
||||
//! The port contains no unsafe code, global state, or side-effects in general,
|
||||
//! and the only allocations are in the conversion to/from decimal strings.
|
||||
//!
|
||||
//! Most of the API and the testcases are intact in some form or another,
|
||||
//! with some ergonomic changes, such as idiomatic short names, returning
|
||||
//! new values instead of mutating the receiver, and having separate method
|
||||
//! variants that take a non-default rounding mode (with the suffix `_r`).
|
||||
//! Comments have been preserved where possible, only slightly adapted.
|
||||
//!
|
||||
//! Instead of keeping a pointer to a configuration struct and inspecting it
|
||||
//! dynamically on every operation, types (e.g., `ieee::Double`), traits
|
||||
//! (e.g., `ieee::Semantics`) and associated constants are employed for
|
||||
//! increased type safety and performance.
|
||||
//!
|
||||
//! On-heap bigints are replaced everywhere (except in decimal conversion),
|
||||
//! with short arrays of `type Limb = u128` elements (instead of `u64`),
|
||||
//! This allows fitting the largest supported significands in one integer
|
||||
//! (`ieee::Quad` and `ppc::Fallback` use slightly less than 128 bits).
|
||||
//! All of the functions in the `ieee::sig` module operate on slices.
|
||||
//!
|
||||
//! # Note
|
||||
//!
|
||||
//! This API is completely unstable and subject to change.
|
||||
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
|
||||
use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
|
||||
use core::str::FromStr;
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// IEEE-754R 7: Default exception handling.
|
||||
///
|
||||
/// UNDERFLOW or OVERFLOW are always returned or-ed with INEXACT.
|
||||
#[must_use]
|
||||
pub struct Status: u8 {
|
||||
const OK = 0x00;
|
||||
const INVALID_OP = 0x01;
|
||||
const DIV_BY_ZERO = 0x02;
|
||||
const OVERFLOW = 0x04;
|
||||
const UNDERFLOW = 0x08;
|
||||
const INEXACT = 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub struct StatusAnd<T> {
|
||||
pub status: Status,
|
||||
pub value: T,
|
||||
}
|
||||
|
||||
impl Status {
|
||||
pub fn and<T>(self, value: T) -> StatusAnd<T> {
|
||||
StatusAnd { status: self, value }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> StatusAnd<T> {
|
||||
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> StatusAnd<U> {
|
||||
StatusAnd { status: self.status, value: f(self.value) }
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unpack {
|
||||
($status:ident|=, $e:expr) => {
|
||||
match $e {
|
||||
$crate::StatusAnd { status, value } => {
|
||||
$status |= status;
|
||||
value
|
||||
}
|
||||
}
|
||||
};
|
||||
($status:ident=, $e:expr) => {
|
||||
match $e {
|
||||
$crate::StatusAnd { status, value } => {
|
||||
$status = status;
|
||||
value
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Category of internally-represented number.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Category {
|
||||
Infinity,
|
||||
NaN,
|
||||
Normal,
|
||||
Zero,
|
||||
}
|
||||
|
||||
/// IEEE-754R 4.3: Rounding-direction attributes.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Round {
|
||||
NearestTiesToEven,
|
||||
TowardPositive,
|
||||
TowardNegative,
|
||||
TowardZero,
|
||||
NearestTiesToAway,
|
||||
}
|
||||
|
||||
impl Neg for Round {
|
||||
type Output = Round;
|
||||
fn neg(self) -> Round {
|
||||
match self {
|
||||
Round::TowardPositive => Round::TowardNegative,
|
||||
Round::TowardNegative => Round::TowardPositive,
|
||||
Round::NearestTiesToEven | Round::TowardZero | Round::NearestTiesToAway => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A signed type to represent a floating point number's unbiased exponent.
|
||||
pub type ExpInt = i16;
|
||||
|
||||
// \c ilogb error results.
|
||||
pub const IEK_INF: ExpInt = ExpInt::MAX;
|
||||
pub const IEK_NAN: ExpInt = ExpInt::MIN;
|
||||
pub const IEK_ZERO: ExpInt = ExpInt::MIN + 1;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct ParseError(pub &'static str);
|
||||
|
||||
/// A self-contained host- and target-independent arbitrary-precision
|
||||
/// floating-point software implementation.
|
||||
///
|
||||
/// `apfloat` uses significand bignum integer arithmetic as provided by functions
|
||||
/// in the `ieee::sig`.
|
||||
///
|
||||
/// Written for clarity rather than speed, in particular with a view to use in
|
||||
/// the front-end of a cross compiler so that target arithmetic can be correctly
|
||||
/// performed on the host. Performance should nonetheless be reasonable,
|
||||
/// particularly for its intended use. It may be useful as a base
|
||||
/// implementation for a run-time library during development of a faster
|
||||
/// target-specific one.
|
||||
///
|
||||
/// All 5 rounding modes in the IEEE-754R draft are handled correctly for all
|
||||
/// implemented operations. Currently implemented operations are add, subtract,
|
||||
/// multiply, divide, fused-multiply-add, conversion-to-float,
|
||||
/// conversion-to-integer and conversion-from-integer. New rounding modes
|
||||
/// (e.g., away from zero) can be added with three or four lines of code.
|
||||
///
|
||||
/// Four formats are built-in: IEEE single precision, double precision,
|
||||
/// quadruple precision, and x87 80-bit extended double (when operating with
|
||||
/// full extended precision). Adding a new format that obeys IEEE semantics
|
||||
/// only requires adding two lines of code: a declaration and definition of the
|
||||
/// format.
|
||||
///
|
||||
/// All operations return the status of that operation as an exception bit-mask,
|
||||
/// so multiple operations can be done consecutively with their results or-ed
|
||||
/// together. The returned status can be useful for compiler diagnostics; e.g.,
|
||||
/// inexact, underflow and overflow can be easily diagnosed on constant folding,
|
||||
/// and compiler optimizers can determine what exceptions would be raised by
|
||||
/// folding operations and optimize, or perhaps not optimize, accordingly.
|
||||
///
|
||||
/// At present, underflow tininess is detected after rounding; it should be
|
||||
/// straight forward to add support for the before-rounding case too.
|
||||
///
|
||||
/// The library reads hexadecimal floating point numbers as per C99, and
|
||||
/// correctly rounds if necessary according to the specified rounding mode.
|
||||
/// Syntax is required to have been validated by the caller.
|
||||
///
|
||||
/// It also reads decimal floating point numbers and correctly rounds according
|
||||
/// to the specified rounding mode.
|
||||
///
|
||||
/// Non-zero finite numbers are represented internally as a sign bit, a 16-bit
|
||||
/// signed exponent, and the significand as an array of integer limbs. After
|
||||
/// normalization of a number of precision P the exponent is within the range of
|
||||
/// the format, and if the number is not denormal the P-th bit of the
|
||||
/// significand is set as an explicit integer bit. For denormals the most
|
||||
/// significant bit is shifted right so that the exponent is maintained at the
|
||||
/// format's minimum, so that the smallest denormal has just the least
|
||||
/// significant bit of the significand set. The sign of zeros and infinities
|
||||
/// is significant; the exponent and significand of such numbers is not stored,
|
||||
/// but has a known implicit (deterministic) value: 0 for the significands, 0
|
||||
/// for zero exponent, all 1 bits for infinity exponent. For NaNs the sign and
|
||||
/// significand are deterministic, although not really meaningful, and preserved
|
||||
/// in non-conversion operations. The exponent is implicitly all 1 bits.
|
||||
///
|
||||
/// `apfloat` does not provide any exception handling beyond default exception
|
||||
/// handling. We represent Signaling NaNs via IEEE-754R 2008 6.2.1 should clause
|
||||
/// by encoding Signaling NaNs with the first bit of its trailing significand
|
||||
/// as 0.
|
||||
///
|
||||
/// Future work
|
||||
/// ===========
|
||||
///
|
||||
/// Some features that may or may not be worth adding:
|
||||
///
|
||||
/// Optional ability to detect underflow tininess before rounding.
|
||||
///
|
||||
/// New formats: x87 in single and double precision mode (IEEE apart from
|
||||
/// extended exponent range) (hard).
|
||||
///
|
||||
/// New operations: sqrt, nexttoward.
|
||||
///
|
||||
pub trait Float:
|
||||
Copy
|
||||
+ Default
|
||||
+ FromStr<Err = ParseError>
|
||||
+ PartialOrd
|
||||
+ fmt::Display
|
||||
+ Neg<Output = Self>
|
||||
+ AddAssign
|
||||
+ SubAssign
|
||||
+ MulAssign
|
||||
+ DivAssign
|
||||
+ RemAssign
|
||||
+ Add<Output = StatusAnd<Self>>
|
||||
+ Sub<Output = StatusAnd<Self>>
|
||||
+ Mul<Output = StatusAnd<Self>>
|
||||
+ Div<Output = StatusAnd<Self>>
|
||||
+ Rem<Output = StatusAnd<Self>>
|
||||
{
|
||||
/// Total number of bits in the in-memory format.
|
||||
const BITS: usize;
|
||||
|
||||
/// Number of bits in the significand. This includes the integer bit.
|
||||
const PRECISION: usize;
|
||||
|
||||
/// The largest E such that 2<sup>E</sup> is representable; this matches the
|
||||
/// definition of IEEE 754.
|
||||
const MAX_EXP: ExpInt;
|
||||
|
||||
/// The smallest E such that 2<sup>E</sup> is a normalized number; this
|
||||
/// matches the definition of IEEE 754.
|
||||
const MIN_EXP: ExpInt;
|
||||
|
||||
/// Positive Zero.
|
||||
const ZERO: Self;
|
||||
|
||||
/// Positive Infinity.
|
||||
const INFINITY: Self;
|
||||
|
||||
/// NaN (Not a Number).
|
||||
// FIXME(eddyb) provide a default when qnan becomes const fn.
|
||||
const NAN: Self;
|
||||
|
||||
/// Factory for QNaN values.
|
||||
// FIXME(eddyb) should be const fn.
|
||||
fn qnan(payload: Option<u128>) -> Self;
|
||||
|
||||
/// Factory for SNaN values.
|
||||
// FIXME(eddyb) should be const fn.
|
||||
fn snan(payload: Option<u128>) -> Self;
|
||||
|
||||
/// Largest finite number.
|
||||
// FIXME(eddyb) should be const (but FloatPair::largest is nontrivial).
|
||||
fn largest() -> Self;
|
||||
|
||||
/// Smallest (by magnitude) finite number.
|
||||
/// Might be denormalized, which implies a relative loss of precision.
|
||||
const SMALLEST: Self;
|
||||
|
||||
/// Smallest (by magnitude) normalized finite number.
|
||||
// FIXME(eddyb) should be const (but FloatPair::smallest_normalized is nontrivial).
|
||||
fn smallest_normalized() -> Self;
|
||||
|
||||
// Arithmetic
|
||||
|
||||
fn add_r(self, rhs: Self, round: Round) -> StatusAnd<Self>;
|
||||
fn sub_r(self, rhs: Self, round: Round) -> StatusAnd<Self> {
|
||||
self.add_r(-rhs, round)
|
||||
}
|
||||
fn mul_r(self, rhs: Self, round: Round) -> StatusAnd<Self>;
|
||||
fn mul_add_r(self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd<Self>;
|
||||
fn mul_add(self, multiplicand: Self, addend: Self) -> StatusAnd<Self> {
|
||||
self.mul_add_r(multiplicand, addend, Round::NearestTiesToEven)
|
||||
}
|
||||
fn div_r(self, rhs: Self, round: Round) -> StatusAnd<Self>;
|
||||
/// IEEE remainder.
|
||||
// This is not currently correct in all cases.
|
||||
fn ieee_rem(self, rhs: Self) -> StatusAnd<Self> {
|
||||
let mut v = self;
|
||||
|
||||
let status;
|
||||
v = unpack!(status=, v / rhs);
|
||||
if status == Status::DIV_BY_ZERO {
|
||||
return status.and(self);
|
||||
}
|
||||
|
||||
assert!(Self::PRECISION < 128);
|
||||
|
||||
let status;
|
||||
let x = unpack!(status=, v.to_i128_r(128, Round::NearestTiesToEven, &mut false));
|
||||
if status == Status::INVALID_OP {
|
||||
return status.and(self);
|
||||
}
|
||||
|
||||
let status;
|
||||
let mut v = unpack!(status=, Self::from_i128(x));
|
||||
assert_eq!(status, Status::OK); // should always work
|
||||
|
||||
let status;
|
||||
v = unpack!(status=, v * rhs);
|
||||
assert_eq!(status - Status::INEXACT, Status::OK); // should not overflow or underflow
|
||||
|
||||
let status;
|
||||
v = unpack!(status=, self - v);
|
||||
assert_eq!(status - Status::INEXACT, Status::OK); // likewise
|
||||
|
||||
if v.is_zero() {
|
||||
status.and(v.copy_sign(self)) // IEEE754 requires this
|
||||
} else {
|
||||
status.and(v)
|
||||
}
|
||||
}
|
||||
/// C fmod, or llvm frem.
|
||||
fn c_fmod(self, rhs: Self) -> StatusAnd<Self>;
|
||||
fn round_to_integral(self, round: Round) -> StatusAnd<Self>;
|
||||
|
||||
/// IEEE-754R 2008 5.3.1: nextUp.
|
||||
fn next_up(self) -> StatusAnd<Self>;
|
||||
|
||||
/// IEEE-754R 2008 5.3.1: nextDown.
|
||||
///
|
||||
/// *NOTE* since nextDown(x) = -nextUp(-x), we only implement nextUp with
|
||||
/// appropriate sign switching before/after the computation.
|
||||
fn next_down(self) -> StatusAnd<Self> {
|
||||
(-self).next_up().map(|r| -r)
|
||||
}
|
||||
|
||||
fn abs(self) -> Self {
|
||||
if self.is_negative() { -self } else { self }
|
||||
}
|
||||
fn copy_sign(self, rhs: Self) -> Self {
|
||||
if self.is_negative() != rhs.is_negative() { -self } else { self }
|
||||
}
|
||||
|
||||
// Conversions
|
||||
fn from_bits(input: u128) -> Self;
|
||||
fn from_i128_r(input: i128, round: Round) -> StatusAnd<Self> {
|
||||
if input < 0 {
|
||||
Self::from_u128_r(input.wrapping_neg() as u128, -round).map(|r| -r)
|
||||
} else {
|
||||
Self::from_u128_r(input as u128, round)
|
||||
}
|
||||
}
|
||||
fn from_i128(input: i128) -> StatusAnd<Self> {
|
||||
Self::from_i128_r(input, Round::NearestTiesToEven)
|
||||
}
|
||||
fn from_u128_r(input: u128, round: Round) -> StatusAnd<Self>;
|
||||
fn from_u128(input: u128) -> StatusAnd<Self> {
|
||||
Self::from_u128_r(input, Round::NearestTiesToEven)
|
||||
}
|
||||
fn from_str_r(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError>;
|
||||
fn to_bits(self) -> u128;
|
||||
|
||||
/// Converts a floating point number to an integer according to the
|
||||
/// rounding mode. In case of an invalid operation exception,
|
||||
/// deterministic values are returned, namely zero for NaNs and the
|
||||
/// minimal or maximal value respectively for underflow or overflow.
|
||||
/// If the rounded value is in range but the floating point number is
|
||||
/// not the exact integer, the C standard doesn't require an inexact
|
||||
/// exception to be raised. IEEE-854 does require it so we do that.
|
||||
///
|
||||
/// Note that for conversions to integer type the C standard requires
|
||||
/// round-to-zero to always be used.
|
||||
///
|
||||
/// The *is_exact output tells whether the result is exact, in the sense
|
||||
/// that converting it back to the original floating point type produces
|
||||
/// the original value. This is almost equivalent to `result == Status::OK`,
|
||||
/// except for negative zeroes.
|
||||
fn to_i128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd<i128> {
|
||||
let status;
|
||||
if self.is_negative() {
|
||||
if self.is_zero() {
|
||||
// Negative zero can't be represented as an int.
|
||||
*is_exact = false;
|
||||
}
|
||||
let r = unpack!(status=, (-self).to_u128_r(width, -round, is_exact));
|
||||
|
||||
// Check for values that don't fit in the signed integer.
|
||||
if r > (1 << (width - 1)) {
|
||||
// Return the most negative integer for the given width.
|
||||
*is_exact = false;
|
||||
Status::INVALID_OP.and(-1 << (width - 1))
|
||||
} else {
|
||||
status.and(r.wrapping_neg() as i128)
|
||||
}
|
||||
} else {
|
||||
// Positive case is simpler, can pretend it's a smaller unsigned
|
||||
// integer, and `to_u128` will take care of all the edge cases.
|
||||
self.to_u128_r(width - 1, round, is_exact).map(|r| r as i128)
|
||||
}
|
||||
}
|
||||
fn to_i128(self, width: usize) -> StatusAnd<i128> {
|
||||
self.to_i128_r(width, Round::TowardZero, &mut true)
|
||||
}
|
||||
fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd<u128>;
|
||||
fn to_u128(self, width: usize) -> StatusAnd<u128> {
|
||||
self.to_u128_r(width, Round::TowardZero, &mut true)
|
||||
}
|
||||
|
||||
fn cmp_abs_normal(self, rhs: Self) -> Ordering;
|
||||
|
||||
/// Bitwise comparison for equality (QNaNs compare equal, 0!=-0).
|
||||
fn bitwise_eq(self, rhs: Self) -> bool;
|
||||
|
||||
// IEEE-754R 5.7.2 General operations.
|
||||
|
||||
/// Implements IEEE minNum semantics. Returns the smaller of the 2 arguments if
|
||||
/// both are not NaN. If either argument is a NaN, returns the other argument.
|
||||
fn min(self, other: Self) -> Self {
|
||||
if self.is_nan() {
|
||||
other
|
||||
} else if other.is_nan() {
|
||||
self
|
||||
} else if other.partial_cmp(&self) == Some(Ordering::Less) {
|
||||
other
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements IEEE maxNum semantics. Returns the larger of the 2 arguments if
|
||||
/// both are not NaN. If either argument is a NaN, returns the other argument.
|
||||
fn max(self, other: Self) -> Self {
|
||||
if self.is_nan() {
|
||||
other
|
||||
} else if other.is_nan() {
|
||||
self
|
||||
} else if self.partial_cmp(&other) == Some(Ordering::Less) {
|
||||
other
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// IEEE-754R isSignMinus: Returns whether the current value is
|
||||
/// negative.
|
||||
///
|
||||
/// This applies to zeros and NaNs as well.
|
||||
fn is_negative(self) -> bool;
|
||||
|
||||
/// IEEE-754R isNormal: Returns whether the current value is normal.
|
||||
///
|
||||
/// This implies that the current value of the float is not zero, subnormal,
|
||||
/// infinite, or NaN following the definition of normality from IEEE-754R.
|
||||
fn is_normal(self) -> bool {
|
||||
!self.is_denormal() && self.is_finite_non_zero()
|
||||
}
|
||||
|
||||
/// Returns `true` if the current value is zero, subnormal, or
|
||||
/// normal.
|
||||
///
|
||||
/// This means that the value is not infinite or NaN.
|
||||
fn is_finite(self) -> bool {
|
||||
!self.is_nan() && !self.is_infinite()
|
||||
}
|
||||
|
||||
/// Returns `true` if the float is plus or minus zero.
|
||||
fn is_zero(self) -> bool {
|
||||
self.category() == Category::Zero
|
||||
}
|
||||
|
||||
/// IEEE-754R isSubnormal(): Returns whether the float is a
|
||||
/// denormal.
|
||||
fn is_denormal(self) -> bool;
|
||||
|
||||
/// IEEE-754R isInfinite(): Returns whether the float is infinity.
|
||||
fn is_infinite(self) -> bool {
|
||||
self.category() == Category::Infinity
|
||||
}
|
||||
|
||||
/// Returns `true` if the float is a quiet or signaling NaN.
|
||||
fn is_nan(self) -> bool {
|
||||
self.category() == Category::NaN
|
||||
}
|
||||
|
||||
/// Returns `true` if the float is a signaling NaN.
|
||||
fn is_signaling(self) -> bool;
|
||||
|
||||
// Simple Queries
|
||||
|
||||
fn category(self) -> Category;
|
||||
fn is_non_zero(self) -> bool {
|
||||
!self.is_zero()
|
||||
}
|
||||
fn is_finite_non_zero(self) -> bool {
|
||||
self.is_finite() && !self.is_zero()
|
||||
}
|
||||
fn is_pos_zero(self) -> bool {
|
||||
self.is_zero() && !self.is_negative()
|
||||
}
|
||||
fn is_neg_zero(self) -> bool {
|
||||
self.is_zero() && self.is_negative()
|
||||
}
|
||||
|
||||
/// Returns `true` if the number has the smallest possible non-zero
|
||||
/// magnitude in the current semantics.
|
||||
fn is_smallest(self) -> bool {
|
||||
Self::SMALLEST.copy_sign(self).bitwise_eq(self)
|
||||
}
|
||||
|
||||
/// Returns `true` if the number has the largest possible finite
|
||||
/// magnitude in the current semantics.
|
||||
fn is_largest(self) -> bool {
|
||||
Self::largest().copy_sign(self).bitwise_eq(self)
|
||||
}
|
||||
|
||||
/// Returns `true` if the number is an exact integer.
|
||||
fn is_integer(self) -> bool {
|
||||
// This could be made more efficient; I'm going for obviously correct.
|
||||
if !self.is_finite() {
|
||||
return false;
|
||||
}
|
||||
self.round_to_integral(Round::TowardZero).value.bitwise_eq(self)
|
||||
}
|
||||
|
||||
/// If this value has an exact multiplicative inverse, return it.
|
||||
fn get_exact_inverse(self) -> Option<Self>;
|
||||
|
||||
/// Returns the exponent of the internal representation of the Float.
|
||||
///
|
||||
/// Because the radix of Float is 2, this is equivalent to floor(log2(x)).
|
||||
/// For special Float values, this returns special error codes:
|
||||
///
|
||||
/// NaN -> \c IEK_NAN
|
||||
/// 0 -> \c IEK_ZERO
|
||||
/// Inf -> \c IEK_INF
|
||||
///
|
||||
fn ilogb(self) -> ExpInt;
|
||||
|
||||
/// Returns: self * 2<sup>exp</sup> for integral exponents.
|
||||
/// Equivalent to C standard library function `ldexp`.
|
||||
fn scalbn_r(self, exp: ExpInt, round: Round) -> Self;
|
||||
fn scalbn(self, exp: ExpInt) -> Self {
|
||||
self.scalbn_r(exp, Round::NearestTiesToEven)
|
||||
}
|
||||
|
||||
/// Equivalent to C standard library function with the same name.
|
||||
///
|
||||
/// While the C standard says exp is an unspecified value for infinity and nan,
|
||||
/// this returns INT_MAX for infinities, and INT_MIN for NaNs (see `ilogb`).
|
||||
fn frexp_r(self, exp: &mut ExpInt, round: Round) -> Self;
|
||||
fn frexp(self, exp: &mut ExpInt) -> Self {
|
||||
self.frexp_r(exp, Round::NearestTiesToEven)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FloatConvert<T: Float>: Float {
|
||||
/// Converts a value of one floating point type to another.
|
||||
/// The return value corresponds to the IEEE754 exceptions. *loses_info
|
||||
/// records whether the transformation lost information, i.e., whether
|
||||
/// converting the result back to the original type will produce the
|
||||
/// original value (this is almost the same as return `value == Status::OK`,
|
||||
/// but there are edge cases where this is not so).
|
||||
fn convert_r(self, round: Round, loses_info: &mut bool) -> StatusAnd<T>;
|
||||
fn convert(self, loses_info: &mut bool) -> StatusAnd<T> {
|
||||
self.convert_r(Round::NearestTiesToEven, loses_info)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! float_common_impls {
|
||||
($ty:ident<$t:tt>) => {
|
||||
impl<$t> Default for $ty<$t>
|
||||
where
|
||||
Self: Float,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
impl<$t> ::core::str::FromStr for $ty<$t>
|
||||
where
|
||||
Self: Float,
|
||||
{
|
||||
type Err = ParseError;
|
||||
fn from_str(s: &str) -> Result<Self, ParseError> {
|
||||
Self::from_str_r(s, Round::NearestTiesToEven).map(|x| x.value)
|
||||
}
|
||||
}
|
||||
|
||||
// Rounding ties to the nearest even, by default.
|
||||
|
||||
impl<$t> ::core::ops::Add for $ty<$t>
|
||||
where
|
||||
Self: Float,
|
||||
{
|
||||
type Output = StatusAnd<Self>;
|
||||
fn add(self, rhs: Self) -> StatusAnd<Self> {
|
||||
self.add_r(rhs, Round::NearestTiesToEven)
|
||||
}
|
||||
}
|
||||
|
||||
impl<$t> ::core::ops::Sub for $ty<$t>
|
||||
where
|
||||
Self: Float,
|
||||
{
|
||||
type Output = StatusAnd<Self>;
|
||||
fn sub(self, rhs: Self) -> StatusAnd<Self> {
|
||||
self.sub_r(rhs, Round::NearestTiesToEven)
|
||||
}
|
||||
}
|
||||
|
||||
impl<$t> ::core::ops::Mul for $ty<$t>
|
||||
where
|
||||
Self: Float,
|
||||
{
|
||||
type Output = StatusAnd<Self>;
|
||||
fn mul(self, rhs: Self) -> StatusAnd<Self> {
|
||||
self.mul_r(rhs, Round::NearestTiesToEven)
|
||||
}
|
||||
}
|
||||
|
||||
impl<$t> ::core::ops::Div for $ty<$t>
|
||||
where
|
||||
Self: Float,
|
||||
{
|
||||
type Output = StatusAnd<Self>;
|
||||
fn div(self, rhs: Self) -> StatusAnd<Self> {
|
||||
self.div_r(rhs, Round::NearestTiesToEven)
|
||||
}
|
||||
}
|
||||
|
||||
impl<$t> ::core::ops::Rem for $ty<$t>
|
||||
where
|
||||
Self: Float,
|
||||
{
|
||||
type Output = StatusAnd<Self>;
|
||||
fn rem(self, rhs: Self) -> StatusAnd<Self> {
|
||||
self.c_fmod(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<$t> ::core::ops::AddAssign for $ty<$t>
|
||||
where
|
||||
Self: Float,
|
||||
{
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self = (*self + rhs).value;
|
||||
}
|
||||
}
|
||||
|
||||
impl<$t> ::core::ops::SubAssign for $ty<$t>
|
||||
where
|
||||
Self: Float,
|
||||
{
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
*self = (*self - rhs).value;
|
||||
}
|
||||
}
|
||||
|
||||
impl<$t> ::core::ops::MulAssign for $ty<$t>
|
||||
where
|
||||
Self: Float,
|
||||
{
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
*self = (*self * rhs).value;
|
||||
}
|
||||
}
|
||||
|
||||
impl<$t> ::core::ops::DivAssign for $ty<$t>
|
||||
where
|
||||
Self: Float,
|
||||
{
|
||||
fn div_assign(&mut self, rhs: Self) {
|
||||
*self = (*self / rhs).value;
|
||||
}
|
||||
}
|
||||
|
||||
impl<$t> ::core::ops::RemAssign for $ty<$t>
|
||||
where
|
||||
Self: Float,
|
||||
{
|
||||
fn rem_assign(&mut self, rhs: Self) {
|
||||
*self = (*self % rhs).value;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod ieee;
|
||||
pub mod ppc;
|
||||
|
|
@ -1,434 +0,0 @@
|
|||
use crate::ieee;
|
||||
use crate::{Category, ExpInt, Float, FloatConvert, ParseError, Round, Status, StatusAnd};
|
||||
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::ops::Neg;
|
||||
|
||||
#[must_use]
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
|
||||
pub struct DoubleFloat<F>(F, F);
|
||||
pub type DoubleDouble = DoubleFloat<ieee::Double>;
|
||||
|
||||
// These are legacy semantics for the Fallback, inaccurate implementation of
|
||||
// IBM double-double, if the accurate DoubleDouble doesn't handle the
|
||||
// operation. It's equivalent to having an IEEE number with consecutive 106
|
||||
// bits of mantissa and 11 bits of exponent.
|
||||
//
|
||||
// It's not equivalent to IBM double-double. For example, a legit IBM
|
||||
// double-double, 1 + epsilon:
|
||||
//
|
||||
// 1 + epsilon = 1 + (1 >> 1076)
|
||||
//
|
||||
// is not representable by a consecutive 106 bits of mantissa.
|
||||
//
|
||||
// Currently, these semantics are used in the following way:
|
||||
//
|
||||
// DoubleDouble -> (Double, Double) ->
|
||||
// DoubleDouble's Fallback -> IEEE operations
|
||||
//
|
||||
// FIXME: Implement all operations in DoubleDouble, and delete these
|
||||
// semantics.
|
||||
// FIXME(eddyb) This shouldn't need to be `pub`, it's only used in bounds.
|
||||
pub struct FallbackS<F>(#[allow(unused)] F);
|
||||
type Fallback<F> = ieee::IeeeFloat<FallbackS<F>>;
|
||||
impl<F: Float> ieee::Semantics for FallbackS<F> {
|
||||
// Forbid any conversion to/from bits.
|
||||
const BITS: usize = 0;
|
||||
const PRECISION: usize = F::PRECISION * 2;
|
||||
const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt;
|
||||
const MIN_EXP: ExpInt = F::MIN_EXP as ExpInt + F::PRECISION as ExpInt;
|
||||
}
|
||||
|
||||
// Convert number to F. To avoid spurious underflows, we re-
|
||||
// normalize against the F exponent range first, and only *then*
|
||||
// truncate the mantissa. The result of that second conversion
|
||||
// may be inexact, but should never underflow.
|
||||
// FIXME(eddyb) This shouldn't need to be `pub`, it's only used in bounds.
|
||||
pub struct FallbackExtendedS<F>(#[allow(unused)] F);
|
||||
type FallbackExtended<F> = ieee::IeeeFloat<FallbackExtendedS<F>>;
|
||||
impl<F: Float> ieee::Semantics for FallbackExtendedS<F> {
|
||||
// Forbid any conversion to/from bits.
|
||||
const BITS: usize = 0;
|
||||
const PRECISION: usize = Fallback::<F>::PRECISION;
|
||||
const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt;
|
||||
}
|
||||
|
||||
impl<F: Float> From<Fallback<F>> for DoubleFloat<F>
|
||||
where
|
||||
F: FloatConvert<FallbackExtended<F>>,
|
||||
FallbackExtended<F>: FloatConvert<F>,
|
||||
{
|
||||
fn from(x: Fallback<F>) -> Self {
|
||||
let mut status;
|
||||
let mut loses_info = false;
|
||||
|
||||
let extended: FallbackExtended<F> = unpack!(status=, x.convert(&mut loses_info));
|
||||
assert_eq!((status, loses_info), (Status::OK, false));
|
||||
|
||||
let a = unpack!(status=, extended.convert(&mut loses_info));
|
||||
assert_eq!(status - Status::INEXACT, Status::OK);
|
||||
|
||||
// If conversion was exact or resulted in a special case, we're done;
|
||||
// just set the second double to zero. Otherwise, re-convert back to
|
||||
// the extended format and compute the difference. This now should
|
||||
// convert exactly to double.
|
||||
let b = if a.is_finite_non_zero() && loses_info {
|
||||
let u: FallbackExtended<F> = unpack!(status=, a.convert(&mut loses_info));
|
||||
assert_eq!((status, loses_info), (Status::OK, false));
|
||||
let v = unpack!(status=, extended - u);
|
||||
assert_eq!(status, Status::OK);
|
||||
let v = unpack!(status=, v.convert(&mut loses_info));
|
||||
assert_eq!((status, loses_info), (Status::OK, false));
|
||||
v
|
||||
} else {
|
||||
F::ZERO
|
||||
};
|
||||
|
||||
DoubleFloat(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FloatConvert<Self>> From<DoubleFloat<F>> for Fallback<F> {
|
||||
fn from(DoubleFloat(a, b): DoubleFloat<F>) -> Self {
|
||||
let mut status;
|
||||
let mut loses_info = false;
|
||||
|
||||
// Get the first F and convert to our format.
|
||||
let a = unpack!(status=, a.convert(&mut loses_info));
|
||||
assert_eq!((status, loses_info), (Status::OK, false));
|
||||
|
||||
// Unless we have a special case, add in second F.
|
||||
if a.is_finite_non_zero() {
|
||||
let b = unpack!(status=, b.convert(&mut loses_info));
|
||||
assert_eq!((status, loses_info), (Status::OK, false));
|
||||
|
||||
(a + b).value
|
||||
} else {
|
||||
a
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float_common_impls!(DoubleFloat<F>);
|
||||
|
||||
impl<F: Float> Neg for DoubleFloat<F> {
|
||||
type Output = Self;
|
||||
fn neg(self) -> Self {
|
||||
if self.1.is_finite_non_zero() {
|
||||
DoubleFloat(-self.0, -self.1)
|
||||
} else {
|
||||
DoubleFloat(-self.0, self.1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FloatConvert<Fallback<F>>> fmt::Display for DoubleFloat<F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&Fallback::from(*self), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FloatConvert<Fallback<F>>> Float for DoubleFloat<F>
|
||||
where
|
||||
Self: From<Fallback<F>>,
|
||||
{
|
||||
const BITS: usize = F::BITS * 2;
|
||||
const PRECISION: usize = Fallback::<F>::PRECISION;
|
||||
const MAX_EXP: ExpInt = Fallback::<F>::MAX_EXP;
|
||||
const MIN_EXP: ExpInt = Fallback::<F>::MIN_EXP;
|
||||
|
||||
const ZERO: Self = DoubleFloat(F::ZERO, F::ZERO);
|
||||
|
||||
const INFINITY: Self = DoubleFloat(F::INFINITY, F::ZERO);
|
||||
|
||||
// FIXME(eddyb) remove when qnan becomes const fn.
|
||||
const NAN: Self = DoubleFloat(F::NAN, F::ZERO);
|
||||
|
||||
fn qnan(payload: Option<u128>) -> Self {
|
||||
DoubleFloat(F::qnan(payload), F::ZERO)
|
||||
}
|
||||
|
||||
fn snan(payload: Option<u128>) -> Self {
|
||||
DoubleFloat(F::snan(payload), F::ZERO)
|
||||
}
|
||||
|
||||
fn largest() -> Self {
|
||||
let status;
|
||||
let mut r = DoubleFloat(F::largest(), F::largest());
|
||||
r.1 = r.1.scalbn(-(F::PRECISION as ExpInt + 1));
|
||||
r.1 = unpack!(status=, r.1.next_down());
|
||||
assert_eq!(status, Status::OK);
|
||||
r
|
||||
}
|
||||
|
||||
const SMALLEST: Self = DoubleFloat(F::SMALLEST, F::ZERO);
|
||||
|
||||
fn smallest_normalized() -> Self {
|
||||
DoubleFloat(F::smallest_normalized().scalbn(F::PRECISION as ExpInt), F::ZERO)
|
||||
}
|
||||
|
||||
// Implement addition, subtraction, multiplication and division based on:
|
||||
// "Software for Doubled-Precision Floating-Point Computations",
|
||||
// by Seppo Linnainmaa, ACM TOMS vol 7 no 3, September 1981, pages 272-283.
|
||||
|
||||
fn add_r(mut self, rhs: Self, round: Round) -> StatusAnd<Self> {
|
||||
match (self.category(), rhs.category()) {
|
||||
(Category::Infinity, Category::Infinity) => {
|
||||
if self.is_negative() != rhs.is_negative() {
|
||||
Status::INVALID_OP.and(Self::NAN.copy_sign(self))
|
||||
} else {
|
||||
Status::OK.and(self)
|
||||
}
|
||||
}
|
||||
|
||||
(_, Category::Zero) | (Category::NaN, _) | (Category::Infinity, Category::Normal) => {
|
||||
Status::OK.and(self)
|
||||
}
|
||||
|
||||
(Category::Zero, _) | (_, Category::NaN | Category::Infinity) => Status::OK.and(rhs),
|
||||
|
||||
(Category::Normal, Category::Normal) => {
|
||||
let mut status = Status::OK;
|
||||
let (a, aa, c, cc) = (self.0, self.1, rhs.0, rhs.1);
|
||||
let mut z = a;
|
||||
z = unpack!(status|=, z.add_r(c, round));
|
||||
if !z.is_finite() {
|
||||
if !z.is_infinite() {
|
||||
return status.and(DoubleFloat(z, F::ZERO));
|
||||
}
|
||||
status = Status::OK;
|
||||
let a_cmp_c = a.cmp_abs_normal(c);
|
||||
z = cc;
|
||||
z = unpack!(status|=, z.add_r(aa, round));
|
||||
if a_cmp_c == Ordering::Greater {
|
||||
// z = cc + aa + c + a;
|
||||
z = unpack!(status|=, z.add_r(c, round));
|
||||
z = unpack!(status|=, z.add_r(a, round));
|
||||
} else {
|
||||
// z = cc + aa + a + c;
|
||||
z = unpack!(status|=, z.add_r(a, round));
|
||||
z = unpack!(status|=, z.add_r(c, round));
|
||||
}
|
||||
if !z.is_finite() {
|
||||
return status.and(DoubleFloat(z, F::ZERO));
|
||||
}
|
||||
self.0 = z;
|
||||
let mut zz = aa;
|
||||
zz = unpack!(status|=, zz.add_r(cc, round));
|
||||
if a_cmp_c == Ordering::Greater {
|
||||
// self.1 = a - z + c + zz;
|
||||
self.1 = a;
|
||||
self.1 = unpack!(status|=, self.1.sub_r(z, round));
|
||||
self.1 = unpack!(status|=, self.1.add_r(c, round));
|
||||
self.1 = unpack!(status|=, self.1.add_r(zz, round));
|
||||
} else {
|
||||
// self.1 = c - z + a + zz;
|
||||
self.1 = c;
|
||||
self.1 = unpack!(status|=, self.1.sub_r(z, round));
|
||||
self.1 = unpack!(status|=, self.1.add_r(a, round));
|
||||
self.1 = unpack!(status|=, self.1.add_r(zz, round));
|
||||
}
|
||||
} else {
|
||||
// q = a - z;
|
||||
let mut q = a;
|
||||
q = unpack!(status|=, q.sub_r(z, round));
|
||||
|
||||
// zz = q + c + (a - (q + z)) + aa + cc;
|
||||
// Compute a - (q + z) as -((q + z) - a) to avoid temporary copies.
|
||||
let mut zz = q;
|
||||
zz = unpack!(status|=, zz.add_r(c, round));
|
||||
q = unpack!(status|=, q.add_r(z, round));
|
||||
q = unpack!(status|=, q.sub_r(a, round));
|
||||
q = -q;
|
||||
zz = unpack!(status|=, zz.add_r(q, round));
|
||||
zz = unpack!(status|=, zz.add_r(aa, round));
|
||||
zz = unpack!(status|=, zz.add_r(cc, round));
|
||||
if zz.is_zero() && !zz.is_negative() {
|
||||
return Status::OK.and(DoubleFloat(z, F::ZERO));
|
||||
}
|
||||
self.0 = z;
|
||||
self.0 = unpack!(status|=, self.0.add_r(zz, round));
|
||||
if !self.0.is_finite() {
|
||||
self.1 = F::ZERO;
|
||||
return status.and(self);
|
||||
}
|
||||
self.1 = z;
|
||||
self.1 = unpack!(status|=, self.1.sub_r(self.0, round));
|
||||
self.1 = unpack!(status|=, self.1.add_r(zz, round));
|
||||
}
|
||||
status.and(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mul_r(mut self, rhs: Self, round: Round) -> StatusAnd<Self> {
|
||||
// Interesting observation: For special categories, finding the lowest
|
||||
// common ancestor of the following layered graph gives the correct
|
||||
// return category:
|
||||
//
|
||||
// NaN
|
||||
// / \
|
||||
// Zero Inf
|
||||
// \ /
|
||||
// Normal
|
||||
//
|
||||
// e.g., NaN * NaN = NaN
|
||||
// Zero * Inf = NaN
|
||||
// Normal * Zero = Zero
|
||||
// Normal * Inf = Inf
|
||||
match (self.category(), rhs.category()) {
|
||||
(Category::NaN, _) => Status::OK.and(self),
|
||||
|
||||
(_, Category::NaN) => Status::OK.and(rhs),
|
||||
|
||||
(Category::Zero, Category::Infinity) | (Category::Infinity, Category::Zero) => {
|
||||
Status::OK.and(Self::NAN)
|
||||
}
|
||||
|
||||
(Category::Zero | Category::Infinity, _) => Status::OK.and(self),
|
||||
|
||||
(_, Category::Zero | Category::Infinity) => Status::OK.and(rhs),
|
||||
|
||||
(Category::Normal, Category::Normal) => {
|
||||
let mut status = Status::OK;
|
||||
let (a, b, c, d) = (self.0, self.1, rhs.0, rhs.1);
|
||||
// t = a * c
|
||||
let mut t = a;
|
||||
t = unpack!(status|=, t.mul_r(c, round));
|
||||
if !t.is_finite_non_zero() {
|
||||
return status.and(DoubleFloat(t, F::ZERO));
|
||||
}
|
||||
|
||||
// tau = fmsub(a, c, t), that is -fmadd(-a, c, t).
|
||||
let mut tau = a;
|
||||
tau = unpack!(status|=, tau.mul_add_r(c, -t, round));
|
||||
// v = a * d
|
||||
let mut v = a;
|
||||
v = unpack!(status|=, v.mul_r(d, round));
|
||||
// w = b * c
|
||||
let mut w = b;
|
||||
w = unpack!(status|=, w.mul_r(c, round));
|
||||
v = unpack!(status|=, v.add_r(w, round));
|
||||
// tau += v + w
|
||||
tau = unpack!(status|=, tau.add_r(v, round));
|
||||
// u = t + tau
|
||||
let mut u = t;
|
||||
u = unpack!(status|=, u.add_r(tau, round));
|
||||
|
||||
self.0 = u;
|
||||
if !u.is_finite() {
|
||||
self.1 = F::ZERO;
|
||||
} else {
|
||||
// self.1 = (t - u) + tau
|
||||
t = unpack!(status|=, t.sub_r(u, round));
|
||||
t = unpack!(status|=, t.add_r(tau, round));
|
||||
self.1 = t;
|
||||
}
|
||||
status.and(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mul_add_r(self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd<Self> {
|
||||
Fallback::from(self)
|
||||
.mul_add_r(Fallback::from(multiplicand), Fallback::from(addend), round)
|
||||
.map(Self::from)
|
||||
}
|
||||
|
||||
fn div_r(self, rhs: Self, round: Round) -> StatusAnd<Self> {
|
||||
Fallback::from(self).div_r(Fallback::from(rhs), round).map(Self::from)
|
||||
}
|
||||
|
||||
fn c_fmod(self, rhs: Self) -> StatusAnd<Self> {
|
||||
Fallback::from(self).c_fmod(Fallback::from(rhs)).map(Self::from)
|
||||
}
|
||||
|
||||
fn round_to_integral(self, round: Round) -> StatusAnd<Self> {
|
||||
Fallback::from(self).round_to_integral(round).map(Self::from)
|
||||
}
|
||||
|
||||
fn next_up(self) -> StatusAnd<Self> {
|
||||
Fallback::from(self).next_up().map(Self::from)
|
||||
}
|
||||
|
||||
fn from_bits(input: u128) -> Self {
|
||||
let (a, b) = (input, input >> F::BITS);
|
||||
DoubleFloat(F::from_bits(a & ((1 << F::BITS) - 1)), F::from_bits(b & ((1 << F::BITS) - 1)))
|
||||
}
|
||||
|
||||
fn from_u128_r(input: u128, round: Round) -> StatusAnd<Self> {
|
||||
Fallback::from_u128_r(input, round).map(Self::from)
|
||||
}
|
||||
|
||||
fn from_str_r(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError> {
|
||||
Fallback::from_str_r(s, round).map(|r| r.map(Self::from))
|
||||
}
|
||||
|
||||
fn to_bits(self) -> u128 {
|
||||
self.0.to_bits() | (self.1.to_bits() << F::BITS)
|
||||
}
|
||||
|
||||
fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd<u128> {
|
||||
Fallback::from(self).to_u128_r(width, round, is_exact)
|
||||
}
|
||||
|
||||
fn cmp_abs_normal(self, rhs: Self) -> Ordering {
|
||||
self.0.cmp_abs_normal(rhs.0).then_with(|| {
|
||||
let result = self.1.cmp_abs_normal(rhs.1);
|
||||
if result != Ordering::Equal {
|
||||
let against = self.0.is_negative() ^ self.1.is_negative();
|
||||
let rhs_against = rhs.0.is_negative() ^ rhs.1.is_negative();
|
||||
(!against)
|
||||
.cmp(&!rhs_against)
|
||||
.then_with(|| if against { result.reverse() } else { result })
|
||||
} else {
|
||||
result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn bitwise_eq(self, rhs: Self) -> bool {
|
||||
self.0.bitwise_eq(rhs.0) && self.1.bitwise_eq(rhs.1)
|
||||
}
|
||||
|
||||
fn is_negative(self) -> bool {
|
||||
self.0.is_negative()
|
||||
}
|
||||
|
||||
fn is_denormal(self) -> bool {
|
||||
self.category() == Category::Normal
|
||||
&& (self.0.is_denormal() || self.0.is_denormal() ||
|
||||
// (double)(Hi + Lo) == Hi defines a normal number.
|
||||
!(self.0 + self.1).value.bitwise_eq(self.0))
|
||||
}
|
||||
|
||||
fn is_signaling(self) -> bool {
|
||||
self.0.is_signaling()
|
||||
}
|
||||
|
||||
fn category(self) -> Category {
|
||||
self.0.category()
|
||||
}
|
||||
|
||||
fn get_exact_inverse(self) -> Option<Self> {
|
||||
Fallback::from(self).get_exact_inverse().map(Self::from)
|
||||
}
|
||||
|
||||
fn ilogb(self) -> ExpInt {
|
||||
self.0.ilogb()
|
||||
}
|
||||
|
||||
fn scalbn_r(self, exp: ExpInt, round: Round) -> Self {
|
||||
DoubleFloat(self.0.scalbn_r(exp, round), self.1.scalbn_r(exp, round))
|
||||
}
|
||||
|
||||
fn frexp_r(self, exp: &mut ExpInt, round: Round) -> Self {
|
||||
let a = self.0.frexp_r(exp, round);
|
||||
let mut b = self.1;
|
||||
if self.category() == Category::Normal {
|
||||
b = b.scalbn_r(-*exp, round);
|
||||
}
|
||||
DoubleFloat(a, b)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,530 +0,0 @@
|
|||
use rustc_apfloat::ppc::DoubleDouble;
|
||||
use rustc_apfloat::{Category, Float, Round};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double() {
|
||||
let test = DoubleDouble::ZERO;
|
||||
let expected = "0x0p+0".parse::<DoubleDouble>().unwrap();
|
||||
assert!(test.is_zero());
|
||||
assert!(!test.is_negative());
|
||||
assert!(test.bitwise_eq(expected));
|
||||
assert_eq!(0, test.to_bits());
|
||||
|
||||
let test = -DoubleDouble::ZERO;
|
||||
let expected = "-0x0p+0".parse::<DoubleDouble>().unwrap();
|
||||
assert!(test.is_zero());
|
||||
assert!(test.is_negative());
|
||||
assert!(test.bitwise_eq(expected));
|
||||
assert_eq!(0x8000000000000000, test.to_bits());
|
||||
|
||||
let test = "1.0".parse::<DoubleDouble>().unwrap();
|
||||
assert_eq!(0x3ff0000000000000, test.to_bits());
|
||||
|
||||
// LDBL_MAX
|
||||
let test = "1.79769313486231580793728971405301e+308".parse::<DoubleDouble>().unwrap();
|
||||
assert_eq!(0x7c8ffffffffffffe_7fefffffffffffff, test.to_bits());
|
||||
|
||||
// LDBL_MIN
|
||||
let test = "2.00416836000897277799610805135016e-292".parse::<DoubleDouble>().unwrap();
|
||||
assert_eq!(0x0000000000000000_0360000000000000, test.to_bits());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_add_special() {
|
||||
let data = [
|
||||
// (1 + 0) + (-1 + 0) = Category::Zero
|
||||
(0x3ff0000000000000, 0xbff0000000000000, Category::Zero, Round::NearestTiesToEven),
|
||||
// LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = Category::Infinity
|
||||
(
|
||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
||||
0x7948000000000000,
|
||||
Category::Infinity,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// FIXME: change the 4th 0x75effffffffffffe to 0x75efffffffffffff when
|
||||
// DoubleDouble's fallback is gone.
|
||||
// LDBL_MAX + (1.011111... >> (1023 - 106) + (1.1111111...0 >> (1023 -
|
||||
// 160))) = Category::Normal
|
||||
(
|
||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
||||
0x75effffffffffffe_7947ffffffffffff,
|
||||
Category::Normal,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = Category::Infinity
|
||||
(
|
||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
||||
Category::Infinity,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// NaN + (1 + 0) = Category::NaN
|
||||
(0x7ff8000000000000, 0x3ff0000000000000, Category::NaN, Round::NearestTiesToEven),
|
||||
];
|
||||
|
||||
for (op1, op2, expected, round) in data {
|
||||
{
|
||||
let mut a1 = DoubleDouble::from_bits(op1);
|
||||
let a2 = DoubleDouble::from_bits(op2);
|
||||
a1 = a1.add_r(a2, round).value;
|
||||
|
||||
assert_eq!(expected, a1.category(), "{:#x} + {:#x}", op1, op2);
|
||||
}
|
||||
{
|
||||
let a1 = DoubleDouble::from_bits(op1);
|
||||
let mut a2 = DoubleDouble::from_bits(op2);
|
||||
a2 = a2.add_r(a1, round).value;
|
||||
|
||||
assert_eq!(expected, a2.category(), "{:#x} + {:#x}", op2, op1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_add() {
|
||||
let data = [
|
||||
// (1 + 0) + (1e-105 + 0) = (1 + 1e-105)
|
||||
(
|
||||
0x3ff0000000000000,
|
||||
0x3960000000000000,
|
||||
0x3960000000000000_3ff0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// (1 + 0) + (1e-106 + 0) = (1 + 1e-106)
|
||||
(
|
||||
0x3ff0000000000000,
|
||||
0x3950000000000000,
|
||||
0x3950000000000000_3ff0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// (1 + 1e-106) + (1e-106 + 0) = (1 + 1e-105)
|
||||
(
|
||||
0x3950000000000000_3ff0000000000000,
|
||||
0x3950000000000000,
|
||||
0x3960000000000000_3ff0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// (1 + 0) + (epsilon + 0) = (1 + epsilon)
|
||||
(
|
||||
0x3ff0000000000000,
|
||||
0x0000000000000001,
|
||||
0x0000000000000001_3ff0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// FIXME: change 0xf950000000000000 to 0xf940000000000000, when
|
||||
// DoubleDouble's fallback is gone.
|
||||
// (DBL_MAX - 1 << (1023 - 105)) + (1 << (1023 - 53) + 0) = DBL_MAX +
|
||||
// 1.11111... << (1023 - 52)
|
||||
(
|
||||
0xf950000000000000_7fefffffffffffff,
|
||||
0x7c90000000000000,
|
||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// FIXME: change 0xf950000000000000 to 0xf940000000000000, when
|
||||
// DoubleDouble's fallback is gone.
|
||||
// (1 << (1023 - 53) + 0) + (DBL_MAX - 1 << (1023 - 105)) = DBL_MAX +
|
||||
// 1.11111... << (1023 - 52)
|
||||
(
|
||||
0x7c90000000000000,
|
||||
0xf950000000000000_7fefffffffffffff,
|
||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
];
|
||||
|
||||
for (op1, op2, expected, round) in data {
|
||||
{
|
||||
let mut a1 = DoubleDouble::from_bits(op1);
|
||||
let a2 = DoubleDouble::from_bits(op2);
|
||||
a1 = a1.add_r(a2, round).value;
|
||||
|
||||
assert_eq!(expected, a1.to_bits(), "{:#x} + {:#x}", op1, op2);
|
||||
}
|
||||
{
|
||||
let a1 = DoubleDouble::from_bits(op1);
|
||||
let mut a2 = DoubleDouble::from_bits(op2);
|
||||
a2 = a2.add_r(a1, round).value;
|
||||
|
||||
assert_eq!(expected, a2.to_bits(), "{:#x} + {:#x}", op2, op1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_subtract() {
|
||||
let data = [
|
||||
// (1 + 0) - (-1e-105 + 0) = (1 + 1e-105)
|
||||
(
|
||||
0x3ff0000000000000,
|
||||
0xb960000000000000,
|
||||
0x3960000000000000_3ff0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// (1 + 0) - (-1e-106 + 0) = (1 + 1e-106)
|
||||
(
|
||||
0x3ff0000000000000,
|
||||
0xb950000000000000,
|
||||
0x3950000000000000_3ff0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
];
|
||||
|
||||
for (op1, op2, expected, round) in data {
|
||||
let mut a1 = DoubleDouble::from_bits(op1);
|
||||
let a2 = DoubleDouble::from_bits(op2);
|
||||
a1 = a1.sub_r(a2, round).value;
|
||||
|
||||
assert_eq!(expected, a1.to_bits(), "{:#x} - {:#x}", op1, op2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_multiply_special() {
|
||||
let data = [
|
||||
// Category::NaN * Category::NaN = Category::NaN
|
||||
(0x7ff8000000000000, 0x7ff8000000000000, Category::NaN, Round::NearestTiesToEven),
|
||||
// Category::NaN * Category::Zero = Category::NaN
|
||||
(0x7ff8000000000000, 0, Category::NaN, Round::NearestTiesToEven),
|
||||
// Category::NaN * Category::Infinity = Category::NaN
|
||||
(0x7ff8000000000000, 0x7ff0000000000000, Category::NaN, Round::NearestTiesToEven),
|
||||
// Category::NaN * Category::Normal = Category::NaN
|
||||
(0x7ff8000000000000, 0x3ff0000000000000, Category::NaN, Round::NearestTiesToEven),
|
||||
// Category::Infinity * Category::Infinity = Category::Infinity
|
||||
(0x7ff0000000000000, 0x7ff0000000000000, Category::Infinity, Round::NearestTiesToEven),
|
||||
// Category::Infinity * Category::Zero = Category::NaN
|
||||
(0x7ff0000000000000, 0, Category::NaN, Round::NearestTiesToEven),
|
||||
// Category::Infinity * Category::Normal = Category::Infinity
|
||||
(0x7ff0000000000000, 0x3ff0000000000000, Category::Infinity, Round::NearestTiesToEven),
|
||||
// Category::Zero * Category::Zero = Category::Zero
|
||||
(0, 0, Category::Zero, Round::NearestTiesToEven),
|
||||
// Category::Zero * Category::Normal = Category::Zero
|
||||
(0, 0x3ff0000000000000, Category::Zero, Round::NearestTiesToEven),
|
||||
];
|
||||
|
||||
for (op1, op2, expected, round) in data {
|
||||
{
|
||||
let mut a1 = DoubleDouble::from_bits(op1);
|
||||
let a2 = DoubleDouble::from_bits(op2);
|
||||
a1 = a1.mul_r(a2, round).value;
|
||||
|
||||
assert_eq!(expected, a1.category(), "{:#x} * {:#x}", op1, op2);
|
||||
}
|
||||
{
|
||||
let a1 = DoubleDouble::from_bits(op1);
|
||||
let mut a2 = DoubleDouble::from_bits(op2);
|
||||
a2 = a2.mul_r(a1, round).value;
|
||||
|
||||
assert_eq!(expected, a2.category(), "{:#x} * {:#x}", op2, op1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_multiply() {
|
||||
let data = [
|
||||
// 1/3 * 3 = 1.0
|
||||
(
|
||||
0x3c75555555555556_3fd5555555555555,
|
||||
0x4008000000000000,
|
||||
0x3ff0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// (1 + epsilon) * (1 + 0) = Category::Zero
|
||||
(
|
||||
0x0000000000000001_3ff0000000000000,
|
||||
0x3ff0000000000000,
|
||||
0x0000000000000001_3ff0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// (1 + epsilon) * (1 + epsilon) = 1 + 2 * epsilon
|
||||
(
|
||||
0x0000000000000001_3ff0000000000000,
|
||||
0x0000000000000001_3ff0000000000000,
|
||||
0x0000000000000002_3ff0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// -(1 + epsilon) * (1 + epsilon) = -1
|
||||
(
|
||||
0x0000000000000001_bff0000000000000,
|
||||
0x0000000000000001_3ff0000000000000,
|
||||
0xbff0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// (0.5 + 0) * (1 + 2 * epsilon) = 0.5 + epsilon
|
||||
(
|
||||
0x3fe0000000000000,
|
||||
0x0000000000000002_3ff0000000000000,
|
||||
0x0000000000000001_3fe0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// (0.5 + 0) * (1 + epsilon) = 0.5
|
||||
(
|
||||
0x3fe0000000000000,
|
||||
0x0000000000000001_3ff0000000000000,
|
||||
0x3fe0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// __LDBL_MAX__ * (1 + 1 << 106) = inf
|
||||
(
|
||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
||||
0x3950000000000000_3ff0000000000000,
|
||||
0x7ff0000000000000,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// __LDBL_MAX__ * (1 + 1 << 107) > __LDBL_MAX__, but not inf, yes =_=|||
|
||||
(
|
||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
||||
0x3940000000000000_3ff0000000000000,
|
||||
0x7c8fffffffffffff_7fefffffffffffff,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
// __LDBL_MAX__ * (1 + 1 << 108) = __LDBL_MAX__
|
||||
(
|
||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
||||
0x3930000000000000_3ff0000000000000,
|
||||
0x7c8ffffffffffffe_7fefffffffffffff,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
];
|
||||
|
||||
for (op1, op2, expected, round) in data {
|
||||
{
|
||||
let mut a1 = DoubleDouble::from_bits(op1);
|
||||
let a2 = DoubleDouble::from_bits(op2);
|
||||
a1 = a1.mul_r(a2, round).value;
|
||||
|
||||
assert_eq!(expected, a1.to_bits(), "{:#x} * {:#x}", op1, op2);
|
||||
}
|
||||
{
|
||||
let a1 = DoubleDouble::from_bits(op1);
|
||||
let mut a2 = DoubleDouble::from_bits(op2);
|
||||
a2 = a2.mul_r(a1, round).value;
|
||||
|
||||
assert_eq!(expected, a2.to_bits(), "{:#x} * {:#x}", op2, op1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_divide() {
|
||||
// FIXME: Only a sanity check for now. Add more edge cases when the
|
||||
// double-double algorithm is implemented.
|
||||
let data = [
|
||||
// 1 / 3 = 1/3
|
||||
(
|
||||
0x3ff0000000000000,
|
||||
0x4008000000000000,
|
||||
0x3c75555555555556_3fd5555555555555,
|
||||
Round::NearestTiesToEven,
|
||||
),
|
||||
];
|
||||
|
||||
for (op1, op2, expected, round) in data {
|
||||
let mut a1 = DoubleDouble::from_bits(op1);
|
||||
let a2 = DoubleDouble::from_bits(op2);
|
||||
a1 = a1.div_r(a2, round).value;
|
||||
|
||||
assert_eq!(expected, a1.to_bits(), "{:#x} / {:#x}", op1, op2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_remainder() {
|
||||
let data = [
|
||||
// ieee_rem(3.0 + 3.0 << 53, 1.25 + 1.25 << 53) = (0.5 + 0.5 << 53)
|
||||
(
|
||||
0x3cb8000000000000_4008000000000000,
|
||||
0x3ca4000000000000_3ff4000000000000,
|
||||
0x3c90000000000000_3fe0000000000000,
|
||||
),
|
||||
// ieee_rem(3.0 + 3.0 << 53, 1.75 + 1.75 << 53) = (-0.5 - 0.5 << 53)
|
||||
(
|
||||
0x3cb8000000000000_4008000000000000,
|
||||
0x3cac000000000000_3ffc000000000000,
|
||||
0xbc90000000000000_bfe0000000000000,
|
||||
),
|
||||
];
|
||||
|
||||
for (op1, op2, expected) in data {
|
||||
let a1 = DoubleDouble::from_bits(op1);
|
||||
let a2 = DoubleDouble::from_bits(op2);
|
||||
let result = a1.ieee_rem(a2).value;
|
||||
|
||||
assert_eq!(expected, result.to_bits(), "ieee_rem({:#x}, {:#x})", op1, op2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_mod() {
|
||||
let data = [
|
||||
// mod(3.0 + 3.0 << 53, 1.25 + 1.25 << 53) = (0.5 + 0.5 << 53)
|
||||
(
|
||||
0x3cb8000000000000_4008000000000000,
|
||||
0x3ca4000000000000_3ff4000000000000,
|
||||
0x3c90000000000000_3fe0000000000000,
|
||||
),
|
||||
// mod(3.0 + 3.0 << 53, 1.75 + 1.75 << 53) = (1.25 + 1.25 << 53)
|
||||
// 0xbc98000000000000 doesn't seem right, but it's what we currently have.
|
||||
// FIXME: investigate
|
||||
(
|
||||
0x3cb8000000000000_4008000000000000,
|
||||
0x3cac000000000000_3ffc000000000000,
|
||||
0xbc98000000000000_3ff4000000000001,
|
||||
),
|
||||
];
|
||||
|
||||
for (op1, op2, expected) in data {
|
||||
let a1 = DoubleDouble::from_bits(op1);
|
||||
let a2 = DoubleDouble::from_bits(op2);
|
||||
let r = (a1 % a2).value;
|
||||
|
||||
assert_eq!(expected, r.to_bits(), "fmod({:#x}, {:#x})", op1, op2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_fma() {
|
||||
// Sanity check for now.
|
||||
let mut a = "2".parse::<DoubleDouble>().unwrap();
|
||||
a = a.mul_add("3".parse::<DoubleDouble>().unwrap(), "4".parse::<DoubleDouble>().unwrap()).value;
|
||||
assert_eq!(Some(Ordering::Equal), "10".parse::<DoubleDouble>().unwrap().partial_cmp(&a));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_round_to_integral() {
|
||||
{
|
||||
let a = "1.5".parse::<DoubleDouble>().unwrap();
|
||||
let a = a.round_to_integral(Round::NearestTiesToEven).value;
|
||||
assert_eq!(Some(Ordering::Equal), "2".parse::<DoubleDouble>().unwrap().partial_cmp(&a));
|
||||
}
|
||||
{
|
||||
let a = "2.5".parse::<DoubleDouble>().unwrap();
|
||||
let a = a.round_to_integral(Round::NearestTiesToEven).value;
|
||||
assert_eq!(Some(Ordering::Equal), "2".parse::<DoubleDouble>().unwrap().partial_cmp(&a));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_compare() {
|
||||
let data = [
|
||||
// (1 + 0) = (1 + 0)
|
||||
(0x3ff0000000000000, 0x3ff0000000000000, Some(Ordering::Equal)),
|
||||
// (1 + 0) < (1.00...1 + 0)
|
||||
(0x3ff0000000000000, 0x3ff0000000000001, Some(Ordering::Less)),
|
||||
// (1.00...1 + 0) > (1 + 0)
|
||||
(0x3ff0000000000001, 0x3ff0000000000000, Some(Ordering::Greater)),
|
||||
// (1 + 0) < (1 + epsilon)
|
||||
(0x3ff0000000000000, 0x0000000000000001_3ff0000000000001, Some(Ordering::Less)),
|
||||
// NaN != NaN
|
||||
(0x7ff8000000000000, 0x7ff8000000000000, None),
|
||||
// (1 + 0) != NaN
|
||||
(0x3ff0000000000000, 0x7ff8000000000000, None),
|
||||
// Inf = Inf
|
||||
(0x7ff0000000000000, 0x7ff0000000000000, Some(Ordering::Equal)),
|
||||
];
|
||||
|
||||
for (op1, op2, expected) in data {
|
||||
let a1 = DoubleDouble::from_bits(op1);
|
||||
let a2 = DoubleDouble::from_bits(op2);
|
||||
assert_eq!(expected, a1.partial_cmp(&a2), "compare({:#x}, {:#x})", op1, op2,);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_bitwise_eq() {
|
||||
let data = [
|
||||
// (1 + 0) = (1 + 0)
|
||||
(0x3ff0000000000000, 0x3ff0000000000000, true),
|
||||
// (1 + 0) != (1.00...1 + 0)
|
||||
(0x3ff0000000000000, 0x3ff0000000000001, false),
|
||||
// NaN = NaN
|
||||
(0x7ff8000000000000, 0x7ff8000000000000, true),
|
||||
// NaN != NaN with a different bit pattern
|
||||
(0x7ff8000000000000, 0x3ff0000000000000_7ff8000000000000, false),
|
||||
// Inf = Inf
|
||||
(0x7ff0000000000000, 0x7ff0000000000000, true),
|
||||
];
|
||||
|
||||
for (op1, op2, expected) in data {
|
||||
let a1 = DoubleDouble::from_bits(op1);
|
||||
let a2 = DoubleDouble::from_bits(op2);
|
||||
assert_eq!(expected, a1.bitwise_eq(a2), "{:#x} = {:#x}", op1, op2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_change_sign() {
|
||||
let float = DoubleDouble::from_bits(0xbcb0000000000000_400f000000000000);
|
||||
{
|
||||
let actual = float.copy_sign("1".parse::<DoubleDouble>().unwrap());
|
||||
assert_eq!(0xbcb0000000000000_400f000000000000, actual.to_bits());
|
||||
}
|
||||
{
|
||||
let actual = float.copy_sign("-1".parse::<DoubleDouble>().unwrap());
|
||||
assert_eq!(0x3cb0000000000000_c00f000000000000, actual.to_bits());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_factories() {
|
||||
assert_eq!(0, DoubleDouble::ZERO.to_bits());
|
||||
assert_eq!(0x7c8ffffffffffffe_7fefffffffffffff, DoubleDouble::largest().to_bits());
|
||||
assert_eq!(0x0000000000000001, DoubleDouble::SMALLEST.to_bits());
|
||||
assert_eq!(0x0360000000000000, DoubleDouble::smallest_normalized().to_bits());
|
||||
assert_eq!(0x0000000000000000_8000000000000000, (-DoubleDouble::ZERO).to_bits());
|
||||
assert_eq!(0xfc8ffffffffffffe_ffefffffffffffff, (-DoubleDouble::largest()).to_bits());
|
||||
assert_eq!(0x0000000000000000_8000000000000001, (-DoubleDouble::SMALLEST).to_bits());
|
||||
assert_eq!(
|
||||
0x0000000000000000_8360000000000000,
|
||||
(-DoubleDouble::smallest_normalized()).to_bits()
|
||||
);
|
||||
assert!(DoubleDouble::SMALLEST.is_smallest());
|
||||
assert!(DoubleDouble::largest().is_largest());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_is_denormal() {
|
||||
assert!(DoubleDouble::SMALLEST.is_denormal());
|
||||
assert!(!DoubleDouble::largest().is_denormal());
|
||||
assert!(!DoubleDouble::smallest_normalized().is_denormal());
|
||||
{
|
||||
// (4 + 3) is not normalized
|
||||
let data = 0x4008000000000000_4010000000000000;
|
||||
assert!(DoubleDouble::from_bits(data).is_denormal());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_exact_inverse() {
|
||||
assert!(
|
||||
"2.0"
|
||||
.parse::<DoubleDouble>()
|
||||
.unwrap()
|
||||
.get_exact_inverse()
|
||||
.unwrap()
|
||||
.bitwise_eq("0.5".parse::<DoubleDouble>().unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_scalbn() {
|
||||
// 3.0 + 3.0 << 53
|
||||
let input = 0x3cb8000000000000_4008000000000000;
|
||||
let result = DoubleDouble::from_bits(input).scalbn(1);
|
||||
// 6.0 + 6.0 << 53
|
||||
assert_eq!(0x3cc8000000000000_4018000000000000, result.to_bits());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ppc_double_double_frexp() {
|
||||
// 3.0 + 3.0 << 53
|
||||
let input = 0x3cb8000000000000_4008000000000000;
|
||||
let mut exp = 0;
|
||||
// 0.75 + 0.75 << 53
|
||||
let result = DoubleDouble::from_bits(input).frexp(&mut exp);
|
||||
assert_eq!(2, exp);
|
||||
assert_eq!(0x3c98000000000000_3fe8000000000000, result.to_bits());
|
||||
}
|
||||
|
|
@ -362,7 +362,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
|
|||
|
||||
impl<B: WriteBackendMethods> CodegenContext<B> {
|
||||
pub fn create_diag_handler(&self) -> Handler {
|
||||
Handler::with_emitter(true, None, Box::new(self.diag_emitter.clone()), None)
|
||||
Handler::with_emitter(Box::new(self.diag_emitter.clone()))
|
||||
}
|
||||
|
||||
pub fn config(&self, kind: ModuleKind) -> &ModuleConfig {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
tracing = "0.1"
|
||||
either = "1"
|
||||
rustc_apfloat = { path = "../rustc_apfloat" }
|
||||
rustc_apfloat = "0.2.0"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
|||
ecx.push_stack_frame(
|
||||
cid.instance,
|
||||
body,
|
||||
&ret.into(),
|
||||
&ret.clone().into(),
|
||||
StackPopCleanup::Root { cleanup: false },
|
||||
)?;
|
||||
|
||||
|
|
@ -356,7 +356,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
|||
// Since evaluation had no errors, validate the resulting constant.
|
||||
// This is a separate `try` block to provide more targeted error reporting.
|
||||
let validation: Result<_, InterpErrorInfo<'_>> = try {
|
||||
let mut ref_tracking = RefTracking::new(mplace);
|
||||
let mut ref_tracking = RefTracking::new(mplace.clone());
|
||||
let mut inner = false;
|
||||
while let Some((mplace, path)) = ref_tracking.todo.pop() {
|
||||
let mode = match tcx.static_mutability(cid.instance.def_id()) {
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
|
|||
|
||||
let mut msg_place = self.deref_operand(&args[0])?;
|
||||
while msg_place.layout.ty.is_ref() {
|
||||
msg_place = self.deref_operand(&msg_place.into())?;
|
||||
msg_place = self.deref_operand(&msg_place)?;
|
||||
}
|
||||
|
||||
let msg = Symbol::intern(self.read_str(&msg_place)?);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::const_eval::CanAccessStatics;
|
|||
use crate::interpret::MPlaceTy;
|
||||
use crate::interpret::{
|
||||
intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
|
||||
MemoryKind, PlaceTy, Projectable, Scalar,
|
||||
MemoryKind, Place, Projectable, Scalar,
|
||||
};
|
||||
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
||||
use rustc_span::source_map::DUMMY_SP;
|
||||
|
|
@ -21,7 +21,7 @@ fn branches<'tcx>(
|
|||
) -> ValTreeCreationResult<'tcx> {
|
||||
let place = match variant {
|
||||
Some(variant) => ecx.project_downcast(place, variant).unwrap(),
|
||||
None => *place,
|
||||
None => place.clone(),
|
||||
};
|
||||
let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
|
||||
debug!(?place, ?variant);
|
||||
|
|
@ -86,7 +86,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
|
|||
Ok(ty::ValTree::zst())
|
||||
}
|
||||
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
|
||||
let Ok(val) = ecx.read_immediate(&place.into()) else {
|
||||
let Ok(val) = ecx.read_immediate(place) else {
|
||||
return Err(ValTreeCreationError::Other);
|
||||
};
|
||||
let val = val.to_scalar();
|
||||
|
|
@ -102,7 +102,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
|
|||
ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType),
|
||||
|
||||
ty::Ref(_, _, _) => {
|
||||
let Ok(derefd_place)= ecx.deref_operand(&place.into()) else {
|
||||
let Ok(derefd_place)= ecx.deref_operand(place) else {
|
||||
return Err(ValTreeCreationError::Other);
|
||||
};
|
||||
debug!(?derefd_place);
|
||||
|
|
@ -130,7 +130,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
|
|||
bug!("uninhabited types should have errored and never gotten converted to valtree")
|
||||
}
|
||||
|
||||
let Ok(variant) = ecx.read_discriminant(&place.into()) else {
|
||||
let Ok(variant) = ecx.read_discriminant(place) else {
|
||||
return Err(ValTreeCreationError::Other);
|
||||
};
|
||||
branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes)
|
||||
|
|
@ -280,7 +280,7 @@ pub fn valtree_to_const_value<'tcx>(
|
|||
),
|
||||
},
|
||||
ty::Ref(_, _, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => {
|
||||
let mut place = match ty.kind() {
|
||||
let place = match ty.kind() {
|
||||
ty::Ref(_, inner_ty, _) => {
|
||||
// Need to create a place for the pointee to fill for Refs
|
||||
create_pointee_place(&mut ecx, *inner_ty, valtree)
|
||||
|
|
@ -289,8 +289,8 @@ pub fn valtree_to_const_value<'tcx>(
|
|||
};
|
||||
debug!(?place);
|
||||
|
||||
valtree_into_mplace(&mut ecx, &mut place, valtree);
|
||||
dump_place(&ecx, place.into());
|
||||
valtree_into_mplace(&mut ecx, &place, valtree);
|
||||
dump_place(&ecx, &place);
|
||||
intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap();
|
||||
|
||||
match ty.kind() {
|
||||
|
|
@ -329,7 +329,7 @@ pub fn valtree_to_const_value<'tcx>(
|
|||
#[instrument(skip(ecx), level = "debug")]
|
||||
fn valtree_into_mplace<'tcx>(
|
||||
ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
|
||||
place: &mut MPlaceTy<'tcx>,
|
||||
place: &MPlaceTy<'tcx>,
|
||||
valtree: ty::ValTree<'tcx>,
|
||||
) {
|
||||
// This will match on valtree and write the value(s) corresponding to the ValTree
|
||||
|
|
@ -345,14 +345,14 @@ fn valtree_into_mplace<'tcx>(
|
|||
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
|
||||
let scalar_int = valtree.unwrap_leaf();
|
||||
debug!("writing trivial valtree {:?} to place {:?}", scalar_int, place);
|
||||
ecx.write_immediate(Immediate::Scalar(scalar_int.into()), &place.into()).unwrap();
|
||||
ecx.write_immediate(Immediate::Scalar(scalar_int.into()), place).unwrap();
|
||||
}
|
||||
ty::Ref(_, inner_ty, _) => {
|
||||
let mut pointee_place = create_pointee_place(ecx, *inner_ty, valtree);
|
||||
let pointee_place = create_pointee_place(ecx, *inner_ty, valtree);
|
||||
debug!(?pointee_place);
|
||||
|
||||
valtree_into_mplace(ecx, &mut pointee_place, valtree);
|
||||
dump_place(ecx, pointee_place.into());
|
||||
valtree_into_mplace(ecx, &pointee_place, valtree);
|
||||
dump_place(ecx, &pointee_place);
|
||||
intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap();
|
||||
|
||||
let imm = match inner_ty.kind() {
|
||||
|
|
@ -369,7 +369,7 @@ fn valtree_into_mplace<'tcx>(
|
|||
};
|
||||
debug!(?imm);
|
||||
|
||||
ecx.write_immediate(imm, &place.into()).unwrap();
|
||||
ecx.write_immediate(imm, place).unwrap();
|
||||
}
|
||||
ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Str | ty::Slice(_) => {
|
||||
let branches = valtree.unwrap_branch();
|
||||
|
|
@ -389,7 +389,7 @@ fn valtree_into_mplace<'tcx>(
|
|||
Some(variant_idx),
|
||||
)
|
||||
}
|
||||
_ => (*place, branches, None),
|
||||
_ => (place.clone(), branches, None),
|
||||
};
|
||||
debug!(?place_adjusted, ?branches);
|
||||
|
||||
|
|
@ -398,7 +398,7 @@ fn valtree_into_mplace<'tcx>(
|
|||
for (i, inner_valtree) in branches.iter().enumerate() {
|
||||
debug!(?i, ?inner_valtree);
|
||||
|
||||
let mut place_inner = match ty.kind() {
|
||||
let place_inner = match ty.kind() {
|
||||
ty::Str | ty::Slice(_) => ecx.project_index(place, i as u64).unwrap(),
|
||||
_ if !ty.is_sized(*ecx.tcx, ty::ParamEnv::empty())
|
||||
&& i == branches.len() - 1 =>
|
||||
|
|
@ -443,25 +443,25 @@ fn valtree_into_mplace<'tcx>(
|
|||
};
|
||||
|
||||
debug!(?place_inner);
|
||||
valtree_into_mplace(ecx, &mut place_inner, *inner_valtree);
|
||||
dump_place(&ecx, place_inner.into());
|
||||
valtree_into_mplace(ecx, &place_inner, *inner_valtree);
|
||||
dump_place(&ecx, &place_inner);
|
||||
}
|
||||
|
||||
debug!("dump of place_adjusted:");
|
||||
dump_place(ecx, place_adjusted.into());
|
||||
dump_place(ecx, &place_adjusted);
|
||||
|
||||
if let Some(variant_idx) = variant_idx {
|
||||
// don't forget filling the place with the discriminant of the enum
|
||||
ecx.write_discriminant(variant_idx, &place.into()).unwrap();
|
||||
ecx.write_discriminant(variant_idx, place).unwrap();
|
||||
}
|
||||
|
||||
debug!("dump of place after writing discriminant:");
|
||||
dump_place(ecx, place.into());
|
||||
dump_place(ecx, place);
|
||||
}
|
||||
_ => bug!("shouldn't have created a ValTree for {:?}", ty),
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: PlaceTy<'tcx>) {
|
||||
trace!("{:?}", ecx.dump_place(*place));
|
||||
fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>) {
|
||||
trace!("{:?}", ecx.dump_place(Place::Ptr(**place)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
|
||||
let src = self.read_immediate(&src)?;
|
||||
let src = self.read_immediate(src)?;
|
||||
let res = self.ptr_to_ptr(&src, cast_ty)?;
|
||||
self.write_immediate(res, dest)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use rustc_middle::{mir, ty};
|
|||
use rustc_target::abi::{self, TagEncoding};
|
||||
use rustc_target::abi::{VariantIdx, Variants};
|
||||
|
||||
use super::{ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Scalar};
|
||||
use super::{ImmTy, InterpCx, InterpResult, Machine, Readable, Scalar, Writeable};
|
||||
|
||||
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
/// Writes the discriminant of the given variant.
|
||||
|
|
@ -13,7 +13,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
pub fn write_discriminant(
|
||||
&mut self,
|
||||
variant_index: VariantIdx,
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Layout computation excludes uninhabited variants from consideration
|
||||
// therefore there's no way to represent those variants in the given layout.
|
||||
|
|
@ -21,11 +21,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// discriminant, so we cannot do anything here.
|
||||
// When evaluating we will always error before even getting here, but ConstProp 'executes'
|
||||
// dead code, so we cannot ICE here.
|
||||
if dest.layout.for_variant(self, variant_index).abi.is_uninhabited() {
|
||||
if dest.layout().for_variant(self, variant_index).abi.is_uninhabited() {
|
||||
throw_ub!(UninhabitedEnumVariantWritten(variant_index))
|
||||
}
|
||||
|
||||
match dest.layout.variants {
|
||||
match dest.layout().variants {
|
||||
abi::Variants::Single { index } => {
|
||||
assert_eq!(index, variant_index);
|
||||
}
|
||||
|
|
@ -38,8 +38,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// No need to validate that the discriminant here because the
|
||||
// `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
|
||||
|
||||
let discr_val =
|
||||
dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val;
|
||||
let discr_val = dest
|
||||
.layout()
|
||||
.ty
|
||||
.discriminant_for_variant(*self.tcx, variant_index)
|
||||
.unwrap()
|
||||
.val;
|
||||
|
||||
// raw discriminants for enums are isize or bigger during
|
||||
// their computation, but the in-memory tag is the smallest possible
|
||||
|
|
@ -92,11 +96,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
#[instrument(skip(self), level = "trace")]
|
||||
pub fn read_discriminant(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
op: &impl Readable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, VariantIdx> {
|
||||
trace!("read_discriminant_value {:#?}", op.layout);
|
||||
let ty = op.layout().ty;
|
||||
trace!("read_discriminant_value {:#?}", op.layout());
|
||||
// Get type and layout of the discriminant.
|
||||
let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?;
|
||||
let discr_layout = self.layout_of(ty.discriminant_ty(*self.tcx))?;
|
||||
trace!("discriminant type: {:?}", discr_layout.ty);
|
||||
|
||||
// We use "discriminant" to refer to the value associated with a particular enum variant.
|
||||
|
|
@ -104,20 +109,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// declared list of variants -- they can differ with explicitly assigned discriminants.
|
||||
// We use "tag" to refer to how the discriminant is encoded in memory, which can be either
|
||||
// straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
|
||||
let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants {
|
||||
let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout().variants {
|
||||
Variants::Single { index } => {
|
||||
// Do some extra checks on enums.
|
||||
if op.layout.ty.is_enum() {
|
||||
if ty.is_enum() {
|
||||
// Hilariously, `Single` is used even for 0-variant enums.
|
||||
// (See https://github.com/rust-lang/rust/issues/89765).
|
||||
if matches!(op.layout.ty.kind(), ty::Adt(def, ..) if def.variants().is_empty())
|
||||
{
|
||||
if matches!(ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) {
|
||||
throw_ub!(UninhabitedEnumVariantRead(index))
|
||||
}
|
||||
// For consisteny with `write_discriminant`, and to make sure that
|
||||
// `project_downcast` cannot fail due to strange layouts, we declare immediate UB
|
||||
// for uninhabited variants.
|
||||
if op.layout.for_variant(self, index).abi.is_uninhabited() {
|
||||
if op.layout().for_variant(self, index).abi.is_uninhabited() {
|
||||
throw_ub!(UninhabitedEnumVariantRead(index))
|
||||
}
|
||||
}
|
||||
|
|
@ -163,7 +167,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
self.cast_from_int_like(scalar, tag_val.layout, discr_layout.ty).unwrap();
|
||||
let discr_bits = discr_val.assert_bits(discr_layout.size);
|
||||
// Convert discriminant to variant index, and catch invalid discriminants.
|
||||
let index = match *op.layout.ty.kind() {
|
||||
let index = match *ty.kind() {
|
||||
ty::Adt(adt, _) => {
|
||||
adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
|
||||
}
|
||||
|
|
@ -217,12 +221,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
.checked_add(variant_index_relative)
|
||||
.expect("overflow computing absolute variant idx"),
|
||||
);
|
||||
let variants = op
|
||||
.layout
|
||||
.ty
|
||||
.ty_adt_def()
|
||||
.expect("tagged layout for non adt")
|
||||
.variants();
|
||||
let variants =
|
||||
ty.ty_adt_def().expect("tagged layout for non adt").variants();
|
||||
assert!(variant_index < variants.next_index());
|
||||
variant_index
|
||||
} else {
|
||||
|
|
@ -237,7 +237,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
};
|
||||
// For consisteny with `write_discriminant`, and to make sure that `project_downcast` cannot fail due to strange layouts, we declare immediate UB for uninhabited variants.
|
||||
if op.layout.for_variant(self, index).abi.is_uninhabited() {
|
||||
if op.layout().for_variant(self, index).abi.is_uninhabited() {
|
||||
throw_ub!(UninhabitedEnumVariantRead(index))
|
||||
}
|
||||
Ok(index)
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
|||
let tcx = self.ecx.tcx;
|
||||
let ty = mplace.layout.ty;
|
||||
if let ty::Ref(_, referenced_ty, ref_mutability) = *ty.kind() {
|
||||
let value = self.ecx.read_immediate(&mplace.into())?;
|
||||
let value = self.ecx.read_immediate(mplace)?;
|
||||
let mplace = self.ecx.ref_to_mplace(&value)?;
|
||||
assert_eq!(mplace.layout.ty, referenced_ty);
|
||||
// Handle trait object vtables.
|
||||
|
|
@ -358,7 +358,7 @@ pub fn intern_const_alloc_recursive<
|
|||
Some(ret.layout.ty),
|
||||
);
|
||||
|
||||
ref_tracking.track((*ret, base_intern_mode), || ());
|
||||
ref_tracking.track((ret.clone(), base_intern_mode), || ());
|
||||
|
||||
while let Some(((mplace, mode), _)) = ref_tracking.todo.pop() {
|
||||
let res = InternVisitor {
|
||||
|
|
@ -464,7 +464,7 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
|
|||
) -> InterpResult<'tcx, ()>,
|
||||
) -> InterpResult<'tcx, ConstAllocation<'tcx>> {
|
||||
let dest = self.allocate(layout, MemoryKind::Stack)?;
|
||||
f(self, &dest.into())?;
|
||||
f(self, &dest.clone().into())?;
|
||||
let mut alloc = self.memory.alloc_map.remove(&dest.ptr.provenance.unwrap()).unwrap().1;
|
||||
alloc.mutability = Mutability::Not;
|
||||
Ok(self.tcx.mk_const_alloc(alloc))
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
sym::discriminant_value => {
|
||||
let place = self.deref_operand(&args[0])?;
|
||||
let variant = self.read_discriminant(&place.into())?;
|
||||
let variant = self.read_discriminant(&place)?;
|
||||
let discr = self.discriminant_for_variant(place.layout, variant)?;
|
||||
self.write_scalar(discr, dest)?;
|
||||
}
|
||||
|
|
@ -432,7 +432,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
} else {
|
||||
self.project_index(&input, i)?.into()
|
||||
};
|
||||
self.copy_op(&value, &place.into(), /*allow_transmute*/ false)?;
|
||||
self.copy_op(&value, &place, /*allow_transmute*/ false)?;
|
||||
}
|
||||
}
|
||||
sym::simd_extract => {
|
||||
|
|
@ -445,7 +445,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
input_len
|
||||
);
|
||||
self.copy_op(
|
||||
&self.project_index(&input, index)?.into(),
|
||||
&self.project_index(&input, index)?,
|
||||
dest,
|
||||
/*allow_transmute*/ false,
|
||||
)?;
|
||||
|
|
@ -610,7 +610,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
|
||||
nonoverlapping: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
let count = self.read_target_usize(&count)?;
|
||||
let count = self.read_target_usize(count)?;
|
||||
let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?;
|
||||
let (size, align) = (layout.size, layout.align.abi);
|
||||
// `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
|
||||
|
|
@ -622,8 +622,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
)
|
||||
})?;
|
||||
|
||||
let src = self.read_pointer(&src)?;
|
||||
let dst = self.read_pointer(&dst)?;
|
||||
let src = self.read_pointer(src)?;
|
||||
let dst = self.read_pointer(dst)?;
|
||||
|
||||
self.mem_copy(src, align, dst, align, size, nonoverlapping)
|
||||
}
|
||||
|
|
@ -636,9 +636,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
) -> InterpResult<'tcx> {
|
||||
let layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap().ty)?;
|
||||
|
||||
let dst = self.read_pointer(&dst)?;
|
||||
let byte = self.read_scalar(&byte)?.to_u8()?;
|
||||
let count = self.read_target_usize(&count)?;
|
||||
let dst = self.read_pointer(dst)?;
|
||||
let byte = self.read_scalar(byte)?.to_u8()?;
|
||||
let count = self.read_target_usize(count)?;
|
||||
|
||||
// `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
|
||||
// but no actual allocation can be big enough for the difference to be noticeable.
|
||||
|
|
|
|||
|
|
@ -101,11 +101,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let location = self.allocate(loc_layout, MemoryKind::CallerLocation).unwrap();
|
||||
|
||||
// Initialize fields.
|
||||
self.write_immediate(file.to_ref(self), &self.project_field(&location, 0).unwrap().into())
|
||||
self.write_immediate(file.to_ref(self), &self.project_field(&location, 0).unwrap())
|
||||
.expect("writing to memory we just allocated cannot fail");
|
||||
self.write_scalar(line, &self.project_field(&location, 1).unwrap().into())
|
||||
self.write_scalar(line, &self.project_field(&location, 1).unwrap())
|
||||
.expect("writing to memory we just allocated cannot fail");
|
||||
self.write_scalar(col, &self.project_field(&location, 2).unwrap().into())
|
||||
self.write_scalar(col, &self.project_field(&location, 2).unwrap())
|
||||
.expect("writing to memory we just allocated cannot fail");
|
||||
|
||||
location
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ pub use self::eval_context::{Frame, FrameInfo, InterpCx, LocalState, LocalValue,
|
|||
pub use self::intern::{intern_const_alloc_recursive, InternKind};
|
||||
pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
|
||||
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
|
||||
pub use self::operand::{ImmTy, Immediate, OpTy, Operand};
|
||||
pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy};
|
||||
pub use self::operand::{ImmTy, Immediate, OpTy, Operand, Readable};
|
||||
pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy, Writeable};
|
||||
pub use self::projection::Projectable;
|
||||
pub use self::terminator::FnArg;
|
||||
pub use self::validity::{CtfeValidationMode, RefTracking};
|
||||
|
|
|
|||
|
|
@ -180,20 +180,6 @@ impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self {
|
||||
OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self {
|
||||
OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(val: ImmTy<'tcx, Prov>) -> Self {
|
||||
|
|
@ -201,20 +187,6 @@ impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<&'_ ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(val: &ImmTy<'tcx, Prov>) -> Self {
|
||||
OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<&'_ mut ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(val: &mut ImmTy<'tcx, Prov>) -> Self {
|
||||
OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
||||
#[inline]
|
||||
pub fn from_scalar(val: Scalar<Prov>, layout: TyAndLayout<'tcx>) -> Self {
|
||||
|
|
@ -312,13 +284,13 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx: 'mir, Prov: Provenance> Projectable<'mir, 'tcx, Prov> for ImmTy<'tcx, Prov> {
|
||||
impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||
self.layout
|
||||
}
|
||||
|
||||
fn meta<M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
_ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
|
||||
|
|
@ -337,11 +309,11 @@ impl<'mir, 'tcx: 'mir, Prov: Provenance> Projectable<'mir, 'tcx, Prov> for ImmTy
|
|||
Ok(self.offset_(offset, layout, cx))
|
||||
}
|
||||
|
||||
fn to_op<M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
_ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
||||
Ok(self.into())
|
||||
Ok(self.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -362,15 +334,13 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov>
|
||||
for OpTy<'tcx, Prov>
|
||||
{
|
||||
impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||
self.layout
|
||||
}
|
||||
|
||||
fn meta<M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
_ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
|
||||
|
|
@ -394,7 +364,7 @@ impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov>
|
|||
}
|
||||
}
|
||||
|
||||
fn to_op<M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
_ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
||||
|
|
@ -402,6 +372,31 @@ impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov>
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Readable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
|
||||
fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>>;
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> Readable<'tcx, Prov> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||
self.as_mplace_or_imm()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> Readable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||
Left(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for ImmTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||
Right(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
/// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
|
||||
/// Returns `None` if the layout does not permit loading this as a value.
|
||||
|
|
@ -474,14 +469,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
/// ConstProp needs it, though.
|
||||
pub fn read_immediate_raw(
|
||||
&self,
|
||||
src: &OpTy<'tcx, M::Provenance>,
|
||||
src: &impl Readable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, Either<MPlaceTy<'tcx, M::Provenance>, ImmTy<'tcx, M::Provenance>>> {
|
||||
Ok(match src.as_mplace_or_imm() {
|
||||
Left(ref mplace) => {
|
||||
if let Some(val) = self.read_immediate_from_mplace_raw(mplace)? {
|
||||
Right(val)
|
||||
} else {
|
||||
Left(*mplace)
|
||||
Left(mplace.clone())
|
||||
}
|
||||
}
|
||||
Right(val) => Right(val),
|
||||
|
|
@ -494,14 +489,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
#[inline(always)]
|
||||
pub fn read_immediate(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
op: &impl Readable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
||||
if !matches!(
|
||||
op.layout.abi,
|
||||
op.layout().abi,
|
||||
Abi::Scalar(abi::Scalar::Initialized { .. })
|
||||
| Abi::ScalarPair(abi::Scalar::Initialized { .. }, abi::Scalar::Initialized { .. })
|
||||
) {
|
||||
span_bug!(self.cur_span(), "primitive read not possible for type: {:?}", op.layout.ty);
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
"primitive read not possible for type: {:?}",
|
||||
op.layout().ty
|
||||
);
|
||||
}
|
||||
let imm = self.read_immediate_raw(op)?.right().unwrap();
|
||||
if matches!(*imm, Immediate::Uninit) {
|
||||
|
|
@ -513,7 +512,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
/// Read a scalar from a place
|
||||
pub fn read_scalar(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
op: &impl Readable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
|
||||
Ok(self.read_immediate(op)?.to_scalar())
|
||||
}
|
||||
|
|
@ -524,16 +523,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
/// Read a pointer from a place.
|
||||
pub fn read_pointer(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
op: &impl Readable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
|
||||
self.read_scalar(op)?.to_pointer(self)
|
||||
}
|
||||
/// Read a pointer-sized unsigned integer from a place.
|
||||
pub fn read_target_usize(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx, u64> {
|
||||
pub fn read_target_usize(
|
||||
&self,
|
||||
op: &impl Readable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, u64> {
|
||||
self.read_scalar(op)?.to_target_usize(self)
|
||||
}
|
||||
/// Read a pointer-sized signed integer from a place.
|
||||
pub fn read_target_isize(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx, i64> {
|
||||
pub fn read_target_isize(
|
||||
&self,
|
||||
op: &impl Readable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, i64> {
|
||||
self.read_scalar(op)?.to_target_isize(self)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use rustc_target::abi::{self, Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_V
|
|||
use super::{
|
||||
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
|
||||
ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand,
|
||||
Pointer, Projectable, Provenance, Scalar,
|
||||
Pointer, Projectable, Provenance, Readable, Scalar,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
|
|
@ -81,7 +81,7 @@ pub struct MemPlace<Prov: Provenance = AllocId> {
|
|||
}
|
||||
|
||||
/// A MemPlace with its layout. Constructing it is only possible in this module.
|
||||
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
||||
#[derive(Clone, Hash, Eq, PartialEq, Debug)]
|
||||
pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||
mplace: MemPlace<Prov>,
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
|
|
@ -92,6 +92,14 @@ pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
|
|||
pub align: Align,
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> std::ops::Deref for MPlaceTy<'tcx, Prov> {
|
||||
type Target = MemPlace<Prov>;
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &MemPlace<Prov> {
|
||||
&self.mplace
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Place<Prov: Provenance = AllocId> {
|
||||
/// A place referring to a value allocated in the `Memory` system.
|
||||
|
|
@ -125,14 +133,6 @@ impl<'tcx, Prov: Provenance> std::ops::Deref for PlaceTy<'tcx, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> std::ops::Deref for MPlaceTy<'tcx, Prov> {
|
||||
type Target = MemPlace<Prov>;
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &MemPlace<Prov> {
|
||||
&self.mplace
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
|
||||
|
|
@ -140,20 +140,6 @@ impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov>
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self {
|
||||
PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self {
|
||||
PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> MemPlace<Prov> {
|
||||
#[inline(always)]
|
||||
pub fn from_ptr(ptr: Pointer<Option<Prov>>) -> Self {
|
||||
|
|
@ -229,15 +215,13 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov>
|
||||
for MPlaceTy<'tcx, Prov>
|
||||
{
|
||||
impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||
self.layout
|
||||
}
|
||||
|
||||
fn meta<M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
_ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
|
||||
|
|
@ -258,11 +242,59 @@ impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov>
|
|||
})
|
||||
}
|
||||
|
||||
fn to_op<M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
_ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
||||
Ok(self.into())
|
||||
Ok(self.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||
self.layout
|
||||
}
|
||||
|
||||
fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
|
||||
ecx.place_meta(self)
|
||||
}
|
||||
|
||||
fn offset_with_meta(
|
||||
&self,
|
||||
offset: Size,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
cx: &impl HasDataLayout,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
Ok(match self.as_mplace_or_local() {
|
||||
Left(mplace) => mplace.offset_with_meta(offset, meta, layout, cx)?.into(),
|
||||
Right((frame, local, old_offset)) => {
|
||||
assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway...
|
||||
let new_offset = cx
|
||||
.data_layout()
|
||||
.offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?;
|
||||
PlaceTy {
|
||||
place: Place::Local {
|
||||
frame,
|
||||
local,
|
||||
offset: Some(Size::from_bytes(new_offset)),
|
||||
},
|
||||
align: self.align.restrict_for_offset(offset),
|
||||
layout,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
||||
ecx.place_to_op(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -314,53 +346,51 @@ impl<'tcx, Prov: Provenance + 'static> PlaceTy<'tcx, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov>
|
||||
for PlaceTy<'tcx, Prov>
|
||||
{
|
||||
pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
|
||||
fn as_mplace_or_local(
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)>;
|
||||
|
||||
fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>>;
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||
self.layout
|
||||
fn as_mplace_or_local(
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)>
|
||||
{
|
||||
self.as_mplace_or_local()
|
||||
.map_right(|(frame, local, offset)| (frame, local, offset, self.align, self.layout))
|
||||
}
|
||||
|
||||
fn meta<M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
#[inline(always)]
|
||||
fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
|
||||
ecx.place_meta(self)
|
||||
ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> {
|
||||
ecx.force_allocation(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_local(
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)>
|
||||
{
|
||||
Left(self.clone())
|
||||
}
|
||||
|
||||
fn offset_with_meta(
|
||||
#[inline(always)]
|
||||
fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
cx: &impl HasDataLayout,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
Ok(match self.as_mplace_or_local() {
|
||||
Left(mplace) => mplace.offset_with_meta(offset, meta, layout, cx)?.into(),
|
||||
Right((frame, local, old_offset)) => {
|
||||
assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway...
|
||||
let new_offset = cx
|
||||
.data_layout()
|
||||
.offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?;
|
||||
PlaceTy {
|
||||
place: Place::Local {
|
||||
frame,
|
||||
local,
|
||||
offset: Some(Size::from_bytes(new_offset)),
|
||||
},
|
||||
align: self.align.restrict_for_offset(offset),
|
||||
layout,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn to_op<M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
||||
ecx.place_to_op(self)
|
||||
_ecx: &mut InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -412,7 +442,7 @@ where
|
|||
#[instrument(skip(self), level = "debug")]
|
||||
pub fn deref_operand(
|
||||
&self,
|
||||
src: &OpTy<'tcx, M::Provenance>,
|
||||
src: &impl Readable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||
let val = self.read_immediate(src)?;
|
||||
trace!("deref to {} on {:?}", val.layout.ty, *val);
|
||||
|
|
@ -422,7 +452,7 @@ where
|
|||
}
|
||||
|
||||
let mplace = self.ref_to_mplace(&val)?;
|
||||
self.check_mplace(mplace)?;
|
||||
self.check_mplace(&mplace)?;
|
||||
Ok(mplace)
|
||||
}
|
||||
|
||||
|
|
@ -453,7 +483,7 @@ where
|
|||
}
|
||||
|
||||
/// Check if this mplace is dereferenceable and sufficiently aligned.
|
||||
pub fn check_mplace(&self, mplace: MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
|
||||
pub fn check_mplace(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
|
||||
let (size, _align) = self
|
||||
.size_and_align_of_mplace(&mplace)?
|
||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||
|
|
@ -537,13 +567,13 @@ where
|
|||
pub fn write_immediate(
|
||||
&mut self,
|
||||
src: Immediate<M::Provenance>,
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.write_immediate_no_validate(src, dest)?;
|
||||
|
||||
if M::enforce_validity(self, dest.layout) {
|
||||
if M::enforce_validity(self, dest.layout()) {
|
||||
// Data got changed, better make sure it matches the type!
|
||||
self.validate_operand(&self.place_to_op(dest)?)?;
|
||||
self.validate_operand(&dest.to_op(self)?)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -554,7 +584,7 @@ where
|
|||
pub fn write_scalar(
|
||||
&mut self,
|
||||
val: impl Into<Scalar<M::Provenance>>,
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.write_immediate(Immediate::Scalar(val.into()), dest)
|
||||
}
|
||||
|
|
@ -564,7 +594,7 @@ where
|
|||
pub fn write_pointer(
|
||||
&mut self,
|
||||
ptr: impl Into<Pointer<Option<M::Provenance>>>,
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.write_scalar(Scalar::from_maybe_pointer(ptr.into(), self), dest)
|
||||
}
|
||||
|
|
@ -575,20 +605,19 @@ where
|
|||
fn write_immediate_no_validate(
|
||||
&mut self,
|
||||
src: Immediate<M::Provenance>,
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
assert!(dest.layout.is_sized(), "Cannot write unsized immediate data");
|
||||
trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
|
||||
assert!(dest.layout().is_sized(), "Cannot write unsized immediate data");
|
||||
|
||||
// See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
|
||||
// but not factored as a separate function.
|
||||
let mplace = match dest.place {
|
||||
Place::Local { frame, local, offset } => {
|
||||
let mplace = match dest.as_mplace_or_local() {
|
||||
Right((frame, local, offset, align, layout)) => {
|
||||
if offset.is_some() {
|
||||
// This has been projected to a part of this local. We could have complicated
|
||||
// logic to still keep this local as an `Operand`... but it's much easier to
|
||||
// just fall back to the indirect path.
|
||||
*self.force_allocation(dest)?
|
||||
dest.force_mplace(self)?
|
||||
} else {
|
||||
match M::access_local_mut(self, frame, local)? {
|
||||
Operand::Immediate(local_val) => {
|
||||
|
|
@ -623,16 +652,16 @@ where
|
|||
}
|
||||
Operand::Indirect(mplace) => {
|
||||
// The local is in memory, go on below.
|
||||
*mplace
|
||||
MPlaceTy { mplace: *mplace, align, layout }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Place::Ptr(mplace) => mplace, // already referring to memory
|
||||
Left(mplace) => mplace, // already referring to memory
|
||||
};
|
||||
|
||||
// This is already in memory, write there.
|
||||
self.write_immediate_to_mplace_no_validate(src, dest.layout, dest.align, mplace)
|
||||
self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.align, mplace.mplace)
|
||||
}
|
||||
|
||||
/// Write an immediate to memory.
|
||||
|
|
@ -696,16 +725,19 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
|
||||
pub fn write_uninit(
|
||||
&mut self,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let mplace = match dest.as_mplace_or_local() {
|
||||
Left(mplace) => mplace,
|
||||
Right((frame, local, offset)) => {
|
||||
Right((frame, local, offset, align, layout)) => {
|
||||
if offset.is_some() {
|
||||
// This has been projected to a part of this local. We could have complicated
|
||||
// logic to still keep this local as an `Operand`... but it's much easier to
|
||||
// just fall back to the indirect path.
|
||||
// FIXME: share the logic with `write_immediate_no_validate`.
|
||||
self.force_allocation(dest)?
|
||||
dest.force_mplace(self)?
|
||||
} else {
|
||||
match M::access_local_mut(self, frame, local)? {
|
||||
Operand::Immediate(local) => {
|
||||
|
|
@ -714,7 +746,7 @@ where
|
|||
}
|
||||
Operand::Indirect(mplace) => {
|
||||
// The local is in memory, go on below.
|
||||
MPlaceTy { mplace: *mplace, layout: dest.layout, align: dest.align }
|
||||
MPlaceTy { mplace: *mplace, layout, align }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -734,15 +766,15 @@ where
|
|||
#[instrument(skip(self), level = "debug")]
|
||||
pub fn copy_op(
|
||||
&mut self,
|
||||
src: &OpTy<'tcx, M::Provenance>,
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
src: &impl Readable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
allow_transmute: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.copy_op_no_validate(src, dest, allow_transmute)?;
|
||||
|
||||
if M::enforce_validity(self, dest.layout) {
|
||||
if M::enforce_validity(self, dest.layout()) {
|
||||
// Data got changed, better make sure it matches the type!
|
||||
self.validate_operand(&self.place_to_op(dest)?)?;
|
||||
self.validate_operand(&dest.to_op(self)?)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -755,20 +787,20 @@ where
|
|||
#[instrument(skip(self), level = "debug")]
|
||||
fn copy_op_no_validate(
|
||||
&mut self,
|
||||
src: &OpTy<'tcx, M::Provenance>,
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
src: &impl Readable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
allow_transmute: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
// We do NOT compare the types for equality, because well-typed code can
|
||||
// actually "transmute" `&mut T` to `&T` in an assignment without a cast.
|
||||
let layout_compat =
|
||||
mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout);
|
||||
mir_assign_valid_types(*self.tcx, self.param_env, src.layout(), dest.layout());
|
||||
if !allow_transmute && !layout_compat {
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
"type mismatch when copying!\nsrc: {:?},\ndest: {:?}",
|
||||
src.layout.ty,
|
||||
dest.layout.ty,
|
||||
src.layout().ty,
|
||||
dest.layout().ty,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -781,13 +813,13 @@ where
|
|||
// actually sized, due to a trivially false where-clause
|
||||
// predicate like `where Self: Sized` with `Self = dyn Trait`.
|
||||
// See #102553 for an example of such a predicate.
|
||||
if src.layout.is_unsized() {
|
||||
throw_inval!(SizeOfUnsizedType(src.layout.ty));
|
||||
if src.layout().is_unsized() {
|
||||
throw_inval!(SizeOfUnsizedType(src.layout().ty));
|
||||
}
|
||||
if dest.layout.is_unsized() {
|
||||
throw_inval!(SizeOfUnsizedType(dest.layout.ty));
|
||||
if dest.layout().is_unsized() {
|
||||
throw_inval!(SizeOfUnsizedType(dest.layout().ty));
|
||||
}
|
||||
assert_eq!(src.layout.size, dest.layout.size);
|
||||
assert_eq!(src.layout().size, dest.layout().size);
|
||||
// Yay, we got a value that we can write directly.
|
||||
return if layout_compat {
|
||||
self.write_immediate_no_validate(*src_val, dest)
|
||||
|
|
@ -796,10 +828,10 @@ where
|
|||
// loaded using the offsets defined by `src.layout`. When we put this back into
|
||||
// the destination, we have to use the same offsets! So (a) we make sure we
|
||||
// write back to memory, and (b) we use `dest` *with the source layout*.
|
||||
let dest_mem = self.force_allocation(dest)?;
|
||||
let dest_mem = dest.force_mplace(self)?;
|
||||
self.write_immediate_to_mplace_no_validate(
|
||||
*src_val,
|
||||
src.layout,
|
||||
src.layout(),
|
||||
dest_mem.align,
|
||||
*dest_mem,
|
||||
)
|
||||
|
|
@ -808,9 +840,9 @@ where
|
|||
Left(mplace) => mplace,
|
||||
};
|
||||
// Slow path, this does not fit into an immediate. Just memcpy.
|
||||
trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
|
||||
trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout().ty);
|
||||
|
||||
let dest = self.force_allocation(&dest)?;
|
||||
let dest = dest.force_mplace(self)?;
|
||||
let Some((dest_size, _)) = self.size_and_align_of_mplace(&dest)? else {
|
||||
span_bug!(self.cur_span(), "copy_op needs (dynamically) sized values")
|
||||
};
|
||||
|
|
@ -928,7 +960,7 @@ where
|
|||
operands: &IndexSlice<FieldIdx, mir::Operand<'tcx>>,
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.write_uninit(&dest)?;
|
||||
self.write_uninit(dest)?;
|
||||
let (variant_index, variant_dest, active_field_index) = match *kind {
|
||||
mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
|
||||
let variant_dest = self.project_downcast(dest, variant_index)?;
|
||||
|
|
@ -945,7 +977,7 @@ where
|
|||
let op = self.eval_operand(operand, Some(field_dest.layout))?;
|
||||
self.copy_op(&op, &field_dest, /*allow_transmute*/ false)?;
|
||||
}
|
||||
self.write_discriminant(variant_index, &dest)
|
||||
self.write_discriminant(variant_index, dest)
|
||||
}
|
||||
|
||||
pub fn raw_const_to_mplace(
|
||||
|
|
@ -983,7 +1015,7 @@ where
|
|||
|
||||
/// Turn a `dyn* Trait` type into an value with the actual dynamic type.
|
||||
/// Also returns the vtable.
|
||||
pub(super) fn unpack_dyn_star<P: Projectable<'mir, 'tcx, M::Provenance>>(
|
||||
pub(super) fn unpack_dyn_star<P: Projectable<'tcx, M::Provenance>>(
|
||||
&self,
|
||||
val: &P,
|
||||
) -> InterpResult<'tcx, (P, Pointer<Option<M::Provenance>>)> {
|
||||
|
|
|
|||
|
|
@ -16,21 +16,20 @@ use rustc_target::abi::HasDataLayout;
|
|||
use rustc_target::abi::Size;
|
||||
use rustc_target::abi::{self, VariantIdx};
|
||||
|
||||
use super::MPlaceTy;
|
||||
use super::{InterpCx, InterpResult, Machine, MemPlaceMeta, OpTy, Provenance, Scalar};
|
||||
use super::{InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar};
|
||||
|
||||
/// A thing that we can project into, and that has a layout.
|
||||
pub trait Projectable<'mir, 'tcx: 'mir, Prov: Provenance>: Sized {
|
||||
pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
||||
/// Get the layout.
|
||||
fn layout(&self) -> TyAndLayout<'tcx>;
|
||||
|
||||
/// Get the metadata of a wide value.
|
||||
fn meta<M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>>;
|
||||
|
||||
fn len<M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
fn len<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, u64> {
|
||||
|
|
@ -67,7 +66,7 @@ pub trait Projectable<'mir, 'tcx: 'mir, Prov: Provenance>: Sized {
|
|||
|
||||
/// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for
|
||||
/// reading from this thing.
|
||||
fn to_op<M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>;
|
||||
|
|
@ -85,7 +84,7 @@ where
|
|||
///
|
||||
/// This also works for arrays, but then the `usize` index type is restricting.
|
||||
/// For indexing into arrays, use `mplace_index`.
|
||||
pub fn project_field<P: Projectable<'mir, 'tcx, M::Provenance>>(
|
||||
pub fn project_field<P: Projectable<'tcx, M::Provenance>>(
|
||||
&self,
|
||||
base: &P,
|
||||
field: usize,
|
||||
|
|
@ -128,7 +127,7 @@ where
|
|||
}
|
||||
|
||||
/// Downcasting to an enum variant.
|
||||
pub fn project_downcast<P: Projectable<'mir, 'tcx, M::Provenance>>(
|
||||
pub fn project_downcast<P: Projectable<'tcx, M::Provenance>>(
|
||||
&self,
|
||||
base: &P,
|
||||
variant: VariantIdx,
|
||||
|
|
@ -149,7 +148,7 @@ where
|
|||
}
|
||||
|
||||
/// Compute the offset and field layout for accessing the given index.
|
||||
pub fn project_index<P: Projectable<'mir, 'tcx, M::Provenance>>(
|
||||
pub fn project_index<P: Projectable<'tcx, M::Provenance>>(
|
||||
&self,
|
||||
base: &P,
|
||||
index: u64,
|
||||
|
|
@ -178,7 +177,7 @@ where
|
|||
base.offset(offset, field_layout, self)
|
||||
}
|
||||
|
||||
fn project_constant_index<P: Projectable<'mir, 'tcx, M::Provenance>>(
|
||||
fn project_constant_index<P: Projectable<'tcx, M::Provenance>>(
|
||||
&self,
|
||||
base: &P,
|
||||
offset: u64,
|
||||
|
|
@ -204,7 +203,7 @@ where
|
|||
|
||||
/// Iterates over all fields of an array. Much more efficient than doing the
|
||||
/// same by repeatedly calling `operand_index`.
|
||||
pub fn project_array_fields<'a, P: Projectable<'mir, 'tcx, M::Provenance>>(
|
||||
pub fn project_array_fields<'a, P: Projectable<'tcx, M::Provenance>>(
|
||||
&self,
|
||||
base: &'a P,
|
||||
) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, P>> + 'a>
|
||||
|
|
@ -224,7 +223,7 @@ where
|
|||
}
|
||||
|
||||
/// Subslicing
|
||||
fn project_subslice<P: Projectable<'mir, 'tcx, M::Provenance>>(
|
||||
fn project_subslice<P: Projectable<'tcx, M::Provenance>>(
|
||||
&self,
|
||||
base: &P,
|
||||
from: u64,
|
||||
|
|
@ -284,9 +283,7 @@ where
|
|||
#[instrument(skip(self), level = "trace")]
|
||||
pub fn project<P>(&self, base: &P, proj_elem: mir::PlaceElem<'tcx>) -> InterpResult<'tcx, P>
|
||||
where
|
||||
P: Projectable<'mir, 'tcx, M::Provenance>
|
||||
+ From<MPlaceTy<'tcx, M::Provenance>>
|
||||
+ std::fmt::Debug,
|
||||
P: Projectable<'tcx, M::Provenance> + From<MPlaceTy<'tcx, M::Provenance>> + std::fmt::Debug,
|
||||
{
|
||||
use rustc_middle::mir::ProjectionElem::*;
|
||||
Ok(match proj_elem {
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
} else {
|
||||
// Write the src to the first element.
|
||||
let first = self.project_index(&dest, 0)?;
|
||||
self.copy_op(&src, &first.into(), /*allow_transmute*/ false)?;
|
||||
self.copy_op(&src, &first, /*allow_transmute*/ false)?;
|
||||
|
||||
// This is performance-sensitive code for big static/const arrays! So we
|
||||
// avoid writing each operand individually and instead just make many copies
|
||||
|
|
|
|||
|
|
@ -634,7 +634,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// Ensure the return place is aligned and dereferenceable, and protect it for
|
||||
// in-place return value passing.
|
||||
if let Either::Left(mplace) = destination.as_mplace_or_local() {
|
||||
self.check_mplace(mplace)?;
|
||||
self.check_mplace(&mplace)?;
|
||||
} else {
|
||||
// Nothing to do for locals, they are always properly allocated and aligned.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,19 +136,19 @@ pub struct RefTracking<T, PATH = ()> {
|
|||
pub todo: Vec<(T, PATH)>,
|
||||
}
|
||||
|
||||
impl<T: Copy + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH> {
|
||||
impl<T: Clone + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH> {
|
||||
pub fn empty() -> Self {
|
||||
RefTracking { seen: FxHashSet::default(), todo: vec![] }
|
||||
}
|
||||
pub fn new(op: T) -> Self {
|
||||
let mut ref_tracking_for_consts =
|
||||
RefTracking { seen: FxHashSet::default(), todo: vec![(op, PATH::default())] };
|
||||
RefTracking { seen: FxHashSet::default(), todo: vec![(op.clone(), PATH::default())] };
|
||||
ref_tracking_for_consts.seen.insert(op);
|
||||
ref_tracking_for_consts
|
||||
}
|
||||
|
||||
pub fn track(&mut self, op: T, path: impl FnOnce() -> PATH) {
|
||||
if self.seen.insert(op) {
|
||||
if self.seen.insert(op.clone()) {
|
||||
trace!("Recursing below ptr {:#?}", op);
|
||||
let path = path();
|
||||
// Remember to come back to this later.
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ use super::{InterpCx, MPlaceTy, Machine, Projectable};
|
|||
|
||||
/// How to traverse a value and what to do when we are at the leaves.
|
||||
pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
|
||||
type V: Projectable<'mir, 'tcx, M::Provenance>
|
||||
+ From<MPlaceTy<'tcx, M::Provenance>>
|
||||
+ std::fmt::Debug;
|
||||
type V: Projectable<'tcx, M::Provenance> + From<MPlaceTy<'tcx, M::Provenance>>;
|
||||
|
||||
/// The visitor must have an `InterpCx` in it.
|
||||
fn ecx(&self) -> &InterpCx<'mir, 'tcx, M>;
|
||||
|
|
|
|||
|
|
@ -1415,7 +1415,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info:
|
|||
false,
|
||||
TerminalUrl::No,
|
||||
));
|
||||
let handler = rustc_errors::Handler::with_emitter(true, None, emitter, None);
|
||||
let handler = rustc_errors::Handler::with_emitter(emitter);
|
||||
|
||||
// a .span_bug or .bug call has already printed what
|
||||
// it wants to print.
|
||||
|
|
|
|||
|
|
@ -536,7 +536,9 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
|
|||
}
|
||||
};
|
||||
|
||||
if handler.flags.dont_buffer_diagnostics || handler.flags.treat_err_as_bug.is_some() {
|
||||
if handler.inner.lock().flags.dont_buffer_diagnostics
|
||||
|| handler.inner.lock().flags.treat_err_as_bug.is_some()
|
||||
{
|
||||
self.emit();
|
||||
return None;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
|
|||
);
|
||||
|
||||
let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1));
|
||||
let handler = Handler::with_emitter(true, None, Box::new(je), None);
|
||||
let handler = Handler::with_emitter(Box::new(je));
|
||||
handler.span_err(span, "foo");
|
||||
|
||||
let bytes = output.lock().unwrap();
|
||||
|
|
|
|||
|
|
@ -391,7 +391,6 @@ use std::backtrace::{Backtrace, BacktraceStatus};
|
|||
/// Certain errors (fatal, bug, unimpl) may cause immediate exit,
|
||||
/// others log errors for later reporting.
|
||||
pub struct Handler {
|
||||
flags: HandlerFlags,
|
||||
inner: Lock<HandlerInner>,
|
||||
}
|
||||
|
||||
|
|
@ -549,69 +548,47 @@ impl Drop for HandlerInner {
|
|||
|
||||
impl Handler {
|
||||
pub fn with_tty_emitter(
|
||||
color_config: ColorConfig,
|
||||
can_emit_warnings: bool,
|
||||
treat_err_as_bug: Option<NonZeroUsize>,
|
||||
sm: Option<Lrc<SourceMap>>,
|
||||
fluent_bundle: Option<Lrc<FluentBundle>>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
ice_file: Option<PathBuf>,
|
||||
) -> Self {
|
||||
Self::with_tty_emitter_and_flags(
|
||||
color_config,
|
||||
sm,
|
||||
fluent_bundle,
|
||||
fallback_bundle,
|
||||
HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() },
|
||||
ice_file,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn with_tty_emitter_and_flags(
|
||||
color_config: ColorConfig,
|
||||
sm: Option<Lrc<SourceMap>>,
|
||||
fluent_bundle: Option<Lrc<FluentBundle>>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
flags: HandlerFlags,
|
||||
ice_file: Option<PathBuf>,
|
||||
) -> Self {
|
||||
let emitter = Box::new(EmitterWriter::stderr(
|
||||
color_config,
|
||||
ColorConfig::Auto,
|
||||
sm,
|
||||
fluent_bundle,
|
||||
None,
|
||||
fallback_bundle,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
flags.macro_backtrace,
|
||||
flags.track_diagnostics,
|
||||
false,
|
||||
false,
|
||||
TerminalUrl::No,
|
||||
));
|
||||
Self::with_emitter_and_flags(emitter, flags, ice_file)
|
||||
Self::with_emitter(emitter)
|
||||
}
|
||||
pub fn disable_warnings(mut self) -> Self {
|
||||
self.inner.get_mut().flags.can_emit_warnings = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_emitter(
|
||||
can_emit_warnings: bool,
|
||||
treat_err_as_bug: Option<NonZeroUsize>,
|
||||
emitter: Box<dyn Emitter + sync::Send>,
|
||||
ice_file: Option<PathBuf>,
|
||||
) -> Self {
|
||||
Handler::with_emitter_and_flags(
|
||||
emitter,
|
||||
HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() },
|
||||
ice_file,
|
||||
)
|
||||
pub fn treat_err_as_bug(mut self, treat_err_as_bug: NonZeroUsize) -> Self {
|
||||
self.inner.get_mut().flags.treat_err_as_bug = Some(treat_err_as_bug);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_emitter_and_flags(
|
||||
emitter: Box<dyn Emitter + sync::Send>,
|
||||
flags: HandlerFlags,
|
||||
ice_file: Option<PathBuf>,
|
||||
) -> Self {
|
||||
pub fn with_flags(mut self, flags: HandlerFlags) -> Self {
|
||||
self.inner.get_mut().flags = flags;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_ice_file(mut self, ice_file: PathBuf) -> Self {
|
||||
self.inner.get_mut().ice_file = Some(ice_file);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_emitter(emitter: Box<dyn Emitter + sync::Send>) -> Self {
|
||||
Self {
|
||||
flags,
|
||||
inner: Lock::new(HandlerInner {
|
||||
flags,
|
||||
flags: HandlerFlags { can_emit_warnings: true, ..Default::default() },
|
||||
lint_err_count: 0,
|
||||
err_count: 0,
|
||||
warn_count: 0,
|
||||
|
|
@ -629,7 +606,7 @@ impl Handler {
|
|||
check_unstable_expect_diagnostics: false,
|
||||
unstable_expect_diagnostics: Vec::new(),
|
||||
fulfilled_expectations: Default::default(),
|
||||
ice_file,
|
||||
ice_file: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
@ -657,7 +634,7 @@ impl Handler {
|
|||
// This is here to not allow mutation of flags;
|
||||
// as of this writing it's only used in tests in librustc_middle.
|
||||
pub fn can_emit_warnings(&self) -> bool {
|
||||
self.flags.can_emit_warnings
|
||||
self.inner.lock().flags.can_emit_warnings
|
||||
}
|
||||
|
||||
/// Resets the diagnostic error count as well as the cached emitted diagnostics.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use crate::tests::{matches_codepattern, string_to_stream, with_error_checking_parse};
|
||||
use crate::tests::{
|
||||
matches_codepattern, string_to_stream, with_error_checking_parse, with_expected_parse_error,
|
||||
};
|
||||
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{self, Delimiter, Token};
|
||||
|
|
@ -51,11 +53,15 @@ fn string_to_item(source_str: String) -> Option<P<ast::Item>> {
|
|||
with_error_checking_parse(source_str, &sess(), |p| p.parse_item(ForceCollect::No))
|
||||
}
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn bad_path_expr_1() {
|
||||
// This should trigger error: expected identifier, found keyword `return`
|
||||
create_default_session_globals_then(|| {
|
||||
string_to_expr("::abc::def::return".to_string());
|
||||
with_expected_parse_error(
|
||||
"::abc::def::return",
|
||||
"expected identifier, found keyword `return`",
|
||||
|p| p.parse_expr(),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,33 @@ fn string_to_parser(ps: &ParseSess, source_str: String) -> Parser<'_> {
|
|||
new_parser_from_source_str(ps, PathBuf::from("bogofile").into(), source_str)
|
||||
}
|
||||
|
||||
fn create_test_handler() -> (Handler, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
|
||||
let output = Arc::new(Mutex::new(Vec::new()));
|
||||
let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let fallback_bundle = rustc_errors::fallback_fluent_bundle(
|
||||
vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
|
||||
false,
|
||||
);
|
||||
let emitter = EmitterWriter::new(
|
||||
Box::new(Shared { data: output.clone() }),
|
||||
Some(source_map.clone()),
|
||||
None,
|
||||
fallback_bundle,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
Some(140),
|
||||
false,
|
||||
false,
|
||||
TerminalUrl::No,
|
||||
);
|
||||
let handler = Handler::with_emitter(Box::new(emitter));
|
||||
(handler, source_map, output)
|
||||
}
|
||||
|
||||
/// Returns the result of parsing the given string via the given callback.
|
||||
///
|
||||
/// If there are any errors, this will panic.
|
||||
pub(crate) fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
|
||||
|
|
@ -32,6 +59,26 @@ where
|
|||
x
|
||||
}
|
||||
|
||||
/// Verifies that parsing the given string using the given callback will
|
||||
/// generate an error that contains the given text.
|
||||
pub(crate) fn with_expected_parse_error<T, F>(source_str: &str, expected_output: &str, f: F)
|
||||
where
|
||||
F: for<'a> FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
|
||||
{
|
||||
let (handler, source_map, output) = create_test_handler();
|
||||
let ps = ParseSess::with_span_handler(handler, source_map);
|
||||
let mut p = string_to_parser(&ps, source_str.to_string());
|
||||
let result = f(&mut p);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let bytes = output.lock().unwrap();
|
||||
let actual_output = str::from_utf8(&bytes).unwrap();
|
||||
println!("expected output:\n------\n{}------", expected_output);
|
||||
println!("actual output:\n------\n{}------", actual_output);
|
||||
|
||||
assert!(actual_output.contains(expected_output))
|
||||
}
|
||||
|
||||
/// Maps a string to tts, using a made-up filename.
|
||||
pub(crate) fn string_to_stream(source_str: String) -> TokenStream {
|
||||
let ps = ParseSess::new(
|
||||
|
|
@ -130,13 +177,7 @@ impl<T: Write> Write for Shared<T> {
|
|||
|
||||
fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &str) {
|
||||
create_default_session_if_not_set_then(|_| {
|
||||
let output = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let fallback_bundle = rustc_errors::fallback_fluent_bundle(
|
||||
vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
|
||||
false,
|
||||
);
|
||||
let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let (handler, source_map, output) = create_test_handler();
|
||||
source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned());
|
||||
|
||||
let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end);
|
||||
|
|
@ -148,20 +189,6 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
|
|||
println!("text: {:?}", source_map.span_to_snippet(span));
|
||||
}
|
||||
|
||||
let emitter = EmitterWriter::new(
|
||||
Box::new(Shared { data: output.clone() }),
|
||||
Some(source_map.clone()),
|
||||
None,
|
||||
fallback_bundle,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
TerminalUrl::No,
|
||||
);
|
||||
let handler = Handler::with_emitter(true, None, Box::new(emitter), None);
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
handler.span_err(msp, "foo");
|
||||
|
||||
|
|
|
|||
|
|
@ -267,8 +267,6 @@ lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead
|
|||
lint_improper_ctypes_char_reason = the `char` type has no C equivalent
|
||||
lint_improper_ctypes_dyn = trait objects have no C equivalent
|
||||
|
||||
lint_improper_ctypes_enum_phantomdata = this enum contains a PhantomData field
|
||||
|
||||
lint_improper_ctypes_enum_repr_help =
|
||||
consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
|
||||
|
||||
|
|
|
|||
|
|
@ -985,39 +985,43 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
) -> FfiResult<'tcx> {
|
||||
use FfiResult::*;
|
||||
|
||||
let transparent_safety = def.repr().transparent().then(|| {
|
||||
// Can assume that at most one field is not a ZST, so only check
|
||||
// that field's type for FFI-safety.
|
||||
let transparent_with_all_zst_fields = if def.repr().transparent() {
|
||||
if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
|
||||
return self.check_field_type_for_ffi(cache, field, args);
|
||||
// Transparent newtypes have at most one non-ZST field which needs to be checked..
|
||||
match self.check_field_type_for_ffi(cache, field, args) {
|
||||
FfiUnsafe { ty, .. } if ty.is_unit() => (),
|
||||
r => return r,
|
||||
}
|
||||
|
||||
false
|
||||
} else {
|
||||
// All fields are ZSTs; this means that the type should behave
|
||||
// like (), which is FFI-unsafe... except if all fields are PhantomData,
|
||||
// which is tested for below
|
||||
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
|
||||
// ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
|
||||
// `PhantomData`).
|
||||
true
|
||||
}
|
||||
});
|
||||
// We can't completely trust repr(C) markings; make sure the fields are
|
||||
// actually safe.
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
|
||||
let mut all_phantom = !variant.fields.is_empty();
|
||||
for field in &variant.fields {
|
||||
match self.check_field_type_for_ffi(cache, &field, args) {
|
||||
FfiSafe => {
|
||||
all_phantom = false;
|
||||
}
|
||||
FfiPhantom(..) if !def.repr().transparent() && def.is_enum() => {
|
||||
return FfiUnsafe {
|
||||
ty,
|
||||
reason: fluent::lint_improper_ctypes_enum_phantomdata,
|
||||
help: None,
|
||||
};
|
||||
}
|
||||
FfiPhantom(..) => {}
|
||||
r => return transparent_safety.unwrap_or(r),
|
||||
all_phantom &= match self.check_field_type_for_ffi(cache, &field, args) {
|
||||
FfiSafe => false,
|
||||
// `()` fields are FFI-safe!
|
||||
FfiUnsafe { ty, .. } if ty.is_unit() => false,
|
||||
FfiPhantom(..) => true,
|
||||
r @ FfiUnsafe { .. } => return r,
|
||||
}
|
||||
}
|
||||
|
||||
if all_phantom { FfiPhantom(ty) } else { transparent_safety.unwrap_or(FfiSafe) }
|
||||
if all_phantom {
|
||||
FfiPhantom(ty)
|
||||
} else if transparent_with_all_zst_fields {
|
||||
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
|
||||
} else {
|
||||
FfiSafe
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
|
||||
|
|
@ -1220,25 +1224,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
let sig = tcx.erase_late_bound_regions(sig);
|
||||
if !sig.output().is_unit() {
|
||||
let r = self.check_type_for_ffi(cache, sig.output());
|
||||
match r {
|
||||
FfiSafe => {}
|
||||
_ => {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
for arg in sig.inputs() {
|
||||
let r = self.check_type_for_ffi(cache, *arg);
|
||||
match r {
|
||||
match self.check_type_for_ffi(cache, *arg) {
|
||||
FfiSafe => {}
|
||||
_ => {
|
||||
return r;
|
||||
}
|
||||
r => return r,
|
||||
}
|
||||
}
|
||||
FfiSafe
|
||||
|
||||
let ret_ty = sig.output();
|
||||
if ret_ty.is_unit() {
|
||||
return FfiSafe;
|
||||
}
|
||||
|
||||
self.check_type_for_ffi(cache, ret_ty)
|
||||
}
|
||||
|
||||
ty::Foreign(..) => FfiSafe,
|
||||
|
|
@ -1354,7 +1352,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
// Don't report FFI errors for unit return types. This check exists here, and not in
|
||||
// `check_foreign_fn` (where it would make more sense) so that normalization has definitely
|
||||
// the caller (where it would make more sense) so that normalization has definitely
|
||||
// happened.
|
||||
if is_return_type && ty.is_unit() {
|
||||
return;
|
||||
|
|
@ -1370,9 +1368,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
None,
|
||||
);
|
||||
}
|
||||
// If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic
|
||||
// argument, which after substitution, is `()`, then this branch can be hit.
|
||||
FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => {}
|
||||
FfiResult::FfiUnsafe { ty, reason, help } => {
|
||||
self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ gsgdt = "0.1.2"
|
|||
field-offset = "0.3.5"
|
||||
measureme = "10.0.0"
|
||||
polonius-engine = "0.13.0"
|
||||
rustc_apfloat = { path = "../rustc_apfloat" }
|
||||
rustc_apfloat = "0.2.0"
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ rustc_arena = { path = "../rustc_arena" }
|
|||
tracing = "0.1"
|
||||
either = "1"
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_apfloat = { path = "../rustc_apfloat" }
|
||||
rustc_apfloat = "0.2.0"
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
|
|
|
|||
|
|
@ -494,7 +494,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
|||
trace!("assertion on {:?} should be {:?}", value, expected);
|
||||
|
||||
let expected = Scalar::from_bool(expected);
|
||||
let value_const = self.use_ecx(location, |this| this.ecx.read_scalar(&value))?;
|
||||
let value_const = self.use_ecx(location, |this| this.ecx.read_scalar(value))?;
|
||||
|
||||
if expected != value_const {
|
||||
// Poison all places this operand references so that further code
|
||||
|
|
@ -664,7 +664,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
|||
}
|
||||
TerminatorKind::SwitchInt { ref discr, ref targets } => {
|
||||
if let Some(ref value) = self.eval_operand(&discr, location)
|
||||
&& let Some(value_const) = self.use_ecx(location, |this| this.ecx.read_scalar(&value))
|
||||
&& let Some(value_const) = self.use_ecx(location, |this| this.ecx.read_scalar(value))
|
||||
&& let Ok(constant) = value_const.try_to_int()
|
||||
&& let Ok(constant) = constant.to_bits(constant.size())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1433,8 +1433,6 @@ options! {
|
|||
dep_tasks: bool = (false, parse_bool, [UNTRACKED],
|
||||
"print tasks that execute and the color their dep node gets (requires debug build) \
|
||||
(default: no)"),
|
||||
diagnostic_width: Option<usize> = (None, parse_opt_number, [UNTRACKED],
|
||||
"set the current output width for diagnostic truncation"),
|
||||
dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED],
|
||||
"emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \
|
||||
(default: no)"),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::lint::{
|
|||
use rustc_ast::node_id::NodeId;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
|
||||
use rustc_data_structures::sync::{AppendOnlyVec, AtomicBool, Lock, Lrc};
|
||||
use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
|
||||
use rustc_errors::{emitter::SilentEmitter, Handler};
|
||||
use rustc_errors::{
|
||||
fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
|
||||
EmissionGuarantee, ErrorGuaranteed, IntoDiagnostic, MultiSpan, Noted, StashKey,
|
||||
|
|
@ -224,15 +224,7 @@ impl ParseSess {
|
|||
pub fn new(locale_resources: Vec<&'static str>, file_path_mapping: FilePathMapping) -> Self {
|
||||
let fallback_bundle = fallback_fluent_bundle(locale_resources, false);
|
||||
let sm = Lrc::new(SourceMap::new(file_path_mapping));
|
||||
let handler = Handler::with_tty_emitter(
|
||||
ColorConfig::Auto,
|
||||
true,
|
||||
None,
|
||||
Some(sm.clone()),
|
||||
None,
|
||||
fallback_bundle,
|
||||
None,
|
||||
);
|
||||
let handler = Handler::with_tty_emitter(Some(sm.clone()), fallback_bundle);
|
||||
ParseSess::with_span_handler(handler, sm)
|
||||
}
|
||||
|
||||
|
|
@ -262,21 +254,9 @@ impl ParseSess {
|
|||
pub fn with_silent_emitter(fatal_note: Option<String>) -> Self {
|
||||
let fallback_bundle = fallback_fluent_bundle(Vec::new(), false);
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let fatal_handler = Handler::with_tty_emitter(
|
||||
ColorConfig::Auto,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
fallback_bundle,
|
||||
None,
|
||||
);
|
||||
let handler = Handler::with_emitter(
|
||||
false,
|
||||
None,
|
||||
Box::new(SilentEmitter { fatal_handler, fatal_note }),
|
||||
None,
|
||||
);
|
||||
let fatal_handler = Handler::with_tty_emitter(None, fallback_bundle).disable_warnings();
|
||||
let handler = Handler::with_emitter(Box::new(SilentEmitter { fatal_handler, fatal_note }))
|
||||
.disable_warnings();
|
||||
ParseSess::with_span_handler(handler, sm)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1442,11 +1442,11 @@ pub fn build_session(
|
|||
);
|
||||
let emitter = default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle);
|
||||
|
||||
let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags(
|
||||
emitter,
|
||||
sopts.unstable_opts.diagnostic_handler_flags(can_emit_warnings),
|
||||
ice_file,
|
||||
);
|
||||
let mut span_diagnostic = rustc_errors::Handler::with_emitter(emitter)
|
||||
.with_flags(sopts.unstable_opts.diagnostic_handler_flags(can_emit_warnings));
|
||||
if let Some(ice_file) = ice_file {
|
||||
span_diagnostic = span_diagnostic.with_ice_file(ice_file);
|
||||
}
|
||||
|
||||
let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.unstable_opts.self_profile
|
||||
{
|
||||
|
|
@ -1737,7 +1737,7 @@ pub struct EarlyErrorHandler {
|
|||
impl EarlyErrorHandler {
|
||||
pub fn new(output: ErrorOutputType) -> Self {
|
||||
let emitter = mk_emitter(output);
|
||||
Self { handler: rustc_errors::Handler::with_emitter(true, None, emitter, None) }
|
||||
Self { handler: rustc_errors::Handler::with_emitter(emitter) }
|
||||
}
|
||||
|
||||
pub fn abort_if_errors(&self) {
|
||||
|
|
@ -1751,7 +1751,7 @@ impl EarlyErrorHandler {
|
|||
self.handler.abort_if_errors();
|
||||
|
||||
let emitter = mk_emitter(output);
|
||||
self.handler = Handler::with_emitter(true, None, emitter, None);
|
||||
self.handler = Handler::with_emitter(emitter);
|
||||
}
|
||||
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
|
|
|
|||
|
|
@ -825,8 +825,10 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> {
|
|||
ty::Alias(alias_kind, alias_ty) => {
|
||||
TyKind::Alias(alias_kind.stable(tables), alias_ty.stable(tables))
|
||||
}
|
||||
ty::Param(_) => todo!(),
|
||||
ty::Bound(_, _) => todo!(),
|
||||
ty::Param(param_ty) => TyKind::Param(param_ty.stable(tables)),
|
||||
ty::Bound(debruijn_idx, bound_ty) => {
|
||||
TyKind::Bound(debruijn_idx.as_usize(), bound_ty.stable(tables))
|
||||
}
|
||||
ty::Placeholder(..)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::GeneratorWitnessMIR(_, _)
|
||||
|
|
@ -837,3 +839,19 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_middle::ty::ParamTy {
|
||||
type T = stable_mir::ty::ParamTy;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
use stable_mir::ty::ParamTy;
|
||||
ParamTy { index: self.index, name: self.name.to_string() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_middle::ty::BoundTy {
|
||||
type T = stable_mir::ty::BoundTy;
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
use stable_mir::ty::BoundTy;
|
||||
BoundTy { var: self.var.as_usize(), kind: self.kind.stable(tables) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ type Span = Opaque;
|
|||
pub enum TyKind {
|
||||
RigidTy(RigidTy),
|
||||
Alias(AliasKind, AliasTy),
|
||||
Param(ParamTy),
|
||||
Bound(usize, BoundTy),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -228,3 +230,15 @@ pub struct ExistentialProjection {
|
|||
pub generic_args: GenericArgs,
|
||||
pub term: TermKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ParamTy {
|
||||
pub index: u32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BoundTy {
|
||||
pub var: usize,
|
||||
pub kind: BoundTyKind,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -372,6 +372,7 @@ symbols! {
|
|||
arm_target_feature,
|
||||
array,
|
||||
arrays,
|
||||
as_mut_ptr,
|
||||
as_ptr,
|
||||
as_ref,
|
||||
as_str,
|
||||
|
|
@ -858,6 +859,7 @@ symbols! {
|
|||
item,
|
||||
item_like_imports,
|
||||
iter,
|
||||
iter_mut,
|
||||
iter_repeat,
|
||||
iterator_collect_fn,
|
||||
kcfi,
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
///
|
||||
/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
|
||||
/// see design document in the tracking issue #89653.
|
||||
use core::fmt::Display;
|
||||
use rustc_data_structures::base_n;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{
|
||||
self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind,
|
||||
TermKind, Ty, TyCtxt, UintTy,
|
||||
|
|
@ -19,6 +19,7 @@ use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef};
|
|||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::sym;
|
||||
use rustc_target::abi::call::{Conv, FnAbi};
|
||||
use rustc_target::abi::Integer;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use std::fmt::Write as _;
|
||||
|
||||
|
|
@ -93,44 +94,54 @@ fn encode_const<'tcx>(
|
|||
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
|
||||
options: EncodeTyOptions,
|
||||
) -> String {
|
||||
// L<element-type>[n]<element-value>E as literal argument
|
||||
// L<element-type>[n][<element-value>]E as literal argument
|
||||
let mut s = String::from('L');
|
||||
|
||||
// Element type
|
||||
s.push_str(&encode_ty(tcx, c.ty(), dict, options));
|
||||
match c.kind() {
|
||||
// Const parameters
|
||||
ty::ConstKind::Param(..) => {
|
||||
// L<element-type>E as literal argument
|
||||
|
||||
// The only allowed types of const parameters are bool, u8, u16, u32, u64, u128, usize i8, i16,
|
||||
// i32, i64, i128, isize, and char. The bool value false is encoded as 0 and true as 1.
|
||||
fn push_signed_value<T: Display + PartialOrd>(s: &mut String, value: T, zero: T) {
|
||||
if value < zero {
|
||||
s.push('n')
|
||||
};
|
||||
let _ = write!(s, "{value}");
|
||||
}
|
||||
// Element type
|
||||
s.push_str(&encode_ty(tcx, c.ty(), dict, options));
|
||||
}
|
||||
|
||||
fn push_unsigned_value<T: Display>(s: &mut String, value: T) {
|
||||
let _ = write!(s, "{value}");
|
||||
}
|
||||
// Literal arguments
|
||||
ty::ConstKind::Value(..) => {
|
||||
// L<element-type>[n]<element-value>E as literal argument
|
||||
|
||||
if let Some(scalar_int) = c.try_to_scalar_int() {
|
||||
let signed = c.ty().is_signed();
|
||||
match scalar_int.size().bits() {
|
||||
8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0),
|
||||
16 if signed => push_signed_value(&mut s, scalar_int.try_to_i16().unwrap(), 0),
|
||||
32 if signed => push_signed_value(&mut s, scalar_int.try_to_i32().unwrap(), 0),
|
||||
64 if signed => push_signed_value(&mut s, scalar_int.try_to_i64().unwrap(), 0),
|
||||
128 if signed => push_signed_value(&mut s, scalar_int.try_to_i128().unwrap(), 0),
|
||||
8 => push_unsigned_value(&mut s, scalar_int.try_to_u8().unwrap()),
|
||||
16 => push_unsigned_value(&mut s, scalar_int.try_to_u16().unwrap()),
|
||||
32 => push_unsigned_value(&mut s, scalar_int.try_to_u32().unwrap()),
|
||||
64 => push_unsigned_value(&mut s, scalar_int.try_to_u64().unwrap()),
|
||||
128 => push_unsigned_value(&mut s, scalar_int.try_to_u128().unwrap()),
|
||||
_ => {
|
||||
bug!("encode_const: unexpected size `{:?}`", scalar_int.size().bits());
|
||||
// Element type
|
||||
s.push_str(&encode_ty(tcx, c.ty(), dict, options));
|
||||
|
||||
// The only allowed types of const values are bool, u8, u16, u32,
|
||||
// u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The
|
||||
// bool value false is encoded as 0 and true as 1.
|
||||
match c.ty().kind() {
|
||||
ty::Int(ity) => {
|
||||
let bits = c.eval_bits(tcx, ty::ParamEnv::reveal_all(), c.ty());
|
||||
let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
|
||||
if val < 0 {
|
||||
s.push('n');
|
||||
}
|
||||
let _ = write!(s, "{val}");
|
||||
}
|
||||
ty::Uint(_) => {
|
||||
let val = c.eval_bits(tcx, ty::ParamEnv::reveal_all(), c.ty());
|
||||
let _ = write!(s, "{val}");
|
||||
}
|
||||
ty::Bool => {
|
||||
let val = c.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap();
|
||||
let _ = write!(s, "{val}");
|
||||
}
|
||||
_ => {
|
||||
bug!("encode_const: unexpected type `{:?}`", c.ty());
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
bug!("encode_const: unexpected type `{:?}`", c.ty());
|
||||
}
|
||||
|
||||
_ => {
|
||||
bug!("encode_const: unexpected kind `{:?}`", c.kind());
|
||||
}
|
||||
}
|
||||
|
||||
// Close the "L..E" pair
|
||||
|
|
|
|||
|
|
@ -131,6 +131,17 @@ use crate::intrinsics;
|
|||
|
||||
use crate::hint::spin_loop;
|
||||
|
||||
// Some architectures don't have byte-sized atomics, which results in LLVM
|
||||
// emulating them using a LL/SC loop. However for AtomicBool we can take
|
||||
// advantage of the fact that it only ever contains 0 or 1 and use atomic OR/AND
|
||||
// instead, which LLVM can emulate using a larger atomic OR/AND operation.
|
||||
//
|
||||
// This list should only contain architectures which have word-sized atomic-or/
|
||||
// atomic-and instructions but don't natively support byte-sized atomics.
|
||||
#[cfg(target_has_atomic = "8")]
|
||||
const EMULATE_ATOMIC_BOOL: bool =
|
||||
cfg!(any(target_arch = "riscv32", target_arch = "riscv64", target_arch = "loongarch64"));
|
||||
|
||||
/// A boolean type which can be safely shared between threads.
|
||||
///
|
||||
/// This type has the same in-memory representation as a [`bool`].
|
||||
|
|
@ -553,8 +564,12 @@ impl AtomicBool {
|
|||
#[cfg(target_has_atomic = "8")]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub fn swap(&self, val: bool, order: Ordering) -> bool {
|
||||
// SAFETY: data races are prevented by atomic intrinsics.
|
||||
unsafe { atomic_swap(self.v.get(), val as u8, order) != 0 }
|
||||
if EMULATE_ATOMIC_BOOL {
|
||||
if val { self.fetch_or(true, order) } else { self.fetch_and(false, order) }
|
||||
} else {
|
||||
// SAFETY: data races are prevented by atomic intrinsics.
|
||||
unsafe { atomic_swap(self.v.get(), val as u8, order) != 0 }
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores a value into the [`bool`] if the current value is the same as the `current` value.
|
||||
|
|
@ -664,12 +679,39 @@ impl AtomicBool {
|
|||
success: Ordering,
|
||||
failure: Ordering,
|
||||
) -> Result<bool, bool> {
|
||||
// SAFETY: data races are prevented by atomic intrinsics.
|
||||
match unsafe {
|
||||
atomic_compare_exchange(self.v.get(), current as u8, new as u8, success, failure)
|
||||
} {
|
||||
Ok(x) => Ok(x != 0),
|
||||
Err(x) => Err(x != 0),
|
||||
if EMULATE_ATOMIC_BOOL {
|
||||
// Pick the strongest ordering from success and failure.
|
||||
let order = match (success, failure) {
|
||||
(SeqCst, _) => SeqCst,
|
||||
(_, SeqCst) => SeqCst,
|
||||
(AcqRel, _) => AcqRel,
|
||||
(_, AcqRel) => {
|
||||
panic!("there is no such thing as an acquire-release failure ordering")
|
||||
}
|
||||
(Release, Acquire) => AcqRel,
|
||||
(Acquire, _) => Acquire,
|
||||
(_, Acquire) => Acquire,
|
||||
(Release, Relaxed) => Release,
|
||||
(_, Release) => panic!("there is no such thing as a release failure ordering"),
|
||||
(Relaxed, Relaxed) => Relaxed,
|
||||
};
|
||||
let old = if current == new {
|
||||
// This is a no-op, but we still need to perform the operation
|
||||
// for memory ordering reasons.
|
||||
self.fetch_or(false, order)
|
||||
} else {
|
||||
// This sets the value to the new one and returns the old one.
|
||||
self.swap(new, order)
|
||||
};
|
||||
if old == current { Ok(old) } else { Err(old) }
|
||||
} else {
|
||||
// SAFETY: data races are prevented by atomic intrinsics.
|
||||
match unsafe {
|
||||
atomic_compare_exchange(self.v.get(), current as u8, new as u8, success, failure)
|
||||
} {
|
||||
Ok(x) => Ok(x != 0),
|
||||
Err(x) => Err(x != 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -719,6 +761,10 @@ impl AtomicBool {
|
|||
success: Ordering,
|
||||
failure: Ordering,
|
||||
) -> Result<bool, bool> {
|
||||
if EMULATE_ATOMIC_BOOL {
|
||||
return self.compare_exchange(current, new, success, failure);
|
||||
}
|
||||
|
||||
// SAFETY: data races are prevented by atomic intrinsics.
|
||||
match unsafe {
|
||||
atomic_compare_exchange_weak(self.v.get(), current as u8, new as u8, success, failure)
|
||||
|
|
|
|||
|
|
@ -957,6 +957,7 @@ fn readlink_not_symlink() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating hardlinks
|
||||
fn links_work() {
|
||||
let tmpdir = tmpdir();
|
||||
let input = tmpdir.join("in.txt");
|
||||
|
|
@ -1453,6 +1454,7 @@ fn metadata_access_times() {
|
|||
|
||||
/// Test creating hard links to symlinks.
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating hardlinks
|
||||
fn symlink_hard_link() {
|
||||
let tmpdir = tmpdir();
|
||||
if !got_symlink_permission(&tmpdir) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ macro_rules! or_panic {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn basic() {
|
||||
let dir = tmpdir();
|
||||
let socket_path = dir.path().join("sock");
|
||||
|
|
@ -93,6 +94,7 @@ fn pair() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn try_clone() {
|
||||
let dir = tmpdir();
|
||||
let socket_path = dir.path().join("sock");
|
||||
|
|
@ -119,6 +121,7 @@ fn try_clone() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn iter() {
|
||||
let dir = tmpdir();
|
||||
let socket_path = dir.path().join("sock");
|
||||
|
|
@ -168,6 +171,7 @@ fn long_path() {
|
|||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "nto"))]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn timeouts() {
|
||||
let dir = tmpdir();
|
||||
let socket_path = dir.path().join("sock");
|
||||
|
|
@ -195,6 +199,7 @@ fn timeouts() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_read_timeout() {
|
||||
let dir = tmpdir();
|
||||
let socket_path = dir.path().join("sock");
|
||||
|
|
@ -214,6 +219,7 @@ fn test_read_timeout() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_read_with_timeout() {
|
||||
let dir = tmpdir();
|
||||
let socket_path = dir.path().join("sock");
|
||||
|
|
@ -241,6 +247,7 @@ fn test_read_with_timeout() {
|
|||
// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
|
||||
// when passed zero Durations
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_unix_stream_timeout_zero_duration() {
|
||||
let dir = tmpdir();
|
||||
let socket_path = dir.path().join("sock");
|
||||
|
|
@ -260,6 +267,7 @@ fn test_unix_stream_timeout_zero_duration() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_unix_datagram() {
|
||||
let dir = tmpdir();
|
||||
let path1 = dir.path().join("sock1");
|
||||
|
|
@ -276,6 +284,7 @@ fn test_unix_datagram() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_unnamed_unix_datagram() {
|
||||
let dir = tmpdir();
|
||||
let path1 = dir.path().join("sock1");
|
||||
|
|
@ -293,6 +302,7 @@ fn test_unnamed_unix_datagram() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_unix_datagram_connect_to_recv_addr() {
|
||||
let dir = tmpdir();
|
||||
let path1 = dir.path().join("sock1");
|
||||
|
|
@ -317,6 +327,7 @@ fn test_unix_datagram_connect_to_recv_addr() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_connect_unix_datagram() {
|
||||
let dir = tmpdir();
|
||||
let path1 = dir.path().join("sock1");
|
||||
|
|
@ -343,6 +354,7 @@ fn test_connect_unix_datagram() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_unix_datagram_recv() {
|
||||
let dir = tmpdir();
|
||||
let path1 = dir.path().join("sock1");
|
||||
|
|
@ -385,6 +397,7 @@ fn datagram_pair() {
|
|||
// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
|
||||
// when passed zero Durations
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_unix_datagram_timeout_zero_duration() {
|
||||
let dir = tmpdir();
|
||||
let path = dir.path().join("sock");
|
||||
|
|
@ -529,6 +542,7 @@ fn test_abstract_no_pathname_and_not_unnamed() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_unix_stream_peek() {
|
||||
let (txdone, rxdone) = crate::sync::mpsc::channel();
|
||||
|
||||
|
|
@ -561,6 +575,7 @@ fn test_unix_stream_peek() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_unix_datagram_peek() {
|
||||
let dir = tmpdir();
|
||||
let path1 = dir.path().join("sock");
|
||||
|
|
@ -585,6 +600,7 @@ fn test_unix_datagram_peek() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_unix_datagram_peek_from() {
|
||||
let dir = tmpdir();
|
||||
let path1 = dir.path().join("sock");
|
||||
|
|
@ -648,6 +664,7 @@ fn test_send_vectored_fds_unix_stream() {
|
|||
|
||||
#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_send_vectored_with_ancillary_to_unix_datagram() {
|
||||
fn getpid() -> libc::pid_t {
|
||||
unsafe { libc::getpid() }
|
||||
|
|
@ -715,6 +732,7 @@ fn test_send_vectored_with_ancillary_to_unix_datagram() {
|
|||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets
|
||||
fn test_send_vectored_with_ancillary_unix_datagram() {
|
||||
let dir = tmpdir();
|
||||
let path1 = dir.path().join("sock1");
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ pub fn hashmap_random_keys() -> (u64, u64) {
|
|||
not(target_os = "tvos"),
|
||||
not(target_os = "watchos"),
|
||||
not(target_os = "openbsd"),
|
||||
not(target_os = "freebsd"),
|
||||
not(target_os = "netbsd"),
|
||||
not(target_os = "fuchsia"),
|
||||
not(target_os = "redox"),
|
||||
|
|
@ -68,11 +67,25 @@ mod imp {
|
|||
unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
|
||||
}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
|
||||
// FIXME: using the above when libary std's libc is updated
|
||||
extern "C" {
|
||||
fn getrandom(
|
||||
buffer: *mut libc::c_void,
|
||||
length: libc::size_t,
|
||||
flags: libc::c_uint,
|
||||
) -> libc::ssize_t;
|
||||
}
|
||||
unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
target_os = "linux",
|
||||
target_os = "android",
|
||||
target_os = "espidf",
|
||||
target_os = "horizon"
|
||||
target_os = "horizon",
|
||||
target_os = "freebsd"
|
||||
)))]
|
||||
fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool {
|
||||
false
|
||||
|
|
@ -82,7 +95,8 @@ mod imp {
|
|||
target_os = "linux",
|
||||
target_os = "android",
|
||||
target_os = "espidf",
|
||||
target_os = "horizon"
|
||||
target_os = "horizon",
|
||||
target_os = "freebsd"
|
||||
))]
|
||||
fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
|
||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
||||
|
|
@ -222,7 +236,7 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
|
||||
#[cfg(target_os = "netbsd")]
|
||||
mod imp {
|
||||
use crate::ptr;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Change this file to make users of the `download-ci-llvm` configuration download
|
||||
a new version of LLVM from CI, even if the LLVM submodule hasn’t changed.
|
||||
|
||||
Last change is for: https://github.com/rust-lang/rust/pull/112931
|
||||
Last change is for: https://github.com/rust-lang/rust/pull/113996
|
||||
|
|
|
|||
|
|
@ -559,6 +559,8 @@ fn configure_cmake(
|
|||
|
||||
if target.contains("netbsd") {
|
||||
cfg.define("CMAKE_SYSTEM_NAME", "NetBSD");
|
||||
} else if target.contains("dragonfly") {
|
||||
cfg.define("CMAKE_SYSTEM_NAME", "DragonFly");
|
||||
} else if target.contains("freebsd") {
|
||||
cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD");
|
||||
} else if target.contains("windows") {
|
||||
|
|
@ -569,7 +571,12 @@ fn configure_cmake(
|
|||
cfg.define("CMAKE_SYSTEM_NAME", "SunOS");
|
||||
} else if target.contains("linux") {
|
||||
cfg.define("CMAKE_SYSTEM_NAME", "Linux");
|
||||
} else {
|
||||
builder.info(
|
||||
"could not determine CMAKE_SYSTEM_NAME from the target `{target}`, build may fail",
|
||||
);
|
||||
}
|
||||
|
||||
// When cross-compiling we should also set CMAKE_SYSTEM_VERSION, but in
|
||||
// that case like CMake we cannot easily determine system version either.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -430,6 +430,10 @@ impl Step for Rustfmt {
|
|||
&[],
|
||||
);
|
||||
|
||||
if builder.config.cmd.bless() {
|
||||
cargo.env("BLESS", "1");
|
||||
}
|
||||
|
||||
let dir = testdir(builder, compiler.host);
|
||||
t!(fs::create_dir_all(&dir));
|
||||
cargo.env("RUSTFMT_TEST_DIR", dir);
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ pub(crate) fn try_inline_glob(
|
|||
current_mod: LocalDefId,
|
||||
visited: &mut DefIdSet,
|
||||
inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
|
||||
import: &hir::Item<'_>,
|
||||
) -> Option<Vec<clean::Item>> {
|
||||
let did = res.opt_def_id()?;
|
||||
if did.is_local() {
|
||||
|
|
@ -158,7 +159,15 @@ pub(crate) fn try_inline_glob(
|
|||
.filter(|child| !child.reexport_chain.is_empty())
|
||||
.filter_map(|child| child.res.opt_def_id())
|
||||
.collect();
|
||||
let mut items = build_module_items(cx, did, visited, inlined_names, Some(&reexports));
|
||||
let attrs = cx.tcx.hir().attrs(import.hir_id());
|
||||
let mut items = build_module_items(
|
||||
cx,
|
||||
did,
|
||||
visited,
|
||||
inlined_names,
|
||||
Some(&reexports),
|
||||
Some((attrs, Some(import.owner_id.def_id.to_def_id()))),
|
||||
);
|
||||
items.retain(|item| {
|
||||
if let Some(name) = item.name {
|
||||
// If an item with the same type and name already exists,
|
||||
|
|
@ -549,7 +558,7 @@ pub(crate) fn build_impl(
|
|||
}
|
||||
|
||||
fn build_module(cx: &mut DocContext<'_>, did: DefId, visited: &mut DefIdSet) -> clean::Module {
|
||||
let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None);
|
||||
let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None, None);
|
||||
|
||||
let span = clean::Span::new(cx.tcx.def_span(did));
|
||||
clean::Module { items, span }
|
||||
|
|
@ -561,6 +570,7 @@ fn build_module_items(
|
|||
visited: &mut DefIdSet,
|
||||
inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
|
||||
allowed_def_ids: Option<&DefIdSet>,
|
||||
attrs: Option<(&[ast::Attribute], Option<DefId>)>,
|
||||
) -> Vec<clean::Item> {
|
||||
let mut items = Vec::new();
|
||||
|
||||
|
|
@ -615,7 +625,7 @@ fn build_module_items(
|
|||
cfg: None,
|
||||
inline_stmt_id: None,
|
||||
});
|
||||
} else if let Some(i) = try_inline(cx, res, item.ident.name, None, visited) {
|
||||
} else if let Some(i) = try_inline(cx, res, item.ident.name, attrs, visited) {
|
||||
items.extend(i)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,25 +132,31 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
|
|||
});
|
||||
|
||||
let kind = ModuleItem(Module { items, span });
|
||||
generate_item_with_correct_attrs(cx, kind, doc.def_id, doc.name, doc.import_id, doc.renamed)
|
||||
generate_item_with_correct_attrs(
|
||||
cx,
|
||||
kind,
|
||||
doc.def_id.to_def_id(),
|
||||
doc.name,
|
||||
doc.import_id,
|
||||
doc.renamed,
|
||||
)
|
||||
}
|
||||
|
||||
fn generate_item_with_correct_attrs(
|
||||
cx: &mut DocContext<'_>,
|
||||
kind: ItemKind,
|
||||
local_def_id: LocalDefId,
|
||||
def_id: DefId,
|
||||
name: Symbol,
|
||||
import_id: Option<LocalDefId>,
|
||||
renamed: Option<Symbol>,
|
||||
) -> Item {
|
||||
let def_id = local_def_id.to_def_id();
|
||||
let target_attrs = inline::load_attrs(cx, def_id);
|
||||
let attrs = if let Some(import_id) = import_id {
|
||||
let is_inline = inline::load_attrs(cx, import_id.to_def_id())
|
||||
.lists(sym::doc)
|
||||
.get_word_attr(sym::inline)
|
||||
.is_some();
|
||||
let mut attrs = get_all_import_attributes(cx, import_id, local_def_id, is_inline);
|
||||
let mut attrs = get_all_import_attributes(cx, import_id, def_id, is_inline);
|
||||
add_without_unwanted_attributes(&mut attrs, target_attrs, is_inline, None);
|
||||
attrs
|
||||
} else {
|
||||
|
|
@ -2308,10 +2314,10 @@ fn clean_bare_fn_ty<'tcx>(
|
|||
pub(crate) fn reexport_chain<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
import_def_id: LocalDefId,
|
||||
target_def_id: LocalDefId,
|
||||
target_def_id: DefId,
|
||||
) -> &'tcx [Reexport] {
|
||||
for child in tcx.module_children_local(tcx.local_parent(import_def_id)) {
|
||||
if child.res.opt_def_id() == Some(target_def_id.to_def_id())
|
||||
if child.res.opt_def_id() == Some(target_def_id)
|
||||
&& child.reexport_chain.first().and_then(|r| r.id()) == Some(import_def_id.to_def_id())
|
||||
{
|
||||
return &child.reexport_chain;
|
||||
|
|
@ -2324,7 +2330,7 @@ pub(crate) fn reexport_chain<'tcx>(
|
|||
fn get_all_import_attributes<'hir>(
|
||||
cx: &mut DocContext<'hir>,
|
||||
import_def_id: LocalDefId,
|
||||
target_def_id: LocalDefId,
|
||||
target_def_id: DefId,
|
||||
is_inline: bool,
|
||||
) -> Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)> {
|
||||
let mut attrs = Vec::new();
|
||||
|
|
@ -2541,7 +2547,7 @@ fn clean_maybe_renamed_item<'tcx>(
|
|||
vec![generate_item_with_correct_attrs(
|
||||
cx,
|
||||
kind,
|
||||
item.owner_id.def_id,
|
||||
item.owner_id.def_id.to_def_id(),
|
||||
name,
|
||||
import_id,
|
||||
renamed,
|
||||
|
|
@ -2691,6 +2697,7 @@ fn clean_use_statement_inner<'tcx>(
|
|||
let inline_attr = attrs.lists(sym::doc).get_word_attr(sym::inline);
|
||||
let pub_underscore = visibility.is_public() && name == kw::Underscore;
|
||||
let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id);
|
||||
let import_def_id = import.owner_id.def_id.to_def_id();
|
||||
|
||||
// The parent of the module in which this import resides. This
|
||||
// is the same as `current_mod` if that's already the top
|
||||
|
|
@ -2741,9 +2748,14 @@ fn clean_use_statement_inner<'tcx>(
|
|||
let inner = if kind == hir::UseKind::Glob {
|
||||
if !denied {
|
||||
let mut visited = DefIdSet::default();
|
||||
if let Some(items) =
|
||||
inline::try_inline_glob(cx, path.res, current_mod, &mut visited, inlined_names)
|
||||
{
|
||||
if let Some(items) = inline::try_inline_glob(
|
||||
cx,
|
||||
path.res,
|
||||
current_mod,
|
||||
&mut visited,
|
||||
inlined_names,
|
||||
import,
|
||||
) {
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
|
@ -2759,7 +2771,6 @@ fn clean_use_statement_inner<'tcx>(
|
|||
denied = true;
|
||||
}
|
||||
if !denied {
|
||||
let import_def_id = import.owner_id.to_def_id();
|
||||
if let Some(mut items) = inline::try_inline(
|
||||
cx,
|
||||
path.res,
|
||||
|
|
@ -2779,7 +2790,7 @@ fn clean_use_statement_inner<'tcx>(
|
|||
Import::new_simple(name, resolve_use_source(cx, path), true)
|
||||
};
|
||||
|
||||
vec![Item::from_def_id_and_parts(import.owner_id.to_def_id(), None, ImportItem(inner), cx)]
|
||||
vec![Item::from_def_id_and_parts(import_def_id, None, ImportItem(inner), cx)]
|
||||
}
|
||||
|
||||
fn clean_maybe_renamed_foreign_item<'tcx>(
|
||||
|
|
|
|||
|
|
@ -173,11 +173,8 @@ pub(crate) fn new_handler(
|
|||
}
|
||||
};
|
||||
|
||||
rustc_errors::Handler::with_emitter_and_flags(
|
||||
emitter,
|
||||
unstable_opts.diagnostic_handler_flags(true),
|
||||
None,
|
||||
)
|
||||
rustc_errors::Handler::with_emitter(emitter)
|
||||
.with_flags(unstable_opts.diagnostic_handler_flags(true))
|
||||
}
|
||||
|
||||
/// Parse, resolve, and typecheck the given crate.
|
||||
|
|
|
|||
|
|
@ -587,7 +587,7 @@ pub(crate) fn make_test(
|
|||
);
|
||||
|
||||
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
|
||||
let handler = Handler::with_emitter(false, None, Box::new(emitter), None);
|
||||
let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
|
||||
let sess = ParseSess::with_span_handler(handler, sm);
|
||||
|
||||
let mut found_main = false;
|
||||
|
|
@ -774,7 +774,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
|
|||
TerminalUrl::No,
|
||||
);
|
||||
|
||||
let handler = Handler::with_emitter(false, None, Box::new(emitter), None);
|
||||
let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
|
||||
let sess = ParseSess::with_span_handler(handler, sm);
|
||||
let mut parser =
|
||||
match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ fn check_rust_syntax(
|
|||
let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle };
|
||||
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let handler = Handler::with_emitter(false, None, Box::new(emitter), None);
|
||||
let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
|
||||
let source = dox[code_block.code].to_owned();
|
||||
let sess = ParseSess::with_span_handler(handler, sm);
|
||||
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
|||
// made reachable by cross-crate inlining which we're checking here.
|
||||
// (this is done here because we need to know this upfront).
|
||||
crate::visit_lib::lib_embargo_visit_item(self.cx, ori_res_did);
|
||||
if is_hidden {
|
||||
if is_hidden || glob {
|
||||
return false;
|
||||
}
|
||||
// We store inlined foreign items otherwise, it'd mean that the `use` item would be kept
|
||||
|
|
@ -376,7 +376,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
|||
return true;
|
||||
}
|
||||
let tcx = self.cx.tcx;
|
||||
let item_def_id = reexport_chain(tcx, import_def_id, target_def_id)
|
||||
let item_def_id = reexport_chain(tcx, import_def_id, target_def_id.to_def_id())
|
||||
.iter()
|
||||
.flat_map(|reexport| reexport.id())
|
||||
.map(|id| id.expect_local())
|
||||
|
|
|
|||
|
|
@ -729,7 +729,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
|
|||
false,
|
||||
TerminalUrl::No,
|
||||
);
|
||||
let handler = Handler::with_emitter(false, None, Box::new(emitter), None);
|
||||
let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
|
||||
let sess = ParseSess::with_span_handler(handler, sm);
|
||||
|
||||
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ pub(super) fn check<'tcx>(
|
|||
let haystack = if let ExprKind::MethodCall(path, receiver, [], _) =
|
||||
filter_recv.kind {
|
||||
let p = path.ident.name;
|
||||
if p == sym::iter || p == sym!(iter_mut) {
|
||||
if p == sym::iter || p == sym::iter_mut {
|
||||
receiver
|
||||
} else {
|
||||
filter_recv
|
||||
|
|
|
|||
|
|
@ -472,7 +472,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
|
|||
// This is fine with StackedBorrow and race checks because they don't concern metadata on
|
||||
// the *value* (including the associated provenance if this is an AtomicPtr) at this location.
|
||||
// Only metadata on the location itself is used.
|
||||
let scalar = this.allow_data_races_ref(move |this| this.read_scalar(&place.into()))?;
|
||||
let scalar = this.allow_data_races_ref(move |this| this.read_scalar(place))?;
|
||||
this.validate_overlapping_atomic(place)?;
|
||||
this.buffered_atomic_read(place, atomic, scalar, || {
|
||||
this.validate_atomic_load(place, atomic)
|
||||
|
|
@ -490,7 +490,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
|
|||
this.atomic_access_check(dest)?;
|
||||
|
||||
this.validate_overlapping_atomic(dest)?;
|
||||
this.allow_data_races_mut(move |this| this.write_scalar(val, &dest.into()))?;
|
||||
this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?;
|
||||
this.validate_atomic_store(dest, atomic)?;
|
||||
// FIXME: it's not possible to get the value before write_scalar. A read_scalar will cause
|
||||
// side effects from a read the program did not perform. So we have to initialise
|
||||
|
|
@ -513,12 +513,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
|
|||
this.atomic_access_check(place)?;
|
||||
|
||||
this.validate_overlapping_atomic(place)?;
|
||||
let old = this.allow_data_races_mut(|this| this.read_immediate(&place.into()))?;
|
||||
let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
|
||||
|
||||
// Atomics wrap around on overflow.
|
||||
let val = this.binary_op(op, &old, rhs)?;
|
||||
let val = if neg { this.unary_op(mir::UnOp::Not, &val)? } else { val };
|
||||
this.allow_data_races_mut(|this| this.write_immediate(*val, &place.into()))?;
|
||||
this.allow_data_races_mut(|this| this.write_immediate(*val, place))?;
|
||||
|
||||
this.validate_atomic_rmw(place, atomic)?;
|
||||
|
||||
|
|
@ -538,8 +538,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
|
|||
this.atomic_access_check(place)?;
|
||||
|
||||
this.validate_overlapping_atomic(place)?;
|
||||
let old = this.allow_data_races_mut(|this| this.read_scalar(&place.into()))?;
|
||||
this.allow_data_races_mut(|this| this.write_scalar(new, &place.into()))?;
|
||||
let old = this.allow_data_races_mut(|this| this.read_scalar(place))?;
|
||||
this.allow_data_races_mut(|this| this.write_scalar(new, place))?;
|
||||
|
||||
this.validate_atomic_rmw(place, atomic)?;
|
||||
|
||||
|
|
@ -560,7 +560,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
|
|||
this.atomic_access_check(place)?;
|
||||
|
||||
this.validate_overlapping_atomic(place)?;
|
||||
let old = this.allow_data_races_mut(|this| this.read_immediate(&place.into()))?;
|
||||
let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
|
||||
let lt = this.binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?;
|
||||
|
||||
let new_val = if min {
|
||||
|
|
@ -569,7 +569,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
|
|||
if lt { &rhs } else { &old }
|
||||
};
|
||||
|
||||
this.allow_data_races_mut(|this| this.write_immediate(**new_val, &place.into()))?;
|
||||
this.allow_data_races_mut(|this| this.write_immediate(**new_val, place))?;
|
||||
|
||||
this.validate_atomic_rmw(place, atomic)?;
|
||||
|
||||
|
|
@ -603,7 +603,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
|
|||
// to read with the failure ordering and if successful then try again with the success
|
||||
// read ordering and write in the success case.
|
||||
// Read as immediate for the sake of `binary_op()`
|
||||
let old = this.allow_data_races_mut(|this| this.read_immediate(&(place.into())))?;
|
||||
let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
|
||||
// `binary_op` will bail if either of them is not a scalar.
|
||||
let eq = this.binary_op(mir::BinOp::Eq, &old, expect_old)?;
|
||||
// If the operation would succeed, but is "weak", fail some portion
|
||||
|
|
@ -621,7 +621,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
|
|||
// if successful, perform a full rw-atomic validation
|
||||
// otherwise treat this as an atomic load with the fail ordering.
|
||||
if cmpxchg_success {
|
||||
this.allow_data_races_mut(|this| this.write_scalar(new, &place.into()))?;
|
||||
this.allow_data_races_mut(|this| this.write_scalar(new, place))?;
|
||||
this.validate_atomic_rmw(place, success)?;
|
||||
this.buffered_atomic_rmw(new, place, success, old.to_scalar())?;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -834,7 +834,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
if let Some(thread_info_place) = thread {
|
||||
this.write_scalar(
|
||||
Scalar::from_uint(new_thread_id.to_u32(), thread_info_place.layout.size),
|
||||
&thread_info_place.into(),
|
||||
&thread_info_place,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ impl MainThreadState {
|
|||
this.machine.main_fn_ret_place.unwrap().ptr,
|
||||
this.machine.layouts.isize,
|
||||
);
|
||||
let exit_code = this.read_target_isize(&ret_place.into())?;
|
||||
let exit_code = this.read_target_isize(&ret_place)?;
|
||||
// Need to call this ourselves since we are not going to return to the scheduler
|
||||
// loop, and we want the main thread TLS to not show up as memory leaks.
|
||||
this.terminate_active_thread()?;
|
||||
|
|
@ -321,7 +321,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
|
|||
let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?;
|
||||
for (idx, arg) in argvs.into_iter().enumerate() {
|
||||
let place = ecx.project_field(&argvs_place, idx)?;
|
||||
ecx.write_immediate(arg, &place.into())?;
|
||||
ecx.write_immediate(arg, &place)?;
|
||||
}
|
||||
ecx.mark_immutable(&argvs_place);
|
||||
// A pointer to that place is the 3rd argument for main.
|
||||
|
|
@ -330,7 +330,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
|
|||
{
|
||||
let argc_place =
|
||||
ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?;
|
||||
ecx.write_scalar(argc, &argc_place.into())?;
|
||||
ecx.write_scalar(argc, &argc_place)?;
|
||||
ecx.mark_immutable(&argc_place);
|
||||
ecx.machine.argc = Some(*argc_place);
|
||||
|
||||
|
|
@ -338,7 +338,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
|
|||
ecx.layout_of(Ty::new_imm_ptr(tcx, tcx.types.unit))?,
|
||||
MiriMemoryKind::Machine.into(),
|
||||
)?;
|
||||
ecx.write_immediate(argv, &argv_place.into())?;
|
||||
ecx.write_immediate(argv, &argv_place)?;
|
||||
ecx.mark_immutable(&argv_place);
|
||||
ecx.machine.argv = Some(*argv_place);
|
||||
}
|
||||
|
|
@ -355,7 +355,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
|
|||
// Store the UTF-16 string. We just allocated so we know the bounds are fine.
|
||||
for (idx, &c) in cmd_utf16.iter().enumerate() {
|
||||
let place = ecx.project_field(&cmd_place, idx)?;
|
||||
ecx.write_scalar(Scalar::from_u16(c), &place.into())?;
|
||||
ecx.write_scalar(Scalar::from_u16(c), &place)?;
|
||||
}
|
||||
ecx.mark_immutable(&cmd_place);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let const_val = this.eval_global(cid, None).unwrap_or_else(|err| {
|
||||
panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
|
||||
});
|
||||
this.read_scalar(&const_val.into())
|
||||
this.read_scalar(&const_val)
|
||||
.unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
|
||||
}
|
||||
|
||||
|
|
@ -231,7 +231,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
}
|
||||
|
||||
/// Project to the given *named* field (which must be a struct or union type).
|
||||
fn project_field_named<P: Projectable<'mir, 'tcx, Provenance>>(
|
||||
fn project_field_named<P: Projectable<'tcx, Provenance>>(
|
||||
&self,
|
||||
base: &P,
|
||||
name: &str,
|
||||
|
|
@ -252,13 +252,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
fn write_int(
|
||||
&mut self,
|
||||
i: impl Into<i128>,
|
||||
dest: &PlaceTy<'tcx, Provenance>,
|
||||
dest: &impl Writeable<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
assert!(dest.layout.abi.is_scalar(), "write_int on non-scalar type {}", dest.layout.ty);
|
||||
let val = if dest.layout.abi.is_signed() {
|
||||
Scalar::from_int(i, dest.layout.size)
|
||||
assert!(dest.layout().abi.is_scalar(), "write_int on non-scalar type {}", dest.layout().ty);
|
||||
let val = if dest.layout().abi.is_signed() {
|
||||
Scalar::from_int(i, dest.layout().size)
|
||||
} else {
|
||||
Scalar::from_uint(u64::try_from(i.into()).unwrap(), dest.layout.size)
|
||||
Scalar::from_uint(u64::try_from(i.into()).unwrap(), dest.layout().size)
|
||||
};
|
||||
self.eval_context_mut().write_scalar(val, dest)
|
||||
}
|
||||
|
|
@ -267,12 +267,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
fn write_int_fields(
|
||||
&mut self,
|
||||
values: &[i128],
|
||||
dest: &MPlaceTy<'tcx, Provenance>,
|
||||
dest: &impl Writeable<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
for (idx, &val) in values.iter().enumerate() {
|
||||
let field = this.project_field(dest, idx)?;
|
||||
this.write_int(val, &field.into())?;
|
||||
this.write_int(val, &field)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -281,18 +281,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
fn write_int_fields_named(
|
||||
&mut self,
|
||||
values: &[(&str, i128)],
|
||||
dest: &MPlaceTy<'tcx, Provenance>,
|
||||
dest: &impl Writeable<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
for &(name, val) in values.iter() {
|
||||
let field = this.project_field_named(dest, name)?;
|
||||
this.write_int(val, &field.into())?;
|
||||
this.write_int(val, &field)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write a 0 of the appropriate size to `dest`.
|
||||
fn write_null(&mut self, dest: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
|
||||
fn write_null(&mut self, dest: &impl Writeable<'tcx, Provenance>) -> InterpResult<'tcx> {
|
||||
self.write_int(0, dest)
|
||||
}
|
||||
|
||||
|
|
@ -600,14 +600,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
/// necessary.
|
||||
fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
|
||||
let this = self.eval_context_mut();
|
||||
if let Some(errno_place) = this.active_thread_ref().last_error {
|
||||
Ok(errno_place)
|
||||
if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() {
|
||||
Ok(errno_place.clone())
|
||||
} else {
|
||||
// Allocate new place, set initial value to 0.
|
||||
let errno_layout = this.machine.layouts.u32;
|
||||
let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?;
|
||||
this.write_scalar(Scalar::from_u32(0), &errno_place.into())?;
|
||||
this.active_thread_mut().last_error = Some(errno_place);
|
||||
this.write_scalar(Scalar::from_u32(0), &errno_place)?;
|
||||
this.active_thread_mut().last_error = Some(errno_place.clone());
|
||||
Ok(errno_place)
|
||||
}
|
||||
}
|
||||
|
|
@ -616,14 +616,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
fn set_last_error(&mut self, scalar: Scalar<Provenance>) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let errno_place = this.last_error_place()?;
|
||||
this.write_scalar(scalar, &errno_place.into())
|
||||
this.write_scalar(scalar, &errno_place)
|
||||
}
|
||||
|
||||
/// Gets the last error variable.
|
||||
fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
let this = self.eval_context_mut();
|
||||
let errno_place = this.last_error_place()?;
|
||||
this.read_scalar(&errno_place.into())
|
||||
this.read_scalar(&errno_place)
|
||||
}
|
||||
|
||||
/// This function tries to produce the most similar OS error from the `std::io::ErrorKind`
|
||||
|
|
@ -725,7 +725,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
let mplace = MPlaceTy::from_aligned_ptr(ptr, layout);
|
||||
|
||||
this.check_mplace(mplace)?;
|
||||
this.check_mplace(&mplace)?;
|
||||
|
||||
Ok(mplace)
|
||||
}
|
||||
|
|
@ -772,7 +772,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
let this = self.eval_context_ref();
|
||||
let value_place = this.deref_operand_and_offset(op, offset, base_layout, value_layout)?;
|
||||
this.read_scalar(&value_place.into())
|
||||
this.read_scalar(&value_place)
|
||||
}
|
||||
|
||||
fn write_scalar_at_offset(
|
||||
|
|
@ -785,7 +785,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
) -> InterpResult<'tcx, ()> {
|
||||
let this = self.eval_context_mut();
|
||||
let value_place = this.deref_operand_and_offset(op, offset, base_layout, value_layout)?;
|
||||
this.write_scalar(value, &value_place.into())
|
||||
this.write_scalar(value, &value_place)
|
||||
}
|
||||
|
||||
/// Parse a `timespec` struct and return it as a `std::time::Duration`. It returns `None`
|
||||
|
|
@ -797,10 +797,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
) -> InterpResult<'tcx, Option<Duration>> {
|
||||
let this = self.eval_context_mut();
|
||||
let seconds_place = this.project_field(tp, 0)?;
|
||||
let seconds_scalar = this.read_scalar(&seconds_place.into())?;
|
||||
let seconds_scalar = this.read_scalar(&seconds_place)?;
|
||||
let seconds = seconds_scalar.to_target_isize(this)?;
|
||||
let nanoseconds_place = this.project_field(tp, 1)?;
|
||||
let nanoseconds_scalar = this.read_scalar(&nanoseconds_place.into())?;
|
||||
let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?;
|
||||
let nanoseconds = nanoseconds_scalar.to_target_isize(this)?;
|
||||
|
||||
Ok(try {
|
||||
|
|
|
|||
|
|
@ -651,7 +651,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
|
|||
val: ImmTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?;
|
||||
this.write_immediate(*val, &place.into())?;
|
||||
this.write_immediate(*val, &place)?;
|
||||
Self::add_extern_static(this, name, place.ptr);
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -668,7 +668,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
|
|||
Self::add_extern_static(
|
||||
this,
|
||||
"environ",
|
||||
this.machine.env_vars.environ.unwrap().ptr,
|
||||
this.machine.env_vars.environ.as_ref().unwrap().ptr,
|
||||
);
|
||||
// A couple zero-initialized pointer-sized extern statics.
|
||||
// Most of them are for weak symbols, which we all set to null (indicating that the
|
||||
|
|
@ -685,7 +685,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
|
|||
Self::add_extern_static(
|
||||
this,
|
||||
"environ",
|
||||
this.machine.env_vars.environ.unwrap().ptr,
|
||||
this.machine.env_vars.environ.as_ref().unwrap().ptr,
|
||||
);
|
||||
}
|
||||
"android" => {
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
for (i, ptr) in ptrs.into_iter().enumerate() {
|
||||
let place = this.project_index(&alloc, i as u64)?;
|
||||
|
||||
this.write_pointer(ptr, &place.into())?;
|
||||
this.write_pointer(ptr, &place)?;
|
||||
}
|
||||
|
||||
this.write_immediate(
|
||||
|
|
@ -106,7 +106,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
let op_place = buf_place.offset(offset, ptr_layout, this)?;
|
||||
|
||||
this.write_pointer(ptr, &op_place.into())?;
|
||||
this.write_pointer(ptr, &op_place)?;
|
||||
}
|
||||
}
|
||||
_ => throw_unsup_format!("unknown `miri_get_backtrace` flags {}", flags),
|
||||
|
|
@ -196,33 +196,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
this.write_immediate(
|
||||
name_alloc.to_ref(this),
|
||||
&this.project_field(&dest, 0)?.into(),
|
||||
&this.project_field(&dest, 0)?,
|
||||
)?;
|
||||
this.write_immediate(
|
||||
filename_alloc.to_ref(this),
|
||||
&this.project_field(&dest, 1)?.into(),
|
||||
&this.project_field(&dest, 1)?,
|
||||
)?;
|
||||
}
|
||||
1 => {
|
||||
this.write_scalar(
|
||||
Scalar::from_target_usize(name.len().try_into().unwrap(), this),
|
||||
&this.project_field(&dest, 0)?.into(),
|
||||
&this.project_field(&dest, 0)?,
|
||||
)?;
|
||||
this.write_scalar(
|
||||
Scalar::from_target_usize(filename.len().try_into().unwrap(), this),
|
||||
&this.project_field(&dest, 1)?.into(),
|
||||
&this.project_field(&dest, 1)?,
|
||||
)?;
|
||||
}
|
||||
_ => throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags),
|
||||
}
|
||||
|
||||
this.write_scalar(Scalar::from_u32(lineno), &this.project_field(&dest, 2)?.into())?;
|
||||
this.write_scalar(Scalar::from_u32(colno), &this.project_field(&dest, 3)?.into())?;
|
||||
this.write_scalar(Scalar::from_u32(lineno), &this.project_field(&dest, 2)?)?;
|
||||
this.write_scalar(Scalar::from_u32(colno), &this.project_field(&dest, 3)?)?;
|
||||
|
||||
// Support a 4-field struct for now - this is deprecated
|
||||
// and slated for removal.
|
||||
if num_fields == 5 {
|
||||
this.write_pointer(fn_ptr, &this.project_field(&dest, 4)?.into())?;
|
||||
this.write_pointer(fn_ptr, &this.project_field(&dest, 4)?)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -87,8 +87,8 @@ impl<'tcx> EnvVars<'tcx> {
|
|||
ecx.deallocate_ptr(ptr, None, MiriMemoryKind::Runtime.into())?;
|
||||
}
|
||||
// Deallocate environ var list.
|
||||
let environ = ecx.machine.env_vars.environ.unwrap();
|
||||
let old_vars_ptr = ecx.read_pointer(&environ.into())?;
|
||||
let environ = ecx.machine.env_vars.environ.as_ref().unwrap();
|
||||
let old_vars_ptr = ecx.read_pointer(environ)?;
|
||||
ecx.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -431,8 +431,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
fn update_environ(&mut self) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
// Deallocate the old environ list, if any.
|
||||
if let Some(environ) = this.machine.env_vars.environ {
|
||||
let old_vars_ptr = this.read_pointer(&environ.into())?;
|
||||
if let Some(environ) = this.machine.env_vars.environ.as_ref() {
|
||||
let old_vars_ptr = this.read_pointer(environ)?;
|
||||
this.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?;
|
||||
} else {
|
||||
// No `environ` allocated yet, let's do that.
|
||||
|
|
@ -457,9 +457,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let vars_place = this.allocate(vars_layout, MiriMemoryKind::Runtime.into())?;
|
||||
for (idx, var) in vars.into_iter().enumerate() {
|
||||
let place = this.project_field(&vars_place, idx)?;
|
||||
this.write_pointer(var, &place.into())?;
|
||||
this.write_pointer(var, &place)?;
|
||||
}
|
||||
this.write_pointer(vars_place.ptr, &this.machine.env_vars.environ.unwrap().into())?;
|
||||
this.write_pointer(vars_place.ptr, &this.machine.env_vars.environ.clone().unwrap())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -914,16 +914,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let x = this.read_scalar(x)?.to_f64()?;
|
||||
let exp = this.read_scalar(exp)?.to_i32()?;
|
||||
|
||||
// Saturating cast to i16. Even those are outside the valid exponent range so
|
||||
// `scalbn` below will do its over/underflow handling.
|
||||
let exp = if exp > i32::from(i16::MAX) {
|
||||
i16::MAX
|
||||
} else if exp < i32::from(i16::MIN) {
|
||||
i16::MIN
|
||||
} else {
|
||||
exp.try_into().unwrap()
|
||||
};
|
||||
|
||||
let res = x.scalbn(exp);
|
||||
this.write_scalar(Scalar::from_f64(res), dest)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,12 +97,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
"volatile_load" => {
|
||||
let [place] = check_arg_count(args)?;
|
||||
let place = this.deref_operand(place)?;
|
||||
this.copy_op(&place.into(), dest, /*allow_transmute*/ false)?;
|
||||
this.copy_op(&place, dest, /*allow_transmute*/ false)?;
|
||||
}
|
||||
"volatile_store" => {
|
||||
let [place, dest] = check_arg_count(args)?;
|
||||
let place = this.deref_operand(place)?;
|
||||
this.copy_op(dest, &place.into(), /*allow_transmute*/ false)?;
|
||||
this.copy_op(dest, &place, /*allow_transmute*/ false)?;
|
||||
}
|
||||
|
||||
"write_bytes" | "volatile_set_memory" => {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
};
|
||||
|
||||
for i in 0..dest_len {
|
||||
let op = this.read_immediate(&this.project_index(&op, i)?.into())?;
|
||||
let op = this.read_immediate(&this.project_index(&op, i)?)?;
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
let val = match which {
|
||||
Op::MirOp(mir_op) => this.unary_op(mir_op, &op)?.to_scalar(),
|
||||
|
|
@ -104,7 +104,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
}
|
||||
};
|
||||
this.write_scalar(val, &dest.into())?;
|
||||
this.write_scalar(val, &dest)?;
|
||||
}
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
|
|
@ -172,8 +172,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
};
|
||||
|
||||
for i in 0..dest_len {
|
||||
let left = this.read_immediate(&this.project_index(&left, i)?.into())?;
|
||||
let right = this.read_immediate(&this.project_index(&right, i)?.into())?;
|
||||
let left = this.read_immediate(&this.project_index(&left, i)?)?;
|
||||
let right = this.read_immediate(&this.project_index(&right, i)?)?;
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
let val = match which {
|
||||
Op::MirOp(mir_op) => {
|
||||
|
|
@ -217,7 +217,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
fmin_op(&left, &right)?
|
||||
}
|
||||
};
|
||||
this.write_scalar(val, &dest.into())?;
|
||||
this.write_scalar(val, &dest)?;
|
||||
}
|
||||
}
|
||||
"fma" => {
|
||||
|
|
@ -232,9 +232,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
assert_eq!(dest_len, c_len);
|
||||
|
||||
for i in 0..dest_len {
|
||||
let a = this.read_scalar(&this.project_index(&a, i)?.into())?;
|
||||
let b = this.read_scalar(&this.project_index(&b, i)?.into())?;
|
||||
let c = this.read_scalar(&this.project_index(&c, i)?.into())?;
|
||||
let a = this.read_scalar(&this.project_index(&a, i)?)?;
|
||||
let b = this.read_scalar(&this.project_index(&b, i)?)?;
|
||||
let c = this.read_scalar(&this.project_index(&c, i)?)?;
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
|
||||
// Works for f32 and f64.
|
||||
|
|
@ -258,7 +258,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
Scalar::from_u64(res.to_bits())
|
||||
}
|
||||
};
|
||||
this.write_scalar(val, &dest.into())?;
|
||||
this.write_scalar(val, &dest)?;
|
||||
}
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
|
|
@ -295,13 +295,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
};
|
||||
|
||||
// Initialize with first lane, then proceed with the rest.
|
||||
let mut res = this.read_immediate(&this.project_index(&op, 0)?.into())?;
|
||||
let mut res = this.read_immediate(&this.project_index(&op, 0)?)?;
|
||||
if matches!(which, Op::MirOpBool(_)) {
|
||||
// Convert to `bool` scalar.
|
||||
res = imm_from_bool(simd_element_to_bool(res)?);
|
||||
}
|
||||
for i in 1..op_len {
|
||||
let op = this.read_immediate(&this.project_index(&op, i)?.into())?;
|
||||
let op = this.read_immediate(&this.project_index(&op, i)?)?;
|
||||
res = match which {
|
||||
Op::MirOp(mir_op) => {
|
||||
this.binary_op(mir_op, &res, &op)?
|
||||
|
|
@ -355,7 +355,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
let mut res = init;
|
||||
for i in 0..op_len {
|
||||
let op = this.read_immediate(&this.project_index(&op, i)?.into())?;
|
||||
let op = this.read_immediate(&this.project_index(&op, i)?)?;
|
||||
res = this.binary_op(mir_op, &res, &op)?;
|
||||
}
|
||||
this.write_immediate(*res, dest)?;
|
||||
|
|
@ -372,13 +372,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
assert_eq!(dest_len, no_len);
|
||||
|
||||
for i in 0..dest_len {
|
||||
let mask = this.read_immediate(&this.project_index(&mask, i)?.into())?;
|
||||
let yes = this.read_immediate(&this.project_index(&yes, i)?.into())?;
|
||||
let no = this.read_immediate(&this.project_index(&no, i)?.into())?;
|
||||
let mask = this.read_immediate(&this.project_index(&mask, i)?)?;
|
||||
let yes = this.read_immediate(&this.project_index(&yes, i)?)?;
|
||||
let no = this.read_immediate(&this.project_index(&no, i)?)?;
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
|
||||
let val = if simd_element_to_bool(mask)? { yes } else { no };
|
||||
this.write_immediate(*val, &dest.into())?;
|
||||
this.write_immediate(*val, &dest)?;
|
||||
}
|
||||
}
|
||||
"select_bitmask" => {
|
||||
|
|
@ -403,12 +403,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
& 1u64
|
||||
.checked_shl(simd_bitmask_index(i, dest_len, this.data_layout().endian))
|
||||
.unwrap();
|
||||
let yes = this.read_immediate(&this.project_index(&yes, i.into())?.into())?;
|
||||
let no = this.read_immediate(&this.project_index(&no, i.into())?.into())?;
|
||||
let yes = this.read_immediate(&this.project_index(&yes, i.into())?)?;
|
||||
let no = this.read_immediate(&this.project_index(&no, i.into())?)?;
|
||||
let dest = this.project_index(&dest, i.into())?;
|
||||
|
||||
let val = if mask != 0 { yes } else { no };
|
||||
this.write_immediate(*val, &dest.into())?;
|
||||
this.write_immediate(*val, &dest)?;
|
||||
}
|
||||
for i in dest_len..bitmask_len {
|
||||
// If the mask is "padded", ensure that padding is all-zero.
|
||||
|
|
@ -435,7 +435,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let from_exposed_cast = intrinsic_name == "from_exposed_addr";
|
||||
|
||||
for i in 0..dest_len {
|
||||
let op = this.read_immediate(&this.project_index(&op, i)?.into())?;
|
||||
let op = this.read_immediate(&this.project_index(&op, i)?)?;
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
|
||||
let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) {
|
||||
|
|
@ -472,7 +472,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
to_ty = dest.layout.ty,
|
||||
),
|
||||
};
|
||||
this.write_immediate(val, &dest.into())?;
|
||||
this.write_immediate(val, &dest)?;
|
||||
}
|
||||
}
|
||||
"shuffle" => {
|
||||
|
|
@ -503,17 +503,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let dest = this.project_index(&dest, i)?;
|
||||
|
||||
let val = if src_index < left_len {
|
||||
this.read_immediate(&this.project_index(&left, src_index)?.into())?
|
||||
this.read_immediate(&this.project_index(&left, src_index)?)?
|
||||
} else if src_index < left_len.checked_add(right_len).unwrap() {
|
||||
let right_idx = src_index.checked_sub(left_len).unwrap();
|
||||
this.read_immediate(&this.project_index(&right, right_idx)?.into())?
|
||||
this.read_immediate(&this.project_index(&right, right_idx)?)?
|
||||
} else {
|
||||
span_bug!(
|
||||
this.cur_span(),
|
||||
"simd_shuffle index {src_index} is out of bounds for 2 vectors of size {left_len}",
|
||||
);
|
||||
};
|
||||
this.write_immediate(*val, &dest.into())?;
|
||||
this.write_immediate(*val, &dest)?;
|
||||
}
|
||||
}
|
||||
"gather" => {
|
||||
|
|
@ -528,19 +528,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
assert_eq!(dest_len, mask_len);
|
||||
|
||||
for i in 0..dest_len {
|
||||
let passthru =
|
||||
this.read_immediate(&this.project_index(&passthru, i)?.into())?;
|
||||
let ptr = this.read_immediate(&this.project_index(&ptrs, i)?.into())?;
|
||||
let mask = this.read_immediate(&this.project_index(&mask, i)?.into())?;
|
||||
let passthru = this.read_immediate(&this.project_index(&passthru, i)?)?;
|
||||
let ptr = this.read_immediate(&this.project_index(&ptrs, i)?)?;
|
||||
let mask = this.read_immediate(&this.project_index(&mask, i)?)?;
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
|
||||
let val = if simd_element_to_bool(mask)? {
|
||||
let place = this.deref_operand(&ptr.into())?;
|
||||
this.read_immediate(&place.into())?
|
||||
let place = this.deref_operand(&ptr)?;
|
||||
this.read_immediate(&place)?
|
||||
} else {
|
||||
passthru
|
||||
};
|
||||
this.write_immediate(*val, &dest.into())?;
|
||||
this.write_immediate(*val, &dest)?;
|
||||
}
|
||||
}
|
||||
"scatter" => {
|
||||
|
|
@ -553,13 +552,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
assert_eq!(ptrs_len, mask_len);
|
||||
|
||||
for i in 0..ptrs_len {
|
||||
let value = this.read_immediate(&this.project_index(&value, i)?.into())?;
|
||||
let ptr = this.read_immediate(&this.project_index(&ptrs, i)?.into())?;
|
||||
let mask = this.read_immediate(&this.project_index(&mask, i)?.into())?;
|
||||
let value = this.read_immediate(&this.project_index(&value, i)?)?;
|
||||
let ptr = this.read_immediate(&this.project_index(&ptrs, i)?)?;
|
||||
let mask = this.read_immediate(&this.project_index(&mask, i)?)?;
|
||||
|
||||
if simd_element_to_bool(mask)? {
|
||||
let place = this.deref_operand(&ptr.into())?;
|
||||
this.write_immediate(*value, &place.into())?;
|
||||
let place = this.deref_operand(&ptr)?;
|
||||
this.write_immediate(*value, &place)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -579,7 +578,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
let mut res = 0u64;
|
||||
for i in 0..op_len {
|
||||
let op = this.read_immediate(&this.project_index(&op, i.into())?.into())?;
|
||||
let op = this.read_immediate(&this.project_index(&op, i.into())?)?;
|
||||
if simd_element_to_bool(op)? {
|
||||
res |= 1u64
|
||||
.checked_shl(simd_bitmask_index(i, op_len, this.data_layout().endian))
|
||||
|
|
@ -589,7 +588,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
// We have to force the place type to be an int so that we can write `res` into it.
|
||||
let mut dest = this.force_allocation(dest)?;
|
||||
dest.layout = this.machine.layouts.uint(dest.layout.size).unwrap();
|
||||
this.write_int(res, &dest.into())?;
|
||||
this.write_int(res, &dest)?;
|
||||
}
|
||||
|
||||
name => throw_unsup_format!("unimplemented intrinsic: `simd_{name}`"),
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
})?;
|
||||
this.write_scalar(
|
||||
Scalar::from_i64(qpc),
|
||||
&this.deref_operand(lpPerformanceCount_op)?.into(),
|
||||
&this.deref_operand(lpPerformanceCount_op)?,
|
||||
)?;
|
||||
Ok(Scalar::from_i32(-1)) // return non-zero on success
|
||||
}
|
||||
|
|
@ -179,7 +179,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
// and thus 10^9 counts per second.
|
||||
this.write_scalar(
|
||||
Scalar::from_i64(1_000_000_000),
|
||||
&this.deref_operand_as(lpFrequency_op, this.machine.layouts.u64)?.into(),
|
||||
&this.deref_operand_as(lpFrequency_op, this.machine.layouts.u64)?,
|
||||
)?;
|
||||
Ok(Scalar::from_i32(-1)) // Return non-zero on success
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,14 +201,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
this.write_int(einval, dest)?;
|
||||
} else {
|
||||
if size == 0 {
|
||||
this.write_null(&ret.into())?;
|
||||
this.write_null(&ret)?;
|
||||
} else {
|
||||
let ptr = this.allocate_ptr(
|
||||
Size::from_bytes(size),
|
||||
Align::from_bytes(align).unwrap(),
|
||||
MiriMemoryKind::C.into(),
|
||||
)?;
|
||||
this.write_pointer(ptr, &ret.into())?;
|
||||
this.write_pointer(ptr, &ret)?;
|
||||
}
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
|
|
@ -293,7 +293,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
// Create key and write it into the memory where `key_ptr` wants it.
|
||||
let key = this.machine.tls.create_tls_key(dtor, key_layout.size)?;
|
||||
this.write_scalar(Scalar::from_uint(key, key_layout.size), &key_place.into())?;
|
||||
this.write_scalar(Scalar::from_uint(key, key_layout.size), &key_place)?;
|
||||
|
||||
// Return success (`0`).
|
||||
this.write_null(dest)?;
|
||||
|
|
@ -508,7 +508,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let [_attr, guard_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let guard_size = this.deref_operand(guard_size)?;
|
||||
let guard_size_layout = this.libc_ty_layout("size_t");
|
||||
this.write_scalar(Scalar::from_uint(this.machine.page_size, guard_size_layout.size), &guard_size.into())?;
|
||||
this.write_scalar(Scalar::from_uint(this.machine.page_size, guard_size_layout.size), &guard_size)?;
|
||||
|
||||
// Return success (`0`).
|
||||
this.write_null(dest)?;
|
||||
|
|
@ -538,11 +538,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
this.write_scalar(
|
||||
Scalar::from_uint(this.machine.stack_addr, this.pointer_size()),
|
||||
&addr_place.into(),
|
||||
&addr_place,
|
||||
)?;
|
||||
this.write_scalar(
|
||||
Scalar::from_uint(this.machine.stack_size, this.pointer_size()),
|
||||
&size_place.into(),
|
||||
&size_place,
|
||||
)?;
|
||||
|
||||
// Return success (`0`).
|
||||
|
|
@ -587,20 +587,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
// Reset all fields to `uninit` to make sure nobody reads them.
|
||||
// (This is a std-only shim so we are okay with such hacks.)
|
||||
this.write_uninit(&pwd.into())?;
|
||||
this.write_uninit(&pwd)?;
|
||||
|
||||
// We only set the home_dir field.
|
||||
#[allow(deprecated)]
|
||||
let home_dir = std::env::home_dir().unwrap();
|
||||
let (written, _) = this.write_path_to_c_str(&home_dir, buf, buflen)?;
|
||||
let pw_dir = this.project_field_named(&pwd, "pw_dir")?;
|
||||
this.write_pointer(buf, &pw_dir.into())?;
|
||||
this.write_pointer(buf, &pw_dir)?;
|
||||
|
||||
if written {
|
||||
this.write_pointer(pwd.ptr, &result.into())?;
|
||||
this.write_pointer(pwd.ptr, &result)?;
|
||||
this.write_null(dest)?;
|
||||
} else {
|
||||
this.write_null(&result.into())?;
|
||||
this.write_null(&result)?;
|
||||
this.write_scalar(this.eval_libc("ERANGE"), dest)?;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1457,13 +1457,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
)?;
|
||||
|
||||
let result_place = this.deref_operand(result_op)?;
|
||||
this.write_scalar(this.read_scalar(entry_op)?, &result_place.into())?;
|
||||
this.write_scalar(this.read_scalar(entry_op)?, &result_place)?;
|
||||
|
||||
0
|
||||
}
|
||||
None => {
|
||||
// end of stream: return 0, assign *result=NULL
|
||||
this.write_null(&this.deref_operand(result_op)?.into())?;
|
||||
this.write_null(&this.deref_operand(result_op)?)?;
|
||||
0
|
||||
}
|
||||
Some(Err(e)) =>
|
||||
|
|
|
|||
|
|
@ -74,9 +74,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let event = this.deref_operand_as(event, this.libc_ty_layout("epoll_event"))?;
|
||||
|
||||
let events = this.project_field(&event, 0)?;
|
||||
let events = this.read_scalar(&events.into())?.to_u32()?;
|
||||
let events = this.read_scalar(&events)?.to_u32()?;
|
||||
let data = this.project_field(&event, 1)?;
|
||||
let data = this.read_scalar(&data.into())?;
|
||||
let data = this.read_scalar(&data)?;
|
||||
let event = EpollEvent { events, data };
|
||||
|
||||
if let Some(epfd) = this.machine.file_handler.handles.get_mut(&epfd) {
|
||||
|
|
@ -248,8 +248,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let sv1 = fh.insert_fd(Box::new(SocketPair));
|
||||
let sv1 = ScalarInt::try_from_int(sv1, sv.layout.size).unwrap();
|
||||
|
||||
this.write_scalar(sv0, &sv.into())?;
|
||||
this.write_scalar(sv1, &sv.offset(sv.layout.size, sv.layout, this)?.into())?;
|
||||
this.write_scalar(sv0, &sv)?;
|
||||
this.write_scalar(sv1, &sv.offset(sv.layout.size, sv.layout, this)?)?;
|
||||
|
||||
Ok(Scalar::from_i32(0))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
"_NSGetEnviron" => {
|
||||
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
this.write_pointer(
|
||||
this.machine.env_vars.environ.expect("machine must be initialized").ptr,
|
||||
this.machine.env_vars.environ.as_ref().expect("machine must be initialized").ptr,
|
||||
dest,
|
||||
)?;
|
||||
}
|
||||
|
|
@ -133,7 +133,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let (written, size_needed) = this.write_path_to_c_str(
|
||||
&path,
|
||||
buf_ptr,
|
||||
this.read_scalar(&bufsize.into())?.to_u32()?.into(),
|
||||
this.read_scalar(&bufsize)?.to_u32()?.into(),
|
||||
)?;
|
||||
|
||||
if written {
|
||||
|
|
@ -141,7 +141,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
} else {
|
||||
this.write_scalar(
|
||||
Scalar::from_u32(size_needed.try_into().unwrap()),
|
||||
&bufsize.into(),
|
||||
&bufsize,
|
||||
)?;
|
||||
this.write_int(-1, dest)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -346,7 +346,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
// This can always be revisited to have some external state to catch double-destroys
|
||||
// but not complain about the above code. See https://github.com/rust-lang/miri/pull/1933
|
||||
this.write_uninit(
|
||||
&this.deref_operand_as(attr_op, this.libc_ty_layout("pthread_mutexattr_t"))?.into(),
|
||||
&this.deref_operand_as(attr_op, this.libc_ty_layout("pthread_mutexattr_t"))?,
|
||||
)?;
|
||||
|
||||
Ok(0)
|
||||
|
|
@ -500,7 +500,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
// This might lead to false positives, see comment in pthread_mutexattr_destroy
|
||||
this.write_uninit(
|
||||
&this.deref_operand_as(mutex_op, this.libc_ty_layout("pthread_mutex_t"))?.into(),
|
||||
&this.deref_operand_as(mutex_op, this.libc_ty_layout("pthread_mutex_t"))?,
|
||||
)?;
|
||||
// FIXME: delete interpreter state associated with this mutex.
|
||||
|
||||
|
|
@ -625,7 +625,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
// This might lead to false positives, see comment in pthread_mutexattr_destroy
|
||||
this.write_uninit(
|
||||
&this.deref_operand_as(rwlock_op, this.libc_ty_layout("pthread_rwlock_t"))?.into(),
|
||||
&this.deref_operand_as(rwlock_op, this.libc_ty_layout("pthread_rwlock_t"))?,
|
||||
)?;
|
||||
// FIXME: delete interpreter state associated with this rwlock.
|
||||
|
||||
|
|
@ -675,7 +675,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let this = self.eval_context_mut();
|
||||
|
||||
let clock_id = condattr_get_clock_id(this, attr_op)?;
|
||||
this.write_scalar(Scalar::from_i32(clock_id), &this.deref_operand(clk_id_op)?.into())?;
|
||||
this.write_scalar(Scalar::from_i32(clock_id), &this.deref_operand(clk_id_op)?)?;
|
||||
|
||||
Ok(Scalar::from_i32(0))
|
||||
}
|
||||
|
|
@ -691,7 +691,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
// This might lead to false positives, see comment in pthread_mutexattr_destroy
|
||||
this.write_uninit(
|
||||
&this.deref_operand_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?.into(),
|
||||
&this.deref_operand_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?,
|
||||
)?;
|
||||
|
||||
Ok(0)
|
||||
|
|
@ -868,7 +868,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
// This might lead to false positives, see comment in pthread_mutexattr_destroy
|
||||
this.write_uninit(
|
||||
&this.deref_operand_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?.into(),
|
||||
&this.deref_operand_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?,
|
||||
)?;
|
||||
// FIXME: delete interpreter state associated with this condvar.
|
||||
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
this.project_field_named(&io_status_block, "Information")?;
|
||||
this.write_scalar(
|
||||
Scalar::from_target_usize(n.into(), this),
|
||||
&io_status_information.into(),
|
||||
&io_status_information,
|
||||
)?;
|
||||
}
|
||||
// Return whether this was a success. >= 0 is success.
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let layout = this.machine.layouts.uint(size).unwrap();
|
||||
let futex_val = this
|
||||
.read_scalar_atomic(&MPlaceTy::from_aligned_ptr(ptr, layout), AtomicReadOrd::Relaxed)?;
|
||||
let compare_val = this.read_scalar(&MPlaceTy::from_aligned_ptr(compare, layout).into())?;
|
||||
let compare_val = this.read_scalar(&MPlaceTy::from_aligned_ptr(compare, layout))?;
|
||||
|
||||
if futex_val == compare_val {
|
||||
// If the values are the same, we have to block.
|
||||
|
|
|
|||
|
|
@ -152,18 +152,13 @@ fn default_handler(
|
|||
TerminalUrl::No,
|
||||
))
|
||||
};
|
||||
Handler::with_emitter(
|
||||
true,
|
||||
None,
|
||||
Box::new(SilentOnIgnoredFilesEmitter {
|
||||
has_non_ignorable_parser_errors: false,
|
||||
source_map,
|
||||
emitter,
|
||||
ignore_path_set,
|
||||
can_reset,
|
||||
}),
|
||||
None,
|
||||
)
|
||||
Handler::with_emitter(Box::new(SilentOnIgnoredFilesEmitter {
|
||||
has_non_ignorable_parser_errors: false,
|
||||
source_map,
|
||||
emitter,
|
||||
ignore_path_set,
|
||||
can_reset,
|
||||
}))
|
||||
}
|
||||
|
||||
impl ParseSess {
|
||||
|
|
@ -234,7 +229,7 @@ impl ParseSess {
|
|||
}
|
||||
|
||||
pub(crate) fn set_silent_emitter(&mut self) {
|
||||
self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter(), None);
|
||||
self.parse_sess.span_diagnostic = Handler::with_emitter(silent_emitter());
|
||||
}
|
||||
|
||||
pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
|
||||
|
|
|
|||
|
|
@ -838,6 +838,12 @@ fn handle_result(
|
|||
|
||||
// Ignore LF and CRLF difference for Windows.
|
||||
if !string_eq_ignore_newline_repr(&fmt_text, &text) {
|
||||
if let Some(bless) = std::env::var_os("BLESS") {
|
||||
if bless != "0" {
|
||||
std::fs::write(file_name, fmt_text).unwrap();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let diff = make_diff(&text, &fmt_text, DIFF_CONTEXT_SIZE);
|
||||
assert!(
|
||||
!diff.is_empty(),
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ const EXCEPTIONS: &[(&str, &str)] = &[
|
|||
("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
|
||||
("mdbook", "MPL-2.0"), // mdbook
|
||||
("openssl", "Apache-2.0"), // opt-dist
|
||||
("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), // rustc (license is the same as LLVM uses)
|
||||
("ryu", "Apache-2.0 OR BSL-1.0"), // cargo/... (because of serde)
|
||||
("self_cell", "Apache-2.0"), // rustc (fluent translations)
|
||||
("snap", "BSD-3-Clause"), // rustc
|
||||
|
|
@ -224,6 +225,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
|
|||
"rustc-hash",
|
||||
"rustc-rayon",
|
||||
"rustc-rayon-core",
|
||||
"rustc_apfloat",
|
||||
"rustc_version",
|
||||
"rustix",
|
||||
"ruzstd", // via object in thorin-dwp
|
||||
|
|
|
|||
|
|
@ -302,10 +302,6 @@ pub fn check(path: &Path, bad: &mut bool) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
// apfloat shouldn't be changed because of license problems
|
||||
if is_in(file, "compiler", "rustc_apfloat") {
|
||||
return;
|
||||
}
|
||||
let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr");
|
||||
let mut skip_undocumented_unsafe =
|
||||
contains_ignore_directive(can_contain, &contents, "undocumented-unsafe");
|
||||
|
|
|
|||
|
|
@ -53,6 +53,18 @@ impl<'a, T, U> Trait4<'a, U> for T {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Trait5<T, const N: usize> {
|
||||
fn quux(&self, _: &[T; N]);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Type5;
|
||||
|
||||
impl<T, U, const N: usize> Trait5<U, N> for T {
|
||||
fn quux(&self, _: &[U; N]) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn foo1(a: &dyn Trait1) {
|
||||
a.foo();
|
||||
// CHECK-LABEL: define{{.*}}4foo1{{.*}}!type !{{[0-9]+}}
|
||||
|
|
@ -114,7 +126,24 @@ pub fn bar4<'a>() {
|
|||
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE4:[[:print:]]+]]")
|
||||
}
|
||||
|
||||
pub fn foo5(a: &dyn Trait5<Type5, 32>) {
|
||||
let b = &[Type5; 32];
|
||||
a.quux(&b);
|
||||
// CHECK-LABEL: define{{.*}}4foo5{{.*}}!type !{{[0-9]+}}
|
||||
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE5:[[:print:]]+]]")
|
||||
}
|
||||
|
||||
pub fn bar5() {
|
||||
let a = &[Type5; 32];
|
||||
foo5(&a);
|
||||
let b = &a as &dyn Trait5<Type5, 32>;
|
||||
b.quux(&a);
|
||||
// CHECK-LABEL: define{{.*}}4bar5{{.*}}!type !{{[0-9]+}}
|
||||
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE5:[[:print:]]+]]")
|
||||
}
|
||||
|
||||
// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE1]]"}
|
||||
// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE2]]"}
|
||||
// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE3]]"}
|
||||
// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE4]]"}
|
||||
// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE5]]"}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,19 @@ impl<'a, T, U> Trait4<'a, U> for T {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Trait5<T, const N: usize> {
|
||||
fn quux(&self, _: &[T; N]);
|
||||
}
|
||||
|
||||
pub struct Type5;
|
||||
|
||||
impl Copy for Type5 {}
|
||||
|
||||
impl<T, U, const N: usize> Trait5<U, N> for T {
|
||||
fn quux(&self, _: &[U; N]) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn foo1(a: &dyn Trait1) {
|
||||
a.foo();
|
||||
// CHECK-LABEL: define{{.*}}4foo1{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
|
||||
|
|
@ -138,7 +151,24 @@ pub fn bar4<'a>() {
|
|||
// CHECK: call align 4 {{ptr|i32\*}} %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z]\.0|%_[0-9]}}, {{\{\}\*|ptr|%Type4\*}} align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE4:[[:print:]]+]]) ]
|
||||
}
|
||||
|
||||
pub fn foo5(a: &dyn Trait5<Type5, 32>) {
|
||||
let b = &[Type5; 32];
|
||||
a.quux(&b);
|
||||
// CHECK-LABEL: define{{.*}}4foo5{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
|
||||
// CHECK: call void %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z](\.0)*|%_[0-9]+]}}, {{\{\}\*|ptr|\[32 x %Type5\]\*}} align 1 {{%[a-z](\.0)*|%_[0-9]+}}){{.*}}[ "kcfi"(i32 [[TYPE5:[[:print:]]+]]) ]
|
||||
}
|
||||
|
||||
pub fn bar5() {
|
||||
let a = &[Type5; 32];
|
||||
foo5(&a);
|
||||
let b = &a as &dyn Trait5<Type5, 32>;
|
||||
b.quux(&a);
|
||||
// CHECK-LABEL: define{{.*}}4bar5{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
|
||||
// CHECK: call void %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z](\.0)*|%_[0-9]+]}}, {{\{\}\*|ptr|\[32 x %Type5\]\*}} align 1 {{%[a-z](\.0)*|%_[0-9]+}}){{.*}}[ "kcfi"(i32 [[TYPE5:[[:print:]]+]]) ]
|
||||
}
|
||||
|
||||
// CHECK: !{{[0-9]+}} = !{i32 [[TYPE1]]}
|
||||
// CHECK: !{{[0-9]+}} = !{i32 [[TYPE2]]}
|
||||
// CHECK: !{{[0-9]+}} = !{i32 [[TYPE3]]}
|
||||
// CHECK: !{{[0-9]+}} = !{i32 [[TYPE4]]}
|
||||
// CHECK: !{{[0-9]+}} = !{i32 [[TYPE5]]}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
#![crate_name = "colors"]
|
||||
|
||||
pub struct Color;
|
||||
19
tests/rustdoc/issue-113982-doc_auto_cfg-reexport-foreign.rs
Normal file
19
tests/rustdoc/issue-113982-doc_auto_cfg-reexport-foreign.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// aux-build: issue-113982-doc_auto_cfg-reexport-foreign.rs
|
||||
|
||||
#![feature(no_core, doc_auto_cfg)]
|
||||
#![no_core]
|
||||
#![crate_name = "foo"]
|
||||
|
||||
extern crate colors;
|
||||
|
||||
// @has 'foo/index.html' '//*[@class="stab portability"]' 'Non-colors'
|
||||
// @has 'foo/struct.Color.html' '//*[@class="stab portability"]' \
|
||||
// 'Available on non-crate feature colors only.'
|
||||
#[cfg(not(feature = "colors"))]
|
||||
pub use colors::*;
|
||||
|
||||
// @has 'foo/index.html' '//*[@class="stab portability"]' 'Non-fruits'
|
||||
// @has 'foo/struct.Red.html' '//*[@class="stab portability"]' \
|
||||
// 'Available on non-crate feature fruits only.'
|
||||
#[cfg(not(feature = "fruits"))]
|
||||
pub use colors::Color as Red;
|
||||
9
tests/ui/const_prop/apfloat-f64-roundtrip.rs
Normal file
9
tests/ui/const_prop/apfloat-f64-roundtrip.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// run-pass
|
||||
// compile-flags: -O -Zmir-opt-level=3 -Cno-prepopulate-passes
|
||||
// min-llvm-version: 16.0 (requires APFloat fixes in LLVM)
|
||||
|
||||
// Regression test for a broken MIR optimization (issue #113407).
|
||||
pub fn main() {
|
||||
let f = f64::from_bits(0x19873cc2) as f32;
|
||||
assert_eq!(f.to_bits(), 0);
|
||||
}
|
||||
15
tests/ui/const_prop/apfloat-remainder-regression.rs
Normal file
15
tests/ui/const_prop/apfloat-remainder-regression.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// run-pass
|
||||
// compile-flags: -O -Zmir-opt-level=3 -Cno-prepopulate-passes
|
||||
|
||||
// Regression test for a broken MIR optimization (issue #102403).
|
||||
pub fn f() -> f64 {
|
||||
std::hint::black_box(-1.0) % std::hint::black_box(-1.0)
|
||||
}
|
||||
|
||||
pub fn g() -> f64 {
|
||||
-1.0 % -1.0
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
assert_eq!(f().signum(), g().signum());
|
||||
}
|
||||
28
tests/ui/lint/lint-ctypes-113436-1.rs
Normal file
28
tests/ui/lint/lint-ctypes-113436-1.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#![deny(improper_ctypes_definitions)]
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Foo {
|
||||
a: u8,
|
||||
b: (),
|
||||
}
|
||||
|
||||
extern "C" fn foo(x: Foo) -> Foo {
|
||||
todo!()
|
||||
}
|
||||
|
||||
struct NotSafe(u32);
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Bar {
|
||||
a: u8,
|
||||
b: (),
|
||||
c: NotSafe,
|
||||
}
|
||||
|
||||
extern "C" fn bar(x: Bar) -> Bar {
|
||||
//~^ ERROR `extern` fn uses type `NotSafe`, which is not FFI-safe
|
||||
//~^^ ERROR `extern` fn uses type `NotSafe`, which is not FFI-safe
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
35
tests/ui/lint/lint-ctypes-113436-1.stderr
Normal file
35
tests/ui/lint/lint-ctypes-113436-1.stderr
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
error: `extern` fn uses type `NotSafe`, which is not FFI-safe
|
||||
--> $DIR/lint-ctypes-113436-1.rs:22:22
|
||||
|
|
||||
LL | extern "C" fn bar(x: Bar) -> Bar {
|
||||
| ^^^ not FFI-safe
|
||||
|
|
||||
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
|
||||
= note: this struct has unspecified layout
|
||||
note: the type is defined here
|
||||
--> $DIR/lint-ctypes-113436-1.rs:13:1
|
||||
|
|
||||
LL | struct NotSafe(u32);
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: the lint level is defined here
|
||||
--> $DIR/lint-ctypes-113436-1.rs:1:9
|
||||
|
|
||||
LL | #![deny(improper_ctypes_definitions)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `extern` fn uses type `NotSafe`, which is not FFI-safe
|
||||
--> $DIR/lint-ctypes-113436-1.rs:22:30
|
||||
|
|
||||
LL | extern "C" fn bar(x: Bar) -> Bar {
|
||||
| ^^^ not FFI-safe
|
||||
|
|
||||
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
|
||||
= note: this struct has unspecified layout
|
||||
note: the type is defined here
|
||||
--> $DIR/lint-ctypes-113436-1.rs:13:1
|
||||
|
|
||||
LL | struct NotSafe(u32);
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
34
tests/ui/lint/lint-ctypes-113436.rs
Normal file
34
tests/ui/lint/lint-ctypes-113436.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// check-pass
|
||||
#![deny(improper_ctypes_definitions)]
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Wrap<T>(T);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct TransparentWrap<T>(T);
|
||||
|
||||
pub extern "C" fn f() -> Wrap<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
const _: extern "C" fn() -> Wrap<()> = f;
|
||||
|
||||
pub extern "C" fn ff() -> Wrap<Wrap<()>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
const _: extern "C" fn() -> Wrap<Wrap<()>> = ff;
|
||||
|
||||
pub extern "C" fn g() -> TransparentWrap<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
const _: extern "C" fn() -> TransparentWrap<()> = g;
|
||||
|
||||
pub extern "C" fn gg() -> TransparentWrap<TransparentWrap<()>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
const _: extern "C" fn() -> TransparentWrap<TransparentWrap<()>> = gg;
|
||||
|
||||
fn main() {}
|
||||
15
tests/ui/numbers-arithmetic/apfloat-modulo-wrong.rs
Normal file
15
tests/ui/numbers-arithmetic/apfloat-modulo-wrong.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// run-pass
|
||||
// check-run-results
|
||||
// regression test for issue #109567
|
||||
|
||||
fn f() -> f64 {
|
||||
std::hint::black_box(-1.0) % std::hint::black_box(-1.0)
|
||||
}
|
||||
|
||||
const G: f64 = -1.0 % -1.0;
|
||||
|
||||
pub fn main() {
|
||||
assert_eq!(-1, G.signum() as i32);
|
||||
assert_eq!((-0.0_f64).to_bits(), G.to_bits());
|
||||
assert_eq!(f().signum(), G.signum());
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro
|
|||
use core /* 0#1 */::prelude /* 0#1 */::rust_2018 /* 0#1 */::*;
|
||||
#[macro_use /* 0#1 */]
|
||||
extern crate core /* 0#1 */;
|
||||
extern crate compiler_builtins /* 442 */ as _ /* 0#1 */;
|
||||
extern crate compiler_builtins /* 443 */ as _ /* 0#1 */;
|
||||
// Don't load unnecessary hygiene information from std
|
||||
extern crate std /* 0#0 */;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
|
|||
use ::core /* 0#1 */::prelude /* 0#1 */::rust_2015 /* 0#1 */::*;
|
||||
#[macro_use /* 0#1 */]
|
||||
extern crate core /* 0#2 */;
|
||||
extern crate compiler_builtins /* 442 */ as _ /* 0#2 */;
|
||||
extern crate compiler_builtins /* 443 */ as _ /* 0#2 */;
|
||||
// Don't load unnecessary hygiene information from std
|
||||
extern crate std /* 0#0 */;
|
||||
|
||||
|
|
|
|||
|
|
@ -318,14 +318,6 @@ changelog-branch = "master"
|
|||
|
||||
[shortcut]
|
||||
|
||||
|
||||
[mentions."compiler/rustc_apfloat"]
|
||||
message = """
|
||||
Changes rustc_apfloat. rustc_apfloat is currently in limbo and you almost \
|
||||
certainly don't want to change it (see #55993).
|
||||
"""
|
||||
cc = ["@eddyb"]
|
||||
|
||||
[mentions."compiler/rustc_codegen_cranelift"]
|
||||
cc = ["@bjorn3"]
|
||||
|
||||
|
|
@ -609,7 +601,6 @@ style-team = [
|
|||
"/Cargo.lock" = ["@Mark-Simulacrum"]
|
||||
"/Cargo.toml" = ["@Mark-Simulacrum"]
|
||||
"/compiler" = ["compiler"]
|
||||
"/compiler/rustc_apfloat" = ["@eddyb"]
|
||||
"/compiler/rustc_ast" = ["compiler", "parser"]
|
||||
"/compiler/rustc_ast_lowering" = ["compiler", "ast_lowering"]
|
||||
"/compiler/rustc_hir_analysis" = ["compiler", "types"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue